How to make QCombobox expandable for hierarchical

2019-07-17 02:03发布

问题:

i have a some dictionary variables such as :

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

i want to select values from qcombobox but firstly, i need to keep the cursor at keys from qcombobox then select the value from the highlighted key.

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

How can i do something like that? Thanks for reply.

回答1:

You can use a QPushButton with a QMenu as I show below:

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_())