Thu, 09 Sep 2021 19:22:12 +0200
Made the height/width of the icons bar configurable.
# -*- coding: utf-8 -*- # Copyright (c) 2008 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a sidebar class. """ import enum import json from PyQt6.QtCore import pyqtSlot, Qt, QSize from PyQt6.QtWidgets import QWidget, QStackedWidget, QBoxLayout from .EricIconBar import EricIconBar class EricSideBarSide(enum.Enum): """ Class defining the sidebar sides. """ NORTH = 0 EAST = 1 SOUTH = 2 WEST = 3 class EricSideBar(QWidget): """ Class implementing a sidebar with a widget area, that is hidden or shown, if the current tab is clicked again. """ Version = 3 def __init__(self, orientation=None, iconBarSize=EricIconBar.DefaultBarSize, parent=None): """ Constructor @param orientation orientation of the sidebar widget @type EricSideBarSide @param iconBarSize size category for the bar (one of 'xs', 'sm', 'md', 'lg', 'xl', 'xxl') @type str @param parent parent widget @type QWidget """ super().__init__(parent) # initial layout is done for NORTH self.__iconBar = EricIconBar(orientation=Qt.Orientation.Horizontal, barSize=iconBarSize) self.__stackedWidget = QStackedWidget(self) self.__stackedWidget.setContentsMargins(0, 0, 0, 0) self.layout = QBoxLayout(QBoxLayout.Direction.TopToBottom) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.layout.addWidget(self.__iconBar) self.layout.addWidget(self.__stackedWidget) self.setLayout(self.layout) self.__minimized = False self.__minSize = 0 self.__maxSize = 0 self.__bigSize = QSize() self.splitter = None self.splitterSizes = [] self.__hasFocus = False # flag storing if this widget or any child has the focus self.__autoHide = False self.__orientation = EricSideBarSide.NORTH if orientation is None: orientation = EricSideBarSide.NORTH self.setOrientation(orientation) self.__iconBar.currentChanged.connect( self.__stackedWidget.setCurrentIndex) self.__iconBar.currentChanged.connect( self.__currentIconChanged) self.__iconBar.currentClicked.connect( self.__currentIconClicked) def setSplitter(self, splitter): """ Public method to set the splitter managing the sidebar. @param splitter reference to the splitter (QSplitter) """ self.splitter = splitter self.splitter.splitterMoved.connect(self.__splitterMoved) self.splitter.setChildrenCollapsible(False) index = self.splitter.indexOf(self) self.splitter.setCollapsible(index, False) def __splitterMoved(self, pos, index): """ Private slot to react on splitter moves. @param pos new position of the splitter handle (integer) @param index index of the splitter handle (integer) """ if self.splitter: self.splitterSizes = self.splitter.sizes() def __shrinkIt(self): """ Private method to shrink the sidebar. """ self.__minimized = True self.__bigSize = self.size() if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() if self.splitter: self.splitterSizes = self.splitter.sizes() self.__stackedWidget.hide() if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): self.setFixedHeight(self.__iconBar.minimumSizeHint().height()) else: self.setFixedWidth(self.__iconBar.minimumSizeHint().width()) def __expandIt(self): """ Private method to expand the sidebar. """ self.__minimized = False self.__stackedWidget.show() self.resize(self.__bigSize) if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): minSize = max(self.__minSize, self.minimumSizeHint().height()) self.setMinimumHeight(minSize) self.setMaximumHeight(self.__maxSize) else: minSize = max(self.__minSize, self.minimumSizeHint().width()) self.setMinimumWidth(minSize) self.setMaximumWidth(self.__maxSize) if self.splitter: self.splitter.setSizes(self.splitterSizes) def isMinimized(self): """ Public method to check the minimized state. @return flag indicating the minimized state (boolean) """ return self.__minimized @pyqtSlot(int) def __currentIconChanged(self, index): """ Private slot to handle a change of the current icon. @param index index of the current icon @type int """ if self.isMinimized(): self.__expandIt() @pyqtSlot(int) def __currentIconClicked(self, index): """ Private slot to handle a click of the current icon. @param index index of the clicked icon @type int """ if self.isMinimized(): self.__expandIt() else: self.__shrinkIt() def addTab(self, widget, icon, label=None): """ Public method to add a tab to the sidebar. @param widget reference to the widget to add @type QWidget @param icon reference to the icon of the widget @type QIcon @param label the label text of the widget @type str """ self.__iconBar.addIcon(icon, label) self.__stackedWidget.addWidget(widget) if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def insertTab(self, index, widget, icon, label=None): """ Public method to insert a tab into the sidebar. @param index the index to insert the tab at @type int @param widget reference to the widget to insert @type QWidget @param icon reference to the icon of the widget @type QIcon @param label the label text of the widget @type str """ self.__iconBar.insertIcon(index, icon, label) self.__stackedWidget.insertWidget(index, widget) if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def removeTab(self, index): """ Public method to remove a tab. @param index the index of the tab to remove @type int """ self.__stackedWidget.removeWidget(self.__stackedWidget.widget(index)) self.__iconBar.removeIcon(index) if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): self.__minSize = self.minimumSizeHint().height() else: self.__minSize = self.minimumSizeHint().width() def clear(self): """ Public method to remove all tabs. """ while self.count() > 0: self.removeTab(0) def prevTab(self): """ Public slot used to show the previous tab. """ ind = self.currentIndex() - 1 if ind == -1: ind = self.count() - 1 self.setCurrentIndex(ind) self.currentWidget().setFocus() def nextTab(self): """ Public slot used to show the next tab. """ ind = self.currentIndex() + 1 if ind == self.count(): ind = 0 self.setCurrentIndex(ind) self.currentWidget().setFocus() def count(self): """ Public method to get the number of tabs. @return number of tabs in the sidebar (integer) """ return self.__iconBar.count() def currentIndex(self): """ Public method to get the index of the current tab. @return index of the current tab (integer) """ return self.__stackedWidget.currentIndex() def setCurrentIndex(self, index): """ Public slot to set the current index. @param index the index to set as the current index (integer) """ self.__iconBar.setCurrentIndex(index) self.__stackedWidget.setCurrentIndex(index) if self.isMinimized(): self.__expandIt() def currentWidget(self): """ Public method to get a reference to the current widget. @return reference to the current widget (QWidget) """ return self.__stackedWidget.currentWidget() def setCurrentWidget(self, widget): """ Public slot to set the current widget. @param widget reference to the widget to become the current widget (QWidget) """ self.__stackedWidget.setCurrentWidget(widget) self.__iconBar.setCurrentIndex(self.__stackedWidget.currentIndex()) if self.isMinimized(): self.__expandIt() def indexOf(self, widget): """ Public method to get the index of the given widget. @param widget reference to the widget to get the index of (QWidget) @return index of the given widget (integer) """ return self.__stackedWidget.indexOf(widget) def orientation(self): """ Public method to get the orientation of the sidebar. @return orientation of the sidebar @rtype EricSideBarSide """ return self.__orientation def setOrientation(self, orient): """ Public method to set the orientation of the sidebar. @param orient orientation of the sidebar @type EricSideBarSide """ if orient == EricSideBarSide.NORTH: self.__iconBar.setOrientation(Qt.Orientation.Horizontal) self.layout.setDirection(QBoxLayout.Direction.TopToBottom) elif orient == EricSideBarSide.EAST: self.__iconBar.setOrientation(Qt.Orientation.Vertical) self.layout.setDirection(QBoxLayout.Direction.RightToLeft) elif orient == EricSideBarSide.SOUTH: self.__iconBar.setOrientation(Qt.Orientation.Horizontal) self.layout.setDirection(QBoxLayout.Direction.BottomToTop) elif orient == EricSideBarSide.WEST: self.__iconBar.setOrientation(Qt.Orientation.Vertical) self.layout.setDirection(QBoxLayout.Direction.LeftToRight) self.__orientation = orient def widget(self, index): """ Public method to get a reference to the widget associated with a tab. @param index index of the tab (integer) @return reference to the widget (QWidget) """ return self.__stackedWidget.widget(index) def setIconBarColor(self, color): """ Public method to set the icon bar color. @param color icon bar color @type QColor """ self.__iconBar.setColor(color) def iconBarColor(self): """ Public method to get the icon bar color. @return icon bar color @rtype QColor """ return self.__iconBar.color() def setIconBarSize(self, barSize): """ Public method to set the icon bar size. @param barSize size category for the bar (one of 'xs', 'sm', 'md', 'lg', 'xl', 'xxl') @type str """ self.__iconBar.setBarSize(barSize) def barSize(self): """ Public method to get the icon bar size. @return barSize size category for the bar (one of 'xs', 'sm', 'md', 'lg', 'xl', 'xxl') @rtype str """ return self.__iconBar.barSize() def saveState(self): """ Public method to save the state of the sidebar. @return saved state as a byte array (QByteArray) """ if len(self.splitterSizes) == 0: if self.splitter: self.splitterSizes = self.splitter.sizes() self.__bigSize = self.size() if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): self.__minSize = self.minimumSizeHint().height() self.__maxSize = self.maximumHeight() else: self.__minSize = self.minimumSizeHint().width() self.__maxSize = self.maximumWidth() dataDict = { "version": self.Version, "minimized": self.__minimized, "big_size": [self.__bigSize.width(), self.__bigSize.height()], "min_size": self.__minSize, "max_size": self.__maxSize, "splitter_sizes": self.splitterSizes, } data = json.dumps(dataDict) return data def restoreState(self, state): """ Public method to restore the state of the sidebar. @param state byte array containing the saved state (QByteArray) @return flag indicating success (boolean) """ if not isinstance(state, str) or state == "": return False try: stateDict = json.loads(state) except json.JSONDecodeError: return False if not stateDict: return False if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): minSize = self.layout.minimumSize().height() maxSize = self.maximumHeight() else: minSize = self.layout.minimumSize().width() maxSize = self.maximumWidth() if stateDict["version"] in (2, 3): if stateDict["minimized"] and not self.__minimized: self.__shrinkIt() self.__bigSize = QSize(*stateDict["big_size"]) self.__minSize = max(stateDict["min_size"], minSize) self.__maxSize = max(stateDict["max_size"], maxSize) self.splitterSizes = stateDict["splitter_sizes"] if not stateDict["minimized"]: self.__expandIt() return True return False