【问题标题】:How to add and sort items in QComboBox如何在 QComboBox 中添加和排序项目
【发布时间】:2021-05-20 09:54:18
【问题描述】:

我想(在这个类比示例中)通过单击 QPushButton 或从代表选择历史的 QComboBox 中选择它来更改 QLabel 的颜色。在实际代码中,控件多于一个按钮,因此有一种方法setColor(self, color) 将新颜色添加到 QComboBox 作为其第一项,删除可能的重复项并更改 QLabel 的颜色。这行得通。

第二个选项是从历史 QComboBox 中选择颜色。当一个项目是pressed 并调用相同的setColor(self, color) 时,我会捕获事件。这会导致问题。

问题是当 QComboBox 中有颜色时,例如['red', 'blue', 'green'] 我选择选项'blue' 并且项目的顺序更改为['blue', 'red', 'green'](我仍然想要这个),有一些标准的QComboBox 行为完成选择并且QComboBox 选择'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 信号而不是QComboBox 的currentIndexChanged
  • 我想避免循环引用:当我从btnClicked 调用setColor 时,索引已更改并触发currentIndexChanged,它再次调用setColor,因为通过选择@ 项目更改了时间987654335@ 被触发并且必须调用setColor 来处理所有更改。还是有更好的解决方案?

标签: python-3.x pyqt5 qcombobox


【解决方案1】:

使用与其他答案类似的逻辑,但侧重于插入和删除重复项的功能,QComboBox 必须这样做,并且仅在选择项目时将信息发送到窗口。此外,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_())

【讨论】:

  • 不错的解决方案。顺便说一句,handleCurrentIndexChanged 中的index 可能是-1,所以很好处理。
【解决方案2】:

在包括设置当前索引在内的操作期间重新排列项目绝不是一件好事,除非您确定要跟踪正确的索引并且知道其间发生的操作顺序。

在你的情况下,问题是视图的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))])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 2021-04-12
    • 2020-04-10
    • 1970-01-01
    • 2019-12-08
    • 1970-01-01
    相关资源
    最近更新 更多