用过pycharm的老铁们对这个界面比较熟悉吧,有个好看的下拉框。

如果用PYQT自带的组件combobox, 默认值有多宽,这个下拉框就会有多宽,不方便自定义,不好带图标,且不美观,如图:

于是我想着换种方式做,这是我做的初级效果。当然,还可以更美观一点的,我是项目有什么需求做什么东东,以后新项目有新需求了,我再来更新。下图是我做的效果:

组件实现
from PyQt5.QtCore import QPoint, pyqtSignal
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QPushButton, QMenu, QAction
from Assets.styles.dark import DarkStyle
from Assets.styles.light import LightStyle
from lib.share import ShareInfo
from utils_.main import newIconPixmap
class PseudoComboBox(QPushButton):
set_current_signal = pyqtSignal(str, int)
def __init__(self, current_value=None, options=None, parent=None):
super(PseudoComboBox, self).__init__(parent)
self.style = LightStyle if ShareInfo.setting["theme"] == "light" else DarkStyle
self.options = options if options is not None else []
# 初始化默认值
self.current_text = ""
self.current_index = -1
# 处理options不为空的情况
if len(self.options) > 0:
if current_value is None:
# 情况1:current_value为None,显示第一个选项
self.set_current_index(0)
else:
# 情况2:current_value不为None,根据current_value查找匹配项
self.find_and_set_current(current_value)
self.init_ui()
def init_ui(self):
self.setText(self.current_text or "")
self.setProperty("class", "transparent medium")
self.setStyleSheet("padding-right: 20px;")
self.clicked.connect(self.show_options)
def show_options(self):
if not self.options:
return
menu = QMenu(self)
for index, option in enumerate(self.options):
# 问题所在:需要从option中提取显示文本,而不是直接传入option对象
display_text = self._get_display_text(option)
action = QAction(display_text, menu) # 传入显示文本,而不是option对象
# 使用闭包传递参数
action.triggered.connect(
lambda checked, idx=index, opt=option: self.action_selected(idx, opt)
)
menu.addAction(action)
# 计算菜单显示位置
x = self.mapToGlobal(self.pos()).x() - self.x()
y = self.mapToGlobal(self.pos()).y() + self.height()
menu.exec_(QPoint(x, y))
def action_selected(self, index, option):
self.current_index = index
self.current_text = self._get_display_text(option)
self.setText(self.current_text)
# 发送信号,传递选项的显示文本和索引
self.set_current_signal.emit(self.current_text, index)
def _get_display_text(self, item):
"""从选项项中获取显示文本"""
if isinstance(item, str):
return item
elif isinstance(item, dict):
# 处理字典类型的选项
return item.get('text', str(item))
elif hasattr(item, 'text'):
# 处理有text属性的对象
return item.text
elif hasattr(item, '__str__'):
# 其他对象,调用其字符串表示
return str(item)
else:
return str(item)
def _get_item_value(self, item):
"""从选项项中获取实际值(如果需要)"""
if isinstance(item, str):
return item
elif isinstance(item, dict):
# 处理字典类型的选项
return item.get('value', item.get('text', str(item)))
elif hasattr(item, 'value'):
return item.value
elif hasattr(item, 'text'):
return item.text
else:
return str(item)
def set_current_index(self, index):
"""设置当前索引"""
if 0 <= index < len(self.options):
self.current_index = index
option = self.options[index]
self.current_text = self._get_display_text(option)
self.setText(self.current_text)
def find_and_set_current(self, current_value):
"""根据值查找并设置当前项"""
found = False
for i, option in enumerate(self.options):
option_value = self._get_item_value(option)
# 多种匹配方式
if isinstance(current_value, str):
# 字符串匹配
if str(option_value) == current_value:
self.set_current_index(i)
found = True
break
elif hasattr(current_value, 'value'):
# 对象有value属性
if option_value == current_value.value:
self.set_current_index(i)
found = True
break
elif hasattr(current_value, 'text'):
# 对象有text属性
if self._get_display_text(option) == current_value.text:
self.set_current_index(i)
found = True
break
else:
# 其他类型,转换为字符串比较
if str(option_value) == str(current_value):
self.set_current_index(i)
found = True
break
# 如果没有找到匹配项,默认显示第一个选项
if not found and len(self.options) > 0:
self.set_current_index(0)
def paintEvent(self, event):
super(PseudoComboBox, self).paintEvent(event)
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
suffix_pixmap = newIconPixmap(self.style.arrow_down, 10, 10)
painter.drawPixmap(self.width() - 15, 10, 10, 10, suffix_pixmap)
def clear(self):
self.options = []
self.current_text = ""
self.current_index = -1
self.setText("")
def addItem(self, item):
self.options.append(item)
def setCurrentText(self, text):
"""根据显示文本设置当前项"""
for i, option in enumerate(self.options):
display_text = self._get_display_text(option)
if display_text == text:
self.set_current_index(i)
self.set_current_signal.emit(self.current_text, self.current_index)
return
def setCurrentIndex(self, index):
"""根据索引设置当前项"""
if 0 <= index < len(self.options):
self.set_current_index(index)
self.set_current_signal.emit(self.current_text, self.current_index)
def currentIndex(self):
return self.current_index
def currentText(self):
return self.current_text
def getCurrentValue(self):
"""获取当前选项的实际值(不是显示文本)"""
if 0 <= self.current_index < len(self.options):
return self._get_item_value(self.options[self.current_index])
return None
def count(self):
return len(self.options)



1万+

被折叠的 条评论
为什么被折叠?



