2 回答

TA貢獻1784條經驗 獲得超2個贊
我的問題的解決方案是GroupedDragging
在setDockOptions
.?QMainWindow
我設法通過下面的代碼獲得了非常漂亮的外觀和行為,就像我想要的那樣。
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
class DockWidget(QtWidgets.QDockWidget):
? ? def __init__(self, title: str):
? ? ? ? super().__init__(title)
? ? ? ? self.setTitleBarWidget(QtWidgets.QWidget())
? ? ? ? self.dockLocationChanged.connect(self.on_dockLocationChanged)
? ? def on_dockLocationChanged(self):
? ? ? ? main: QtWidgets.QMainWindow = self.parent()
? ? ? ? all_dock_widgets = main.findChildren(QtWidgets.QDockWidget)
? ? ? ? for dock_widget in all_dock_widgets:
? ? ? ? ? ? sibling_tabs = main.tabifiedDockWidgets(dock_widget)
? ? ? ? ? ? # If you pull a tab out of a group the other tabs still see it as a sibling while dragging...
? ? ? ? ? ? sibling_tabs = [s for s in sibling_tabs if not s.isFloating()]
? ? ? ? ? ? if len(sibling_tabs) != 0:
? ? ? ? ? ? ? ? # Hide title bar
? ? ? ? ? ? ? ? dock_widget.setTitleBarWidget(QtWidgets.QWidget())
? ? ? ? ? ? else:
? ? ? ? ? ? ? ? # Re-enable title bar
? ? ? ? ? ? ? ? dock_widget.setTitleBarWidget(None)
? ? def minimumSizeHint(self) -> QtCore.QSize:
? ? ? ? return QtCore.QSize(100, 100)
if __name__ == "__main__":
? ? app = QtWidgets.QApplication([])
? ? main = QtWidgets.QMainWindow()
? ? dock1 = DockWidget("Blue")
? ? dock2 = DockWidget("Green")
? ? dock3 = DockWidget("Red")
? ? content1 = QtWidgets.QWidget()
? ? content1.setStyleSheet("background-color:blue;")
? ? content1.setMinimumSize(QtCore.QSize(50, 50))
? ? content2 = QtWidgets.QWidget()
? ? content2.setStyleSheet("background-color:green;")
? ? content2.setMinimumSize(QtCore.QSize(50, 50))
? ? content3 = QtWidgets.QWidget()
? ? content3.setStyleSheet("background-color:red;")
? ? content3.setMinimumSize(QtCore.QSize(50, 50))
? ? dock1.setWidget(content1)
? ? dock2.setWidget(content2)
? ? dock3.setWidget(content3)
? ? dock1.setAllowedAreas(Qt.AllDockWidgetAreas)
? ? dock2.setAllowedAreas(Qt.AllDockWidgetAreas)
? ? dock3.setAllowedAreas(Qt.AllDockWidgetAreas)
? ? main.addDockWidget(Qt.LeftDockWidgetArea, dock1)
? ? main.tabifyDockWidget(dock1, dock2)
? ? main.addDockWidget(Qt.RightDockWidgetArea, dock3)
? ? main.setDockOptions(main.GroupedDragging | main.AllowTabbedDocks | main.AllowNestedDocks)
? ? main.setTabPosition(Qt.AllDockWidgetAreas, QtWidgets.QTabWidget.North)
? ? main.resize(400, 200)
? ? main.show()
? ? app.exec_()

TA貢獻1936條經驗 獲得超7個贊
將其發布為已接受答案的一點額外內容。該版本適用于 Qt5 和 Qt6,并且能夠檢測極端情況,例如拉出選項卡并將其放入同一組中或將浮動選項卡組與停靠窗口合并:
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import Qt
from typing import TypeVar, List, Optional
TDockWidget = TypeVar('TDockWidget', bound='DockWidget')
class DockWidget(QtWidgets.QDockWidget):
def __init__(self: TDockWidget, title: str, parent: Optional[QtWidgets.QWidget] = None) -> None:
super(_DockWidget, self).__init__(title, parent)
self.setTitleBarWidget(QtWidgets.QWidget())
self.visibilityChanged.connect(self.on_visibility_changed)
self.dockLocationChanged.connect(self.on_dock_location_changed)
@QtCore.Slot(bool)
def on_visibility_changed(self: TDockWidget, is_visible: bool) -> None:
# this visibility monitor is really only needed to detect merges of
# tabbed, floating windows with existing docked windows
if not is_visible and isinstance(self.parent(), QtWidgets.QMainWindow):
main_window: QtWidgets.QMainWindow = self.parent()
all_dockwidgets: List[QtWidgets.QDockWidget] = main_window.findChildren(QtWidgets.QDockWidget)
for dockwidget in all_dockwidgets:
if hasattr(dockwidget, 'on_dock_location_changed'):
dockwidget.on_dock_location_changed(main_window.dockWidgetArea(dockwidget), False)
@QtCore.Slot(Qt.DockWidgetArea)
def on_dock_location_changed(self: TDockWidget, area: Qt.DockWidgetArea, update_others: bool = True) -> None:
if not isinstance(self.parent(), QtWidgets.QMainWindow):
# mysterious parents call for a title
self.setTitleBarWidget(None)
return
main_window: QtWidgets.QMainWindow = self.parent()
if not main_window.tabifiedDockWidgets(self):
# if there's no siblings we ain't a tab!
self.setTitleBarWidget(None)
if not update_others:
# prevent infinite recursion
return
# force an update to all other docks that may now no longer be tabs
all_dockwidgets: List[QtWidgets.QDockWidget] = main_window.findChildren(QtWidgets.QDockWidget)
for dockwidget in all_dockwidgets:
if dockwidget != self and hasattr(dockwidget, 'on_dock_location_changed'):
dockwidget.on_dock_location_changed(main_window.dockWidgetArea(dockwidget), False)
return
# at this point the dockwidget is either a resting tab or a tab
# that is being dragged and hasn't been dropped yet (siblings are updated post-drop)
# collect all siblings of this dockwidget...
tab_siblings: List[QtWidgets.QDockWidget] = main_window.tabifiedDockWidgets(self)
# and filter for non-floating siblings in the same area
tab_siblings = [x for x in tab_siblings if main_window.dockWidgetArea(x) == area and not x.isFloating()]
if tab_siblings:
if self.titleBarWidget() is not None:
# no changes needed, prevent infinite recursion
return
# show a title if we're not floating (this tab is settled),
# hide it otherwise (this tab just became floating but wasn't dropped)
self.setTitleBarWidget(QtWidgets.QWidget() if not self.isFloating() else None)
# in this case it's also a good idea to tell to reconsider their situation
# since Qt won't notify them separately
for sibling in tab_siblings:
if hasattr(sibling, 'on_dock_location_changed'):
sibling.on_dock_location_changed(main_window.dockWidgetArea(sibling), True)
else:
self.setTitleBarWidget(None)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
main = QtWidgets.QMainWindow()
dock1 = DockWidget("Blue")
dock2 = DockWidget("Green")
dock3 = DockWidget("Red")
content1 = QtWidgets.QWidget()
content1.setStyleSheet("background-color:blue;")
content1.setMinimumSize(QtCore.QSize(50, 50))
content2 = QtWidgets.QWidget()
content2.setStyleSheet("background-color:green;")
content2.setMinimumSize(QtCore.QSize(50, 50))
content3 = QtWidgets.QWidget()
content3.setStyleSheet("background-color:red;")
content3.setMinimumSize(QtCore.QSize(50, 50))
dock1.setWidget(content1)
dock2.setWidget(content2)
dock3.setWidget(content3)
dock1.setAllowedAreas(Qt.AllDockWidgetAreas)
dock2.setAllowedAreas(Qt.AllDockWidgetAreas)
dock3.setAllowedAreas(Qt.AllDockWidgetAreas)
main.addDockWidget(Qt.LeftDockWidgetArea, dock1)
main.tabifyDockWidget(dock1, dock2)
main.addDockWidget(Qt.RightDockWidgetArea, dock3)
main.setDockOptions(main.GroupedDragging | main.AllowTabbedDocks | main.AllowNestedDocks)
main.setTabPosition(Qt.AllDockWidgetAreas, QtWidgets.QTabWidget.North)
main.resize(400, 200)
main.show()
app.exec_()
添加回答
舉報