from logging import getLogger from typing import Union, Protocol from PyQt5.QtCore import ( Qt, QRect, QSize, QPoint, pyqtSignal, ) from PyQt5.QtGui import QFontMetrics from PyQt5.QtWidgets import ( QStyle, QLabel, QWidget, QStyleOptionTab, QStylePainter, QTabBar, QTabWidget, QVBoxLayout, QScrollArea, QLayout, ) from rare.utils.misc import icon as qta_icon logger = getLogger("SideTab") class SideTabBar(QTabBar): def __init__(self, padding: int = -1, parent=None): super(SideTabBar, self).__init__(parent=parent) self.setObjectName("SideTabBar") self.padding = padding self.fm = QFontMetrics(self.font()) def tabSizeHint(self, index): width = QTabBar.tabSizeHint(self, index).height() if self.padding < 0: width += QTabBar.tabSizeHint(self, index).width() else: width += self.padding return QSize(width, self.fm.height() + 18) def paintEvent(self, event): painter = QStylePainter(self) opt = QStyleOptionTab() for i in range(self.count()): self.initStyleOption(opt, i) painter.drawControl(QStyle.CE_TabBarTabShape, opt) painter.save() s = opt.rect.size() s.transpose() r = QRect(QPoint(), s) r.moveCenter(opt.rect.center()) opt.rect = r c = self.tabRect(i).center() painter.translate(c) painter.rotate(90) painter.translate(-c) painter.drawControl(QStyle.CE_TabBarTabLabel, opt) painter.restore() class SideTabContents(object): # str: title set_title = pyqtSignal(str) class SideTabContentsProtocol(Protocol): def layout(self) -> QLayout: pass def set_title(self) -> pyqtSignal: pass def sizeHint(self) -> QSize: pass class SideTabContainer(QWidget): def __init__(self, widget: Union[QWidget, SideTabContentsProtocol], title: str = "", parent: QWidget = None): super(SideTabContainer, self).__init__(parent=parent) self.title = QLabel(self) self.setTitle(title) self.scrollarea = QScrollArea(self) self.scrollarea.setWidgetResizable(True) self.scrollarea.setSizeAdjustPolicy(QScrollArea.AdjustToContents) self.scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scrollarea.setFrameStyle(QScrollArea.NoFrame) if widget.layout(): widget.layout().setAlignment(Qt.AlignTop) widget.layout().setContentsMargins(0, 0, 3, 0) if hasattr(widget, "set_title"): widget.set_title.connect(self.setTitle) self.scrollarea.setMinimumWidth( widget.sizeHint().width() + self.scrollarea.verticalScrollBar().sizeHint().width() ) self.scrollarea.setWidget(widget) layout = QVBoxLayout(self) layout.addWidget(self.title) layout.addWidget(self.scrollarea) def setTitle(self, text: str) -> None: self.title.setText(f"