class TabWindow(QMainWindow): def __init__(self, app, **kwargs): super().__init__(None, **kwargs) self.app = app self.pages = {} self.menubar = None self.menuList = set() self.last_index = -1 self.previous_widget_actions = set() self._setupUi() self.app.willSavePrefs.connect(self.appWillSavePrefs) def _setupActions(self): # (name, shortcut, icon, desc, func) ACTIONS = [ ( "actionToggleTabs", "", "", tr("Show tab bar"), self.toggleTabBar, ), ] createActions(ACTIONS, self) self.actionToggleTabs.setCheckable(True) self.actionToggleTabs.setChecked(True) def _setupUi(self): self.setWindowTitle(self.app.NAME) self.resize(640, 480) self.tabWidget = QTabWidget() # self.tabWidget.setTabPosition(QTabWidget.South) self.tabWidget.setContentsMargins(0, 0, 0, 0) # self.tabWidget.setTabBarAutoHide(True) # This gets rid of the annoying margin around the TabWidget: self.tabWidget.setDocumentMode(True) self._setupActions() self._setupMenu() # This should be the same as self.centralWidget.setLayout(self.verticalLayout) self.verticalLayout = QVBoxLayout(self.tabWidget) # self.verticalLayout.addWidget(self.tabWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.tabWidget.setTabsClosable(True) self.setCentralWidget(self.tabWidget) # only for QMainWindow self.tabWidget.currentChanged.connect(self.updateMenuBar) self.tabWidget.tabCloseRequested.connect(self.onTabCloseRequested) self.updateMenuBar(self.tabWidget.currentIndex()) self.restoreGeometry() def restoreGeometry(self): if self.app.prefs.mainWindowRect is not None: self.setGeometry(self.app.prefs.mainWindowRect) else: moveToScreenCenter(self) def _setupMenu(self): """Setup the menubar boiler plates which will be filled by the underlying tab's widgets whenever they are instantiated.""" self.menubar = self.menuBar( ) # QMainWindow, similar to just QMenuBar() here # self.setMenuBar(self.menubar) # already set if QMainWindow class self.menubar.setGeometry(QRect(0, 0, 100, 22)) self.menuFile = QMenu(self.menubar) self.menuFile.setTitle(tr("File")) self.menuMark = QMenu(self.menubar) self.menuMark.setTitle(tr("Mark")) self.menuActions = QMenu(self.menubar) self.menuActions.setTitle(tr("Actions")) self.menuColumns = QMenu(self.menubar) self.menuColumns.setTitle(tr("Columns")) self.menuView = QMenu(self.menubar) self.menuView.setTitle(tr("View")) self.menuHelp = QMenu(self.menubar) self.menuHelp.setTitle(tr("Help")) self.menuView.addAction(self.actionToggleTabs) self.menuView.addSeparator() self.menuList.add(self.menuFile) self.menuList.add(self.menuMark) self.menuList.add(self.menuActions) self.menuList.add(self.menuColumns) self.menuList.add(self.menuView) self.menuList.add(self.menuHelp) @pyqtSlot(int) def updateMenuBar(self, page_index=None): if page_index < 0: return current_index = self.getCurrentIndex() active_widget = self.getWidgetAtIndex(current_index) if self.last_index < 0: self.last_index = current_index self.previous_widget_actions = active_widget.specific_actions return page_type = type(active_widget).__name__ for menu in self.menuList: if menu is self.menuColumns or menu is self.menuActions or menu is self.menuMark: if not isinstance(active_widget, ResultWindow): menu.setEnabled(False) continue else: menu.setEnabled(True) for action in menu.actions(): if action not in active_widget.specific_actions: if action in self.previous_widget_actions: action.setEnabled(False) continue action.setEnabled(True) self.app.directories_dialog.actionShowResultsWindow.setEnabled( False if page_type == "ResultWindow" else self.app.resultWindow is not None) self.app.actionIgnoreList.setEnabled( True if self.app.ignoreListDialog is not None and not page_type == "IgnoreListDialog" else False) self.app.actionDirectoriesWindow.setEnabled( False if page_type == "DirectoriesDialog" else True) self.previous_widget_actions = active_widget.specific_actions self.last_index = current_index def createPage(self, cls, **kwargs): app = kwargs.get("app", self.app) page = None if cls == "DirectoriesDialog": page = DirectoriesDialog(app) elif cls == "ResultWindow": parent = kwargs.get("parent", self) page = ResultWindow(parent, app) elif cls == "IgnoreListDialog": parent = kwargs.get("parent", self) model = kwargs.get("model") page = IgnoreListDialog(parent, model) self.pages[cls] = page return page def addTab(self, page, title, switch=False): # Warning: this supposedly takes ownership of the page index = self.tabWidget.addTab(page, title) # index = self.tabWidget.insertTab(-1, page, title) if isinstance(page, DirectoriesDialog): self.tabWidget.tabBar().setTabButton(index, QTabBar.RightSide, None) if switch: self.setCurrentIndex(index) return index def showTab(self, page): index = self.indexOfWidget(page) self.setTabVisible(index, True) self.setCurrentIndex(index) def indexOfWidget(self, widget): return self.tabWidget.indexOf(widget) def setCurrentIndex(self, index): return self.tabWidget.setCurrentIndex(index) def setTabVisible(self, index, value): return self.tabWidget.setTabVisible(index, value) def removeTab(self, index): return self.tabWidget.removeTab(index) def isTabVisible(self, index): return self.tabWidget.isTabVisible(index) def getCurrentIndex(self): return self.tabWidget.currentIndex() def getWidgetAtIndex(self, index): return self.tabWidget.widget(index) def getCount(self): return self.tabWidget.count() # --- Events def appWillSavePrefs(self): # Right now this is useless since the first spawn dialog inside the # QTabWidget will assign its geometry after restoring it prefs = self.app.prefs prefs.mainWindowIsMaximized = self.isMaximized() prefs.mainWindowRect = self.geometry() def closeEvent(self, close_event): # Force closing of our tabbed widgets in reverse order so that the # directories dialog (which usually is at index 0) will be called last for index in range(self.getCount() - 1, -1, -1): self.getWidgetAtIndex(index).closeEvent(close_event) self.appWillSavePrefs() @pyqtSlot(int) def onTabCloseRequested(self, index): current_widget = self.getWidgetAtIndex(index) if isinstance(current_widget, DirectoriesDialog): # if we close this one, the application quits. Force user to use the # menu or shortcut. But this is useless if we don't have a button # set up to make a close request anyway. This check could be removed. return current_widget.close() self.setTabVisible(index, False) # self.tabWidget.widget(index).hide() self.removeTab(index) @pyqtSlot() def onDialogAccepted(self): """Remove tabbed dialog when Accepted/Done.""" widget = self.sender() index = self.indexOfWidget(widget) if index > -1: self.removeTab(index) @pyqtSlot() def toggleTabBar(self): value = self.sender().isChecked() self.actionToggleTabs.setChecked(value) self.tabWidget.tabBar().setVisible(value)