def create_layout(self): ''' Creates the complete layout including all controls ''' self.title_label = ElidingLabel(text=self.dock_widget.windowTitle()) self.title_label.set_elide_mode(Qt.ElideRight) self.title_label.setObjectName("dockWidgetTabLabel") self.title_label.setAlignment(Qt.AlignCenter) self.close_button = QPushButton() self.close_button.setObjectName("tabCloseButton") set_button_icon(self.public.style(), self.close_button, QStyle.SP_TitleBarCloseButton) self.close_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.close_button.setVisible(False) self.close_button.setToolTip("Close Tab") self.close_button.clicked.connect(self.public.close_requested) fm = QFontMetrics(self.title_label.font()) spacing = round(fm.height() / 4.0) # Fill the layout layout = QBoxLayout(QBoxLayout.LeftToRight) layout.setContentsMargins(2 * spacing, 0, 0, 0) layout.setSpacing(0) self.public.setLayout(layout) layout.addWidget(self.title_label, 1) layout.addSpacing(spacing) layout.addWidget(self.close_button) layout.addSpacing(round(spacing * 4.0 / 3.0)) layout.setAlignment(Qt.AlignCenter) self.title_label.setVisible(True)
def __init__(self, *, dock_area: 'DockAreaWidget' = None, dock_widget: 'DockWidget' = None, dock_manager: 'DockManager' = None): ''' Parameters ---------- dock_manager : DockManager dock_area : DockAreaWidget Create floating widget with the given dock area ''' if dock_manager is None: if dock_area is not None: dock_manager = dock_area.dock_manager() elif dock_widget is not None: dock_manager = dock_widget.dock_manager() if dock_manager is None: raise ValueError('Must pass in either dock_area, dock_widget, or dock_manager') super().__init__(dock_manager) self.d = FloatingDockContainerPrivate(self) self.d.dock_manager = dock_manager dock_container = DockContainerWidget(dock_manager, self) self.d.dock_container = dock_container dock_container.destroyed.connect(self._destroyed) dock_container.dock_areas_added.connect(self.on_dock_areas_added_or_removed) dock_container.dock_areas_removed.connect(self.on_dock_areas_added_or_removed) if LINUX: self.d.title_bar = FloatingWidgetTitleBar(self) self.setWindowFlags(super().windowFlags() | Qt.Tool) self.setWidget(self.d.dock_container) self.setFloating(True) self.setFeatures(QDockWidget.AllDockWidgetFeatures) self.setTitleBarWidget(self.d.title_bar) self.d.title_bar.close_requested.connect(self.close) else: self.setWindowFlags(Qt.Window | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) layout = QBoxLayout(QBoxLayout.TopToBottom) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) layout.addWidget(dock_container) dock_manager.register_floating_widget(self) # We install an event filter to detect mouse release events because we # do not receive mouse release event if the floating widget is behind # the drop overlay cross qapp = QApplication.instance() qapp.installEventFilter(self) if dock_area is not None: dock_container.add_dock_area(dock_area) elif dock_widget is not None: dock_container.add_dock_widget( DockWidgetArea.center, dock_widget) if (dock_area or dock_widget) and LINUX: self.d.title_bar.enable_close_button(self.is_closable())
def create_layout(self): ''' Creates the complete layout including all controls ''' self.title_label = ElidingLabel() self.title_label.set_elide_mode(Qt.ElideRight) self.title_label.setText("DockWidget->windowTitle()") self.title_label.setObjectName("floatingTitleLabel") self.title_label.setAlignment(Qt.AlignLeft) self.close_button = QPushButton() self.close_button.setObjectName("floatingTitleCloseButton") self.close_button.setFlat(True) # self.close_button.setAutoRaise(True) set_button_icon(self.public.style(), self.close_button, QStyle.SP_TitleBarCloseButton) self.close_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.close_button.setVisible(True) self.close_button.setFocusPolicy(Qt.NoFocus) self.close_button.clicked.connect(self.public.close_requested) fm = QFontMetrics(self.title_label.font()) spacing = round(fm.height() / 4.0) # Fill the layout layout = QBoxLayout(QBoxLayout.LeftToRight) layout.setContentsMargins(6, 0, 0, 0) layout.setSpacing(0) self.public.setLayout(layout) layout.addWidget(self.title_label, 1) layout.addSpacing(spacing) layout.addWidget(self.close_button) layout.setAlignment(Qt.AlignCenter) self.title_label.setVisible(True)
class ExtendedTabBar(QFrame): ''' A tab bar that has QToolBars to the left, right, and floating at the end of the tabs. Note that although this class inherits from QFrame, __getattr__() trickery is used to "inherit" the attributes of an internal object that inherits from QTabBar. This is done because it allows the actual tab bar object to be placed in a layout with other widgets, while allowing this class to be treated as the tab bar itself. ''' RoundedNorth = QTabBar.RoundedNorth RoundedSouth = QTabBar.RoundedSouth RoundedWest = QTabBar.RoundedWest RoundedEast = QTabBar.RoundedEast TriangularNorth = QTabBar.TriangularNorth TriangularSouth = QTabBar.TriangularSouth TriangularWest = QTabBar.TriangularWest TriangularEast = QTabBar.TriangularEast def __init__(self): super(ExtendedTabBar, self).__init__() self._tab_bar = _NoMinimumWidthTabBar() self._left_toolbar = QToolBar() self._floating_toolbar = QToolBar() self._right_toolbar = QToolBar() # Setup the layout. self._main_layout = QBoxLayout(QBoxLayout.LeftToRight) self._main_layout.setContentsMargins(0, 0, 0, 0) self._main_layout.setSpacing(0) self._main_layout.addWidget(self._left_toolbar) self._main_layout.addWidget(self._tab_bar) self._main_layout.addWidget(self._floating_toolbar) self._main_layout.addStretch() self._main_layout.addWidget(self._right_toolbar) self.setLayout(self._main_layout) def __getattr__(self, name): ''' Return the internal tab bar object's attributes if this class does not have the given attribute. ''' return self.__dict__.get(name, getattr(self._tab_bar, name)) def minimumSizeHint(self): ''' Add on any margins to the minimum size hint. Keeps the widget from getting sized so that the tab tops are cut off. ''' margins = self._main_layout.contentsMargins() minimum_size_hint = self._tab_bar.minimumSizeHint() if self.shape() in { QTabBar.RoundedNorth, QTabBar.RoundedSouth, QTabBar.TriangularNorth, QTabBar.TriangularSouth }: return minimum_size_hint + QSize(0, margins.top() + margins.bottom()) else: return minimum_size_hint + QSize(margins.left() + margins.right(), 0) def setShape(self, shape): ''' Sets the tab shapes. shape One of: ExtendedTabBar.RoundedNorth ExtendedTabBar.RoundedSouth ExtendedTabBar.RoundedWest ExtendedTabBar.RoundedEast ExtendedTabBar.TriangularNorth ExtendedTabBar.TriangularSouth ExtendedTabBar.TriangularWest ExtendedTabBar.TriangularEast ''' self._tab_bar.setShape(shape) if shape in { QTabBar.RoundedNorth, QTabBar.RoundedSouth, QTabBar.TriangularNorth, QTabBar.TriangularSouth }: direction = QBoxLayout.LeftToRight orientation = Qt.Horizontal else: direction = QBoxLayout.TopToBottom orientation = Qt.Vertical self._main_layout.setDirection(direction) self._left_toolbar.setOrientation(orientation) self._floating_toolbar.setOrientation(orientation) self._right_toolbar.setOrientation(orientation) @property def left_toolbar(self): ''' Returns the QToolBar to the left (top) of the tabs. ''' return self._left_toolbar @property def floating_toolbar(self): ''' Returns the QToolBar floating to the right (bottom) of the tabs. ''' return self._floating_toolbar @property def right_toolbar(self): ''' Returns the QToolBar to the right (bottom) of the tabs. ''' return self._right_toolbar
class ExtendedTabWidget(QFrame): ''' Like QTabWidget, except the tab bar is located elsewhere. Intended for use with ExtendedTabBar. ''' def __init__(self): super(ExtendedTabWidget, self).__init__() self._tab_bar = None self._stack = QStackedWidget() self._main_layout = QBoxLayout(QBoxLayout.LeftToRight) self._main_layout.setContentsMargins(0, 0, 0, 0) self._main_layout.addWidget(self._stack) self.setLayout(self._main_layout) def _move_tab(self, from_, to): ''' Handles tab moves so that the tab bar indices stay aligned with the widget stack indices. ''' displaced_widget = self._stack.widget(from_) moved_widget = self._stack.widget(to) self._stack.removeWidget(moved_widget) self._stack.removeWidget(displaced_widget) self._stack.insertWidget(to, displaced_widget) self._stack.insertWidget(from_, moved_widget) self._stack.setCurrentIndex(self._tab_bar.currentIndex()) def setTabBar(self, tab_bar): ''' Sets the tab bar that will be used to switch between tabs. tab_bar The tab bar to set as the controller of this widget. ''' if self._tab_bar is not None: raise Exception('Tab bar already set.') self._tab_bar = tab_bar tab_bar.currentChanged.connect(self._stack.setCurrentIndex) tab_bar.tabCloseRequested.connect(self.closeTab) tab_bar.tabMoved.connect(self._move_tab) def closeTab(self, index): ''' Closes a tab, removing from this widget, the tab bar, and deleting its widget. index Index of the tab to be closed. ''' self._tab_bar.removeTab(index) widget = self._stack.widget(index) self._stack.removeWidget(widget) widget.deleteLater() self._stack.setCurrentIndex(self._tab_bar.currentIndex()) def addTab(self, widget, label): ''' Adds a tab. widget The widget for the tab contents. label The name of the tab to show in the tab bar. Returns the index of the added tab. ''' index = self._tab_bar.addTab(label) self._stack.insertWidget(index, widget) return index def count(self): ''' Returns the number of widgets. ''' return self._stack.count() def widget(self, index): ''' Returns the widget at the given index. ''' return self._stack.widget(index) def indexOf(self, widget): ''' Returns the index of the given widget. ''' return self._stack.indexOf(widget)