1
+ import sys
2
+
3
+ from PyQt5 .QtWidgets import (QApplication , QWidget , QVBoxLayout , QMessageBox ,
4
+ QInputDialog , QMenu , QLineEdit )
5
+ from PyQt5 .QtGui import QIcon
6
+ from PyQtAds import QtAds
7
+
8
+ from dockindockmanager import DockInDockManager
9
+ from perspectiveactions import LoadPerspectiveAction , RemovePerspectiveAction
10
+
11
+
12
+ class DockInDockWidget (QWidget ):
13
+ def __init__ (self , parent , perspectives_manager : 'PerspectivesManager' , can_create_new_groups : bool = False , top_level_widget = None ):
14
+ super ().__init__ (parent )
15
+
16
+ if top_level_widget is not None :
17
+ self .__can_create_new_groups = top_level_widget .can_create_new_groups
18
+ else :
19
+ self .__can_create_new_groups = can_create_new_groups
20
+ self .__top_level_dock_widget = top_level_widget if top_level_widget else self
21
+ self .__perspectives_manager = perspectives_manager
22
+ self .__new_perspective_default_name : str = ''
23
+
24
+ layout = QVBoxLayout (self )
25
+ layout .setContentsMargins (0 ,0 ,0 ,0 )
26
+ self .__mgr = DockInDockManager (self )
27
+ layout .addWidget (self .__mgr )
28
+
29
+ def getManager (self ) -> 'DockInDockManager' :
30
+ return self .__mgr
31
+
32
+ def getTopLevelDockWidget (self ) -> 'DockInDockWidget' :
33
+ return self .__top_level_dock_widget
34
+
35
+ def canCreateNewGroups (self ) -> bool :
36
+ return self .__can_create_new_groups
37
+
38
+ def getPerspectivesManager (self ) -> 'PerspectivesManager' :
39
+ return self .__perspectives_manager
40
+
41
+ def addTabWidget (self , widget : QWidget , name : str , after : QtAds .CDockAreaWidget , icon = QIcon ()) -> QtAds .CDockAreaWidget :
42
+ for existing in self .getTopLevelDockWidget ().getManager ().allDockWidgets (True , True ):
43
+ if existing [1 ].objectName () == name :
44
+ QMessageBox .critical (self , "Error" , "Name '" + name + "' already in use" )
45
+ return
46
+
47
+ dock_widget = QtAds .CDockWidget (name )
48
+ dock_widget .setWidget (widget )
49
+ dock_widget .setIcon (icon )
50
+
51
+ # Add the dock widget to the top dock widget area
52
+ return self .__mgr .addDockWidget (QtAds .CenterDockWidgetArea , dock_widget , after )
53
+
54
+ def isTopLevel (self ) -> bool :
55
+ return not self .objectName ()
56
+
57
+ def getGroupNameError (self , group_name : str ) -> str :
58
+ if not group_name :
59
+ return "Group must have a non-empty name"
60
+
61
+ dock_managers = self .__mgr .allManagers (True , True )
62
+ for mgr in dock_managers :
63
+ if mgr .getGroupName () == group_name :
64
+ return "Group name '" + group_name + "' already used"
65
+
66
+ return ""
67
+
68
+ def createGroup (self , group_name : str , insert_pos : QtAds .CDockAreaWidget , icon = QIcon ()) -> 'DockInDockWidget' :
69
+ error = self .getGroupNameError (group_name )
70
+ if error :
71
+ QMessageBox .critical (None , "Error" , error )
72
+ return
73
+
74
+ child = DockInDockWidget (self , self .__top_level_dock_widget , self .__perspectives_manager )
75
+ child .setObjectName (group_name )
76
+
77
+ dock_widget = QtAds .CDockWidget (group_name )
78
+ dock_widget .setWidget (child )
79
+ dock_widget .setIcon (icon )
80
+
81
+ insert_pos = self .__mgr .addDockWidget (QtAds .CenterDockWidgetArea , dock_widget , insert_pos )
82
+
83
+ return child , insert_pos
84
+
85
+ def destroyGroup (self , widget_to_remove : 'DockInDockWidget' ) -> None :
86
+ top_level_widget = widget_to_remove .getTopLevelDockWidget ()
87
+
88
+ if top_level_widget and top_level_widget != widget_to_remove :
89
+ for dock_widget in widget_to_remove .getManager ().getWidgetsInGUIOrder (): #don't use allDockWidgets to preserve sub-groups
90
+ MoveDockWidgetAction .move (dock_widget , top_level_widget .getManager ())
91
+ assert not widget_to_remove .getManager ().allDockWidgets (True , True )
92
+
93
+ # find widget's parent:
94
+ for dock_widget in top_level_widget .getManager ().allDockWidgets (True , True ):
95
+ if dockwidget [1 ].widget () == widget_to_remove :
96
+ dockwidget [0 ].removeDockWidget (dockwidget [1 ])
97
+ del dockwidget [1 ]
98
+ # delete widgetToRemove; automatically deleted when dockWidget is deleted
99
+ widget_to_remove = None
100
+ break
101
+
102
+ assert widget_to_remove == None
103
+ else :
104
+ assert False
105
+
106
+ def attachViewMenu (self , menu : QMenu ) -> None :
107
+ menu .aboutToShow .connect (self .autoFillAttachedViewMenu )
108
+
109
+ def autoFillAttachedViewMenu (self ) -> None :
110
+ menu = self .sender ()
111
+
112
+ if menu :
113
+ menu .clear ()
114
+ self .setupViewMenu (menu )
115
+ else :
116
+ assert False
117
+
118
+ def setupViewMenu (self , menu ):
119
+ dock_managers = self .__mgr .allManagers (True , True )
120
+
121
+ has_perspectives_menu = False
122
+ if self .getTopLevelDockWidget () == self :
123
+ has_perspectives_menu = (self .__perspectives_manager != None )
124
+ else :
125
+ assert False
126
+
127
+ organize = menu
128
+ if has_perspectives_menu :
129
+ organize = menu .addMenu ("Organize" )
130
+
131
+ self .setupMenu (organize , dock_managers )
132
+
133
+ if has_perspectives_menu :
134
+ perspectives = menu .addMenu ("Perspectives" )
135
+ self .fillPerspectivesMenu (perspectives )
136
+
137
+ def setupMenu (self , menu : QMenu , move_to : 'list[DockInDockManager]' ) -> None :
138
+ self .__mgr .fillViewMenu (menu , move_to )
139
+ menu .addSeparator ()
140
+ move_menu = menu .addMenu ("Move" )
141
+ self .__mgr .fillMoveMenu (move_menu , move_to )
142
+
143
+ def fillPerspectivesMenu (self , menu : QMenu ):
144
+ menu .addAction ("Create perspective..." , self .createPerspective )
145
+ perspectives_names = []
146
+ if self .__perspectives_manager :
147
+ perspectives_names = self .__perspectives_manager .perspectiveNames ()
148
+
149
+ if perspectives_names :
150
+ load = menu .addMenu ("Load perspective" )
151
+ for name in perspectives_names :
152
+ load .addAction (LoadPerspectiveAction (load , name , self ))
153
+ remove = menu .addMenu ("Remove perspective" )
154
+ for name in perspectives_names :
155
+ remove .addAction (RemovePerspectiveAction (remove , name , self ))
156
+
157
+ def setNewPerspectiveDefaultName (default_name : str ) -> None :
158
+ self .__new_perspective_default_name = default_name
159
+
160
+ def createPerspective (self ) -> None :
161
+ if not self .__perspectives_manager :
162
+ return
163
+
164
+ name = self .__new_perspective_default_name
165
+ if self .__new_perspective_default_name :
166
+ index = 2
167
+ while name in self .__perspectives_manager .perspectiveNames ():
168
+ name = f"{ self .__new_perspective_default_name } ({ index } )"
169
+ index += 1
170
+
171
+ while True :
172
+ name , ok = QInputDialog .getText (None , "Create perspective" , "Enter perspective name" , QLineEdit .Normal , name )
173
+ if ok :
174
+ if not name :
175
+ QMessageBox .critical (None , "Error" , "Perspective name cannot be empty" )
176
+ continue
177
+ elif name in self .__perspectives_manager .perspectiveNames ():
178
+ if QMessageBox .critical (None , "Error" , f"Perspective '{ name } ' already exists, overwrite it?" , QMessageBox .Yes | QMessageBox .No , QMessageBox .No ) == QMessageBox .No :
179
+ continue
180
+
181
+ self .__perspectives_manager .addPerspective (name , self )
182
+ break
183
+ else :
184
+ break
185
+
186
+ def dumpStatus (self , echo : callable = print , widget : QtAds .CDockWidget = None , tab : str = '' , suffix : str = '' ) -> str :
187
+ if widget is not None :
188
+ as_mgr = DockInDockManager .dockInAManager (widget )
189
+ if as_mgr :
190
+ as_mgr .parent ().dumpStatus (tab = tab )
191
+ else :
192
+ echo (tab + widget .objectName () + suffix )
193
+ else :
194
+ echo (tab + "Group: " + self .getManager ().getGroupName ())
195
+ tab += " "
196
+ visible_widgets = set ()
197
+ for widget in self .getManager ().getWidgetsInGUIOrder ():
198
+ visible_widgets .add (widget )
199
+ self .dumpStatus (widget = widget , tab = tab )
200
+
201
+ for closed in self .getManager ().dockWidgetsMap ().values ():
202
+ if not closed in visible_widgets :
203
+ self .dumpStatus (widget = closed , tab = tab , suffix = " (closed)" )
0 commit comments