Question: How can I go about creating sub-menus in a QComboBox? I am currently using the default layout offered by the widget but this creates a lengthy pull-down list as seen in the attached image. 

Answer: QComboBox normally uses a QListView as its popup. While it is possible to change it to a QTreeView by calling setView(), its result is sometimes cumbersome and often requires further adjustments to make it usable. Most importantly, the view will not use furhter popups, which can become an issue if the whole structure requires too space horizontally or vertically, or the hierarchy is too complex. A simpler solution for such cases is to use a QToolButton with actual menus. Using some custom functions and signals, you can get a behavior similar to that of a QComboBox, getting the text of the currently selected option (even in its full hierarchy). class FakeCombo(QToolButton): currentItemChanged = pyqtSignal(str) currentItemPathChanged = pyqtSignal(list) _currentAction = None def __init__(self, data=None, *args, **kwargs): super().__init__(*args, **kwargs) self.setPopupMode(self.MenuButtonPopup) menu = QMenu(self) self.setMenu(menu) menu.triggered.connect(self.setCurrentAction) self.pressed.connect(self.showMenu) if data: self.setData(data) def _currentActionPath(self): if not self._currentAction: return [] action = self._currentAction path = [action] while action.parent() != self.menu(): action = action.parent().menuAction() path.append(action) return reversed(path) def _getActionsRecursive(self, parent): for action in parent.actions(): if action.menu(): yield from self._getActionsRecursive(action.menu()) else: yield action def _rebuildList(self): self._actions = tuple(self._getActionsRecursive(self.menu())) def currentItem(self): if not self._currentAction: return '' return self._currentAction.text() def currentItemPath(self): return [a.text() for a in self._currentActionPath()] def setCurrentAction(self, action): if self._currentAction == action: return if not isinstance(action, QAction): action = None self._currentAction = action if action is None: self.currentItemChanged.emit('') self.currentItemPathChanged.emit([]) return path = self.currentItemPath() self.setText(': '.join(path)) self.currentItemChanged.emit(path[-1]) self.currentItemPathChanged.emit(path) def setData(self, data): menu = self.menu() menu.clear() if not data: self.setCurrentAction(None) return for item in data: self.addItem(item, menu) self._rebuildList() self.setCurrentAction(self._actions[0]) def addItem(self, item, parent): if isinstance(item, str): action = QAction(item, parent) elif isinstance(item, (tuple, list)): main, subitems = item action = parent.addAction(main) menu = QMenu() action.setMenu(menu) for other in subitems: self.addItem(other, menu) action.destroyed.connect(menu.clear) parent.addAction(action) return action def mousePressEvent(self, event): if self.menu().actions(): QAbstractButton.mousePressEvent(self, event) def keyPressEvent(self, event): if self.menu().actions() or event.key() != Qt.Key_Space: super().keyPressEvent(event) # simple example of data structure made of tuples: # - "final" items are simple strings # - groups are tuples made of a string and another tuple DATA = ( 'Top level item', ('Group #1', ( 'sub item #1 ', 'sub item #2 ', ('Sub group', ( 'sub-sub item #1', 'sub-sub item #2', )), )), ('Group #2', ( 'sub item #3', )), ) app = QApplication([]) win = QWidget() box = FakeCombo(DATA) itemField = QLineEdit(readOnly=True) pathField = QLineEdit(readOnly=True) layout = QFormLayout(win) layout.addRow('Options:', box) layout.addRow('Current item:', itemField) layout.addRow('Current path:', pathField) def updateCurrent(item): itemField.setText(item) pathField.setText(', '.join(box.currentItemPath())) box.currentItemChanged.connect(updateCurrent) updateCurrent(box.currentItem()) win.show() app.exec()  There's obviously some margin of improvement, for instance allowing wheel and arrow key navigation, highlight of the current item on popup etc.