如何使 QCombobox 可扩展为分层项目?

How to make QCombobox expandable for hierarchical items?

我有一些字典变量,例如:

{"Courses" : ["Math" , "Science" , "English"]} , 
{"Cars" : ["Ford" , "BMW" , "Mercedes"]} , etc.

我想 select 来自 qcombobox 的值,但首先,我需要将光标保持在来自 qcombobox 的键上,然后 select 来自突出显示键的值。

Courses
Cars-->Ford
    -->BMW
    -->Mercedes

我该怎么做? 谢谢回复。

您可以使用 QPushButtonQMenu,如下所示:

PyQt4:

from PyQt4 import QtCore, QtGui

data =[{
        "Courses": ["Math", "Science", "English"]
    },
    {
        "Cars": ["Ford", "BMW", "Mercedes"]
    }
]

class ComboBoxExpandable(QtGui.QPushButton):
    currentTextChanged = QtCore.pyqtSignal(str)
    pathChanged = QtCore.pyqtSignal(list)

    def setData(self, value):
        menu = QtGui.QMenu(self)
        self.setMenu(menu)
        self.append_element(value, menu)
        menu.triggered.connect(self.on_triggered)

    @QtCore.pyqtSlot(QtGui.QAction)
    def on_triggered(self, action):
        self.setText(action.text())
        path = [action.text()] 
        w = action.parentWidget()
        while w.parentWidget() and isinstance(w.parentWidget(), QtGui.QMenu):
            path.insert(0, w.title())
            w = w.parentWidget()
        self.pathChanged.emit(path)
        self.currentTextChanged.emit(self.text())

    @staticmethod
    def append_element(value, menu):
        if isinstance(value, list):
            for e in value:
                ComboBoxExpandable.append_element(e, menu)
        elif isinstance(value, dict):
            for k, v in value.items():
                ComboBoxExpandable.append_element(v, menu.addMenu(k))
        else:
            menu.addAction(value)


class Widget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        combo = ComboBoxExpandable()
        combo.setData(data)

        result_label = QtGui.QLabel()

        combo.currentTextChanged.connect(result_label.setText)
        combo.pathChanged.connect(print)

        lay = QtGui.QFormLayout(self)
        lay.addRow("Select: ", combo)
        lay.addRow("Result: ", result_label)


if __name__ == '__main__':
    import sys

    app = QtGui.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

PyQt5:

from PyQt5 import QtCore, QtWidgets

data =[{
        "Courses": ["Math", "Science", "English"]
    },
    {
        "Cars": ["Ford", "BMW", "Mercedes"]
    }
]

class ComboBoxExpandable(QtWidgets.QPushButton):
    currentTextChanged = QtCore.pyqtSignal(str)
    pathChanged = QtCore.pyqtSignal(list)

    def setData(self, value):
        menu = QtWidgets.QMenu(self)
        self.setMenu(menu)
        self.append_element(value, menu)
        menu.triggered.connect(self.on_triggered)

    @QtCore.pyqtSlot(QtWidgets.QAction)
    def on_triggered(self, action):
        self.setText(action.text())
        path = [action.text()] 
        w = action.parentWidget()
        while w.parentWidget() and isinstance(w.parentWidget(), QtWidgets.QMenu):
            path.insert(0, w.title())
            w = w.parentWidget()
        self.pathChanged.emit(path)
        self.currentTextChanged.emit(self.text())

    @staticmethod
    def append_element(value, menu):
        if isinstance(value, list):
            for e in value:
                ComboBoxExpandable.append_element(e, menu)
        elif isinstance(value, dict):
            for k, v in value.items():
                ComboBoxExpandable.append_element(v, menu.addMenu(k))
        else:
            menu.addAction(value)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        combo = ComboBoxExpandable()
        combo.setData(data)

        result_label = QtWidgets.QLabel()

        combo.currentTextChanged.connect(result_label.setText)
        combo.pathChanged.connect(print)

        lay = QtWidgets.QFormLayout(self)
        lay.addRow("Select: ", combo)
        lay.addRow("Result: ", result_label)


if __name__ == '__main__':
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())