mirror of
https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System.git
synced 2024-11-16 05:35:43 +08:00
203 lines
8.4 KiB
Python
203 lines
8.4 KiB
Python
|
import sys
|
||
|
|
||
|
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMessageBox,
|
||
|
QInputDialog, QMenu, QLineEdit)
|
||
|
from PyQt5.QtGui import QIcon
|
||
|
from PyQtAds import QtAds
|
||
|
|
||
|
from dockindockmanager import DockInDockManager
|
||
|
from perspectiveactions import LoadPerspectiveAction, RemovePerspectiveAction
|
||
|
|
||
|
|
||
|
class DockInDockWidget(QWidget):
|
||
|
def __init__(self, parent, perspectives_manager: 'PerspectivesManager', can_create_new_groups: bool = False, top_level_widget = None):
|
||
|
super().__init__(parent)
|
||
|
|
||
|
if top_level_widget is not None:
|
||
|
self.__can_create_new_groups = top_level_widget.can_create_new_groups
|
||
|
else:
|
||
|
self.__can_create_new_groups = can_create_new_groups
|
||
|
self.__top_level_dock_widget = top_level_widget if top_level_widget else self
|
||
|
self.__perspectives_manager = perspectives_manager
|
||
|
self.__new_perspective_default_name: str = ''
|
||
|
|
||
|
layout = QVBoxLayout(self)
|
||
|
layout.setContentsMargins(0,0,0,0)
|
||
|
self.__mgr = DockInDockManager(self)
|
||
|
layout.addWidget(self.__mgr)
|
||
|
|
||
|
def getManager(self) -> 'DockInDockManager':
|
||
|
return self.__mgr
|
||
|
|
||
|
def getTopLevelDockWidget(self) -> 'DockInDockWidget':
|
||
|
return self.__top_level_dock_widget
|
||
|
|
||
|
def canCreateNewGroups(self) -> bool:
|
||
|
return self.__can_create_new_groups
|
||
|
|
||
|
def getPerspectivesManager(self) -> 'PerspectivesManager':
|
||
|
return self.__perspectives_manager
|
||
|
|
||
|
def addTabWidget(self, widget: QWidget, name: str, after: QtAds.CDockAreaWidget, icon = QIcon()) -> QtAds.CDockAreaWidget:
|
||
|
for existing in self.getTopLevelDockWidget().getManager().allDockWidgets(True, True):
|
||
|
if existing[1].objectName() == name:
|
||
|
QMessageBox.critical(self, "Error", "Name '" + name + "' already in use")
|
||
|
return
|
||
|
|
||
|
dock_widget = QtAds.CDockWidget(name)
|
||
|
dock_widget.setWidget(widget)
|
||
|
dock_widget.setIcon(icon)
|
||
|
|
||
|
# Add the dock widget to the top dock widget area
|
||
|
return self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, after)
|
||
|
|
||
|
def isTopLevel(self) -> bool:
|
||
|
return not self.objectName()
|
||
|
|
||
|
def getGroupNameError(self, group_name: str) -> str:
|
||
|
if not group_name:
|
||
|
return "Group must have a non-empty name"
|
||
|
|
||
|
dock_managers = self.__mgr.allManagers(True, True)
|
||
|
for mgr in dock_managers:
|
||
|
if mgr.getGroupName() == group_name:
|
||
|
return "Group name '" + group_name + "' already used"
|
||
|
|
||
|
return ""
|
||
|
|
||
|
def createGroup(self, group_name: str, insert_pos: QtAds.CDockAreaWidget, icon = QIcon()) -> 'DockInDockWidget':
|
||
|
error = self.getGroupNameError(group_name)
|
||
|
if error:
|
||
|
QMessageBox.critical(None, "Error", error)
|
||
|
return
|
||
|
|
||
|
child = DockInDockWidget(self, self.__top_level_dock_widget, self.__perspectives_manager)
|
||
|
child.setObjectName(group_name)
|
||
|
|
||
|
dock_widget = QtAds.CDockWidget(group_name)
|
||
|
dock_widget.setWidget(child)
|
||
|
dock_widget.setIcon(icon)
|
||
|
|
||
|
insert_pos = self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, insert_pos)
|
||
|
|
||
|
return child, insert_pos
|
||
|
|
||
|
def destroyGroup(self, widget_to_remove: 'DockInDockWidget') -> None:
|
||
|
top_level_widget = widget_to_remove.getTopLevelDockWidget()
|
||
|
|
||
|
if top_level_widget and top_level_widget != widget_to_remove:
|
||
|
for dock_widget in widget_to_remove.getManager().getWidgetsInGUIOrder(): #don't use allDockWidgets to preserve sub-groups
|
||
|
MoveDockWidgetAction.move(dock_widget, top_level_widget.getManager())
|
||
|
assert not widget_to_remove.getManager().allDockWidgets(True, True)
|
||
|
|
||
|
# find widget's parent:
|
||
|
for dock_widget in top_level_widget.getManager().allDockWidgets(True, True):
|
||
|
if dockwidget[1].widget() == widget_to_remove:
|
||
|
dockwidget[0].removeDockWidget(dockwidget[1])
|
||
|
del dockwidget[1]
|
||
|
# delete widgetToRemove; automatically deleted when dockWidget is deleted
|
||
|
widget_to_remove = None
|
||
|
break
|
||
|
|
||
|
assert widget_to_remove == None
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
def attachViewMenu(self, menu: QMenu) -> None:
|
||
|
menu.aboutToShow.connect(self.autoFillAttachedViewMenu)
|
||
|
|
||
|
def autoFillAttachedViewMenu(self) -> None:
|
||
|
menu = self.sender()
|
||
|
|
||
|
if menu:
|
||
|
menu.clear()
|
||
|
self.setupViewMenu(menu)
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
def setupViewMenu(self, menu):
|
||
|
dock_managers = self.__mgr.allManagers(True, True)
|
||
|
|
||
|
has_perspectives_menu = False
|
||
|
if self.getTopLevelDockWidget() == self:
|
||
|
has_perspectives_menu = (self.__perspectives_manager != None)
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
organize = menu
|
||
|
if has_perspectives_menu:
|
||
|
organize = menu.addMenu("Organize")
|
||
|
|
||
|
self.setupMenu(organize, dock_managers)
|
||
|
|
||
|
if has_perspectives_menu:
|
||
|
perspectives = menu.addMenu("Perspectives")
|
||
|
self.fillPerspectivesMenu(perspectives)
|
||
|
|
||
|
def setupMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
|
||
|
self.__mgr.fillViewMenu(menu, move_to)
|
||
|
menu.addSeparator()
|
||
|
move_menu = menu.addMenu("Move")
|
||
|
self.__mgr.fillMoveMenu(move_menu, move_to)
|
||
|
|
||
|
def fillPerspectivesMenu(self, menu: QMenu):
|
||
|
menu.addAction("Create perspective...", self.createPerspective)
|
||
|
perspectives_names = []
|
||
|
if self.__perspectives_manager:
|
||
|
perspectives_names = self.__perspectives_manager.perspectiveNames()
|
||
|
|
||
|
if perspectives_names:
|
||
|
load = menu.addMenu("Load perspective")
|
||
|
for name in perspectives_names:
|
||
|
load.addAction(LoadPerspectiveAction(load, name, self))
|
||
|
remove = menu.addMenu("Remove perspective")
|
||
|
for name in perspectives_names:
|
||
|
remove.addAction(RemovePerspectiveAction(remove, name, self))
|
||
|
|
||
|
def setNewPerspectiveDefaultName(default_name: str) -> None:
|
||
|
self.__new_perspective_default_name = default_name
|
||
|
|
||
|
def createPerspective(self) -> None:
|
||
|
if not self.__perspectives_manager:
|
||
|
return
|
||
|
|
||
|
name = self.__new_perspective_default_name
|
||
|
if self.__new_perspective_default_name:
|
||
|
index = 2
|
||
|
while name in self.__perspectives_manager.perspectiveNames():
|
||
|
name = f"{self.__new_perspective_default_name}({index})"
|
||
|
index += 1
|
||
|
|
||
|
while True:
|
||
|
name, ok = QInputDialog.getText(None, "Create perspective", "Enter perspective name", QLineEdit.Normal, name)
|
||
|
if ok:
|
||
|
if not name:
|
||
|
QMessageBox.critical(None, "Error", "Perspective name cannot be empty")
|
||
|
continue
|
||
|
elif name in self.__perspectives_manager.perspectiveNames():
|
||
|
if QMessageBox.critical(None, "Error", f"Perspective '{name}' already exists, overwrite it?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.No:
|
||
|
continue
|
||
|
|
||
|
self.__perspectives_manager.addPerspective(name, self)
|
||
|
break
|
||
|
else:
|
||
|
break
|
||
|
|
||
|
def dumpStatus(self, echo: callable = print, widget: QtAds.CDockWidget = None, tab: str = '', suffix: str = '') -> str:
|
||
|
if widget is not None:
|
||
|
as_mgr = DockInDockManager.dockInAManager(widget)
|
||
|
if as_mgr:
|
||
|
as_mgr.parent().dumpStatus(tab=tab)
|
||
|
else:
|
||
|
echo(tab + widget.objectName() + suffix)
|
||
|
else:
|
||
|
echo(tab + "Group: " + self.getManager().getGroupName())
|
||
|
tab += " "
|
||
|
visible_widgets = set()
|
||
|
for widget in self.getManager().getWidgetsInGUIOrder():
|
||
|
visible_widgets.add(widget)
|
||
|
self.dumpStatus(widget=widget, tab=tab)
|
||
|
|
||
|
for closed in self.getManager().dockWidgetsMap().values():
|
||
|
if not closed in visible_widgets:
|
||
|
self.dumpStatus(widget=closed, tab=tab, suffix=" (closed)")
|