如何在 QComboBox 中添加和排序项目
How to add and sort items in QComboBox
我想(在这个类比示例中)通过 QPushButton 单击或 select 从表示 selection 历史的 QComboBox 更改 QLabel 的颜色。在实际代码中,控件多于一个按钮,因此有一种方法 setColor(self, color)
将新颜色添加到 QComboBox 作为其第一项,删除可能的重复项并更改 QLabel 的颜色。这行得通。
第二个选项是从历史QComboBox中选择颜色。我在项目为 pressed
时捕获事件并调用相同的 setColor(self, color)
。这会导致问题。
问题是当 QComboBox 中有颜色时,例如['red', 'blue', 'green']
我选择选项 'blue'
并且项目的顺序更改为 ['blue', 'red', 'green']
(我仍然想要这个),有一些标准的 QComboBox 行为完成了 selection 和 QComboBox selects 'green'
项。所以,QLabel 有正确的颜色,但 QComboBox 没有正确的文本。
如何覆盖“标准行为”并解决这个问题?请试试这个例子:
import sys
from random import random
from PyQt5.QtWidgets import QApplication, QWidget, QComboBox, QVBoxLayout, QPushButton, QLabel
class MyCombo(QComboBox):
def __init__(self, parent):
super().__init__(parent)
self.view().pressed.connect(self.handleItemPressed)
def handleItemPressed(self, index):
color = self.model().itemData(index)[0]
self.parent().setColor(color)
def removeDuplicates(self):
unique = list()
while len(unique) < self.model().rowCount():
if self.itemText(len(unique)) in unique:
self.removeItem(len(unique))
else:
unique.append(self.itemText(len(unique)))
class Gui(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(300, 200)
lay = QVBoxLayout()
self.cmb = MyCombo(self)
self.btn = QPushButton('Random', self)
self.btn.clicked.connect(self.btnClicked)
self.pnl = QLabel(self)
lay.addWidget(self.cmb)
lay.addWidget(self.btn)
lay.addWidget(self.pnl)
self.setLayout(lay)
def setColor(self, color):
self.cmb.insertItem(0, color)
self.cmb.setCurrentIndex(0)
self.cmb.removeDuplicates()
self.pnl.setStyleSheet('background-color: %s' % color)
def btnClicked(self):
colors = ['red', 'green', 'blue', 'yellow', 'orange']
self.setColor(colors[int(random() * len(colors))])
if __name__ == '__main__':
app = QApplication(sys.argv)
g = Gui()
g.show()
sys.exit(app.exec_())
在包括设置当前索引在内的操作过程中重新排列项目从来都不是一件好事,除非您确定跟踪正确的索引并且知道操作顺序发生在两者之间。
在你的情况下,问题是视图的pressed
信号在之前组合框的当前索引被改变,结果是当鼠标按钮 释放时 当前索引也是错误的,这也是由于项目的重新排序。
您的实现还有另一个问题:键盘和鼠标滚轮导航不会正确更新颜色。
我的建议是保持模型不变(除非添加了新颜色),并在显示弹出窗口之前重新排序仅。
为了避免递归,请永远记住,您可以使用 blockSignals()
.
来阻止对象的信号
class MyCombo(QComboBox):
colorChanged = pyqtSignal(str)
def __init__(self, parent):
super().__init__(parent)
self.currentIndexChanged.connect(self.emitChangedColor)
def emitChangedColor(self, index):
if index >= 0:
self.colorChanged.emit(self.itemText(index))
def setColor(self, color):
blocked = self.blockSignals(True)
for index in range(self.count()):
if self.itemText(index) == color:
break
else:
index = 0
self.insertItem(index, color)
self.setCurrentIndex(index)
self.blockSignals(blocked)
def showPopup(self):
if self.currentIndex() > 0:
blocked = self.blockSignals(True)
current = self.currentText()
self.removeItem(self.currentIndex())
self.insertItem(0, current)
self.setCurrentIndex(0)
self.blockSignals(blocked)
super().showPopup()
class Gui(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(300, 200)
lay = QVBoxLayout()
self.cmb = MyCombo(self)
self.cmb.colorChanged.connect(self.setColor)
self.btn = QPushButton('Random', self)
self.btn.clicked.connect(self.btnClicked)
self.pnl = QLabel(self)
lay.addWidget(self.cmb)
lay.addWidget(self.btn)
lay.addWidget(self.pnl)
self.setLayout(lay)
def setColor(self, color):
self.cmb.setColor(color)
self.pnl.setStyleSheet('background-color: %s' % color)
def btnClicked(self):
colors = ['red', 'green', 'blue', 'yellow', 'orange']
self.setColor(colors[int(random() * len(colors))])
QComboBox 的逻辑与其他答案类似,但侧重于插入和删除重复项的功能,因此必须在选择项目时才将信息发送到 window。此外,可以将 QColor 作为附加数据传递,而不是传递名称。
import sys
import random
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
QApplication,
QWidget,
QComboBox,
QVBoxLayout,
QPushButton,
QLabel,
)
class ColorComboBox(QComboBox):
colorChanged = pyqtSignal(str, QColor)
def __init__(self, parent=None):
super().__init__(parent)
self.currentIndexChanged.connect(self.handleCurrentIndexChanged)
def handleCurrentIndexChanged(self, index):
key = self.itemText(index)
color = self.itemData(index)
self.colorChanged.emit(key, color)
def addColor(self, key, color):
index = self.findText(key)
if index != -1:
self.removeItem(index)
self.insertItem(0, key, color)
self.setCurrentIndex(0)
class Gui(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(300, 200)
self.cmb = ColorComboBox()
self.btn = QPushButton("Random")
self.pnl = QLabel()
lay = QVBoxLayout(self)
lay.addWidget(self.cmb)
lay.addWidget(self.btn)
lay.addWidget(self.pnl)
self.btn.clicked.connect(self.btnClicked)
self.cmb.colorChanged.connect(self.updateColor)
def btnClicked(self):
colors = [
("red", QColor("red")),
("green", QColor("green")),
("blue", QColor("blue")),
("yellow", QColor("yellow")),
("orange", QColor("orange")),
]
key, color = random.choice(colors)
self.cmb.addColor(key, color)
def updateColor(self, key, color):
self.pnl.setStyleSheet('background-color: %s' % color.name())
if __name__ == "__main__":
app = QApplication(sys.argv)
g = Gui()
g.show()
sys.exit(app.exec_())
我想(在这个类比示例中)通过 QPushButton 单击或 select 从表示 selection 历史的 QComboBox 更改 QLabel 的颜色。在实际代码中,控件多于一个按钮,因此有一种方法 setColor(self, color)
将新颜色添加到 QComboBox 作为其第一项,删除可能的重复项并更改 QLabel 的颜色。这行得通。
第二个选项是从历史QComboBox中选择颜色。我在项目为 pressed
时捕获事件并调用相同的 setColor(self, color)
。这会导致问题。
问题是当 QComboBox 中有颜色时,例如['red', 'blue', 'green']
我选择选项 'blue'
并且项目的顺序更改为 ['blue', 'red', 'green']
(我仍然想要这个),有一些标准的 QComboBox 行为完成了 selection 和 QComboBox selects 'green'
项。所以,QLabel 有正确的颜色,但 QComboBox 没有正确的文本。
如何覆盖“标准行为”并解决这个问题?请试试这个例子:
import sys
from random import random
from PyQt5.QtWidgets import QApplication, QWidget, QComboBox, QVBoxLayout, QPushButton, QLabel
class MyCombo(QComboBox):
def __init__(self, parent):
super().__init__(parent)
self.view().pressed.connect(self.handleItemPressed)
def handleItemPressed(self, index):
color = self.model().itemData(index)[0]
self.parent().setColor(color)
def removeDuplicates(self):
unique = list()
while len(unique) < self.model().rowCount():
if self.itemText(len(unique)) in unique:
self.removeItem(len(unique))
else:
unique.append(self.itemText(len(unique)))
class Gui(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(300, 200)
lay = QVBoxLayout()
self.cmb = MyCombo(self)
self.btn = QPushButton('Random', self)
self.btn.clicked.connect(self.btnClicked)
self.pnl = QLabel(self)
lay.addWidget(self.cmb)
lay.addWidget(self.btn)
lay.addWidget(self.pnl)
self.setLayout(lay)
def setColor(self, color):
self.cmb.insertItem(0, color)
self.cmb.setCurrentIndex(0)
self.cmb.removeDuplicates()
self.pnl.setStyleSheet('background-color: %s' % color)
def btnClicked(self):
colors = ['red', 'green', 'blue', 'yellow', 'orange']
self.setColor(colors[int(random() * len(colors))])
if __name__ == '__main__':
app = QApplication(sys.argv)
g = Gui()
g.show()
sys.exit(app.exec_())
在包括设置当前索引在内的操作过程中重新排列项目从来都不是一件好事,除非您确定跟踪正确的索引并且知道操作顺序发生在两者之间。
在你的情况下,问题是视图的pressed
信号在之前组合框的当前索引被改变,结果是当鼠标按钮 释放时 当前索引也是错误的,这也是由于项目的重新排序。
您的实现还有另一个问题:键盘和鼠标滚轮导航不会正确更新颜色。
我的建议是保持模型不变(除非添加了新颜色),并在显示弹出窗口之前重新排序仅。
为了避免递归,请永远记住,您可以使用 blockSignals()
.
class MyCombo(QComboBox):
colorChanged = pyqtSignal(str)
def __init__(self, parent):
super().__init__(parent)
self.currentIndexChanged.connect(self.emitChangedColor)
def emitChangedColor(self, index):
if index >= 0:
self.colorChanged.emit(self.itemText(index))
def setColor(self, color):
blocked = self.blockSignals(True)
for index in range(self.count()):
if self.itemText(index) == color:
break
else:
index = 0
self.insertItem(index, color)
self.setCurrentIndex(index)
self.blockSignals(blocked)
def showPopup(self):
if self.currentIndex() > 0:
blocked = self.blockSignals(True)
current = self.currentText()
self.removeItem(self.currentIndex())
self.insertItem(0, current)
self.setCurrentIndex(0)
self.blockSignals(blocked)
super().showPopup()
class Gui(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(300, 200)
lay = QVBoxLayout()
self.cmb = MyCombo(self)
self.cmb.colorChanged.connect(self.setColor)
self.btn = QPushButton('Random', self)
self.btn.clicked.connect(self.btnClicked)
self.pnl = QLabel(self)
lay.addWidget(self.cmb)
lay.addWidget(self.btn)
lay.addWidget(self.pnl)
self.setLayout(lay)
def setColor(self, color):
self.cmb.setColor(color)
self.pnl.setStyleSheet('background-color: %s' % color)
def btnClicked(self):
colors = ['red', 'green', 'blue', 'yellow', 'orange']
self.setColor(colors[int(random() * len(colors))])
QComboBox 的逻辑与其他答案类似,但侧重于插入和删除重复项的功能,因此必须在选择项目时才将信息发送到 window。此外,可以将 QColor 作为附加数据传递,而不是传递名称。
import sys
import random
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
QApplication,
QWidget,
QComboBox,
QVBoxLayout,
QPushButton,
QLabel,
)
class ColorComboBox(QComboBox):
colorChanged = pyqtSignal(str, QColor)
def __init__(self, parent=None):
super().__init__(parent)
self.currentIndexChanged.connect(self.handleCurrentIndexChanged)
def handleCurrentIndexChanged(self, index):
key = self.itemText(index)
color = self.itemData(index)
self.colorChanged.emit(key, color)
def addColor(self, key, color):
index = self.findText(key)
if index != -1:
self.removeItem(index)
self.insertItem(0, key, color)
self.setCurrentIndex(0)
class Gui(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(300, 200)
self.cmb = ColorComboBox()
self.btn = QPushButton("Random")
self.pnl = QLabel()
lay = QVBoxLayout(self)
lay.addWidget(self.cmb)
lay.addWidget(self.btn)
lay.addWidget(self.pnl)
self.btn.clicked.connect(self.btnClicked)
self.cmb.colorChanged.connect(self.updateColor)
def btnClicked(self):
colors = [
("red", QColor("red")),
("green", QColor("green")),
("blue", QColor("blue")),
("yellow", QColor("yellow")),
("orange", QColor("orange")),
]
key, color = random.choice(colors)
self.cmb.addColor(key, color)
def updateColor(self, key, color):
self.pnl.setStyleSheet('background-color: %s' % color.name())
if __name__ == "__main__":
app = QApplication(sys.argv)
g = Gui()
g.show()
sys.exit(app.exec_())