src/eric7/EricWidgets/EricToolBarManager.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2008 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a toolbar manager class.
8 """
9
10 from PyQt6.QtCore import QObject, QByteArray, QDataStream, QIODevice
11 from PyQt6.QtWidgets import QToolBar
12
13 import Utilities
14
15
16 class EricToolBarManager(QObject):
17 """
18 Class implementing a toolbar manager.
19 """
20 VersionMarker = 0xffff
21 ToolBarMarker = 0xfefe
22 CustomToolBarMarker = 0xfdfd
23
24 def __init__(self, ui=None, parent=None):
25 """
26 Constructor
27
28 @param ui reference to the user interface object (UI.UserInterface)
29 @param parent reference to the parent object (QObject)
30 """
31 super().__init__(parent)
32
33 self.__mainWindow = None
34 self.__ui = ui
35
36 self.__toolBars = {}
37 # maps toolbar IDs to actions
38 self.__toolBarsWithSeparators = {}
39 # maps toolbar IDs to actions incl. separators
40 self.__defaultToolBars = {}
41 # maps default toolbar IDs to actions
42 self.__customToolBars = []
43 # list of custom toolbars
44 self.__allToolBars = {}
45 # maps toolbar IDs to toolbars
46
47 self.__categoryToActions = {}
48 # maps categories to actions
49 self.__actionToCategory = {}
50 # maps action IDs to categories
51 self.__allActions = {}
52 # maps action IDs to actions
53 self.__actionToToolBars = {}
54 # maps action IDs to toolbars
55
56 self.__widgetActions = {}
57 # maps widget action IDs to toolbars
58 self.__allWidgetActions = {}
59 # maps widget action IDs to widget actions
60
61 ######################################################
62 ## Private methods
63 ######################################################
64
65 def __toolBarByName(self, name):
66 """
67 Private slot to get a toolbar by its object name.
68
69 @param name object name of the toolbar (string)
70 @return reference to the toolbar (QToolBar)
71 """
72 for toolBar in list(self.__allToolBars.values()):
73 if toolBar.objectName() == name:
74 return toolBar
75 return None
76
77 def __findAction(self, name):
78 """
79 Private method to find an action by name.
80
81 @param name name of the action to search for (string)
82 @return reference to the action (QAction)
83 """
84 # check objectName() first
85 for action in list(self.__allActions.values()):
86 if action.objectName() == name:
87 return action
88
89 # check text() next
90 for action in list(self.__allActions.values()):
91 if action.text() == name:
92 return action
93
94 return None
95
96 def __findDefaultToolBar(self, name):
97 """
98 Private method to find a default toolbar by name.
99
100 @param name name of the default toolbar to search for (string)
101 @return reference to the default toolbar (QToolBar)
102 """
103 # check objectName() first
104 for tbID in self.__defaultToolBars:
105 tb = self.__allToolBars[tbID]
106 if tb.objectName() == name:
107 return tb
108
109 # check windowTitle() next
110 for tbID in self.__defaultToolBars:
111 tb = self.__allToolBars[tbID]
112 if tb.windowTitle() == name:
113 return tb
114
115 return None
116
117 ######################################################
118 ## Public methods
119 ######################################################
120
121 def setMainWindow(self, mainWindow):
122 """
123 Public method to set the reference to the main window.
124
125 @param mainWindow reference to the main window (QMainWindow)
126 """
127 self.__mainWindow = mainWindow
128
129 def mainWindow(self):
130 """
131 Public method to get the reference to the main window.
132
133 @return reference to the main window (QMainWindow)
134 """
135 return self.__mainWindow
136
137 def addToolBar(self, toolBar, category):
138 """
139 Public method to add a toolbar to be managed.
140
141 @param toolBar reference to the toolbar to be managed (QToolBar)
142 @param category category for the toolbar (string)
143 """
144 if toolBar is None:
145 return
146
147 newActions = []
148 newActionsWithSeparators = []
149 actions = toolBar.actions()
150 for action in actions:
151 actID = id(action)
152 self.addAction(action, category)
153 if actID in self.__widgetActions:
154 self.__widgetActions[actID] = toolBar
155 newActionsWithSeparators.append(action)
156 if action.isSeparator():
157 action = None
158 else:
159 if toolBar not in self.__actionToToolBars[actID]:
160 self.__actionToToolBars[actID].append(toolBar)
161 newActions.append(action)
162 tbID = id(toolBar)
163 self.__defaultToolBars[tbID] = newActions
164 self.__toolBars[tbID] = newActions
165 self.__toolBarsWithSeparators[tbID] = newActionsWithSeparators
166 self.__allToolBars[tbID] = toolBar
167
168 def removeToolBar(self, toolBar):
169 """
170 Public method to remove a toolbar added with addToolBar().
171
172 @param toolBar reference to the toolbar to be removed (QToolBar)
173 """
174 if toolBar is None:
175 return
176
177 tbID = id(toolBar)
178
179 if tbID not in self.__defaultToolBars:
180 return
181
182 defaultActions = self.__defaultToolBars[tbID][:]
183 self.setToolBar(toolBar, [])
184 for action in defaultActions:
185 self.removeAction(action)
186
187 del self.__defaultToolBars[tbID]
188 del self.__toolBars[tbID]
189 del self.__toolBarsWithSeparators[tbID]
190 del self.__allToolBars[tbID]
191
192 for action in defaultActions:
193 if action is None:
194 toolBar.addSeparator()
195 else:
196 toolBar.addAction(action)
197
198 def setToolBars(self, toolBars):
199 """
200 Public method to set the actions of several toolbars.
201
202 @param toolBars dictionary with toolbar id as key and
203 a list of actions as value
204 """
205 for key, actions in list(toolBars.items()):
206 tb = self.__allToolBars[key]
207 self.setToolBar(tb, actions)
208
209 def setToolBar(self, toolBar, actions):
210 """
211 Public method to set the actions of a toolbar.
212
213 @param toolBar reference to the toolbar to configure (QToolBar)
214 @param actions list of actions to be set (list of QAction)
215 """
216 if toolBar is None:
217 return
218
219 tbID = id(toolBar)
220 if tbID not in self.__toolBars:
221 return
222 if self.__toolBars[tbID] == actions:
223 return
224
225 # step 1: check list of actions
226 toRemove = {}
227 newActions = []
228 for action in actions:
229 if (
230 action is None or
231 (action not in newActions and
232 id(action) in self.__allActions)
233 ):
234 newActions.append(action)
235 oldTB = self.toolBarWidgetAction(action)
236 if oldTB is not None and oldTB != toolBar:
237 if id(oldTB) not in toRemove:
238 toRemove[id(oldTB)] = []
239 toRemove[id(oldTB)].append(action)
240 self.removeWidgetActions(toRemove)
241
242 # step 2: remove all toolbar actions
243 for action in self.__toolBarsWithSeparators[tbID]:
244 if self.toolBarWidgetAction(action) == tbID:
245 self.__widgetActions[id(action)] = None
246 toolBar.removeAction(action)
247 if action.isSeparator():
248 del action
249 else:
250 self.__actionToToolBars[id(action)].remove(toolBar)
251
252 # step 3: set the actions as requested
253 newActionsWithSeparators = []
254 for action in newActions:
255 newAction = None
256 if action is None:
257 newAction = toolBar.addSeparator()
258 elif id(action) in self.__allActions:
259 toolBar.addAction(action)
260 newAction = action
261 self.__actionToToolBars[id(action)].append(toolBar)
262 if id(action) in self.__widgetActions:
263 self.__widgetActions[id(action)] = toolBar
264 else:
265 continue
266 newActionsWithSeparators.append(newAction)
267
268 if toolBar.isVisible():
269 toolBar.hide()
270 toolBar.show()
271 self.__toolBars[tbID] = newActions
272 self.__toolBarsWithSeparators[tbID] = newActionsWithSeparators
273
274 def resetToolBar(self, toolBar):
275 """
276 Public method to reset a toolbar to its default state.
277
278 @param toolBar reference to the toolbar to configure (QToolBar)
279 """
280 if not self.isDefaultToolBar():
281 return
282 self.setToolBar(toolBar, self.__defaultToolBars[id(toolBar)])
283
284 def resetAllToolBars(self):
285 """
286 Public method to reset all toolbars to their default state.
287 """
288 self.setToolBars(self.__defaultToolBars)
289 for toolBar in self.__customToolBars[:]:
290 self.deleteToolBar(toolBar)
291
292 def defaultToolBars(self):
293 """
294 Public method to get all toolbars added with addToolBar().
295
296 @return list of all default toolbars (list of QToolBar)
297 """
298 return list(self.__defaultToolBars.values())
299
300 def isDefaultToolBar(self, toolBar):
301 """
302 Public method to check, if a toolbar was added with addToolBar().
303
304 @param toolBar reference to the toolbar to be checked (QToolBar)
305 @return flag indicating an added toolbar (boolean)
306 """
307 return (
308 toolBar is not None and
309 id(toolBar) in self.__defaultToolBars
310 )
311
312 def createToolBar(self, title, name=""):
313 """
314 Public method to create a custom toolbar.
315
316 @param title title to be used for the toolbar (string)
317 @param name optional name for the new toolbar (string)
318 @return reference to the created toolbar (QToolBar)
319 """
320 if self.__mainWindow is None:
321 return None
322
323 toolBar = QToolBar(title, self.__mainWindow)
324 toolBar.setToolTip(title)
325 if not name:
326 index = 1
327 customPrefix = "__CustomPrefix__"
328 name = "{0}{1:d}".format(customPrefix, index)
329 while self.__toolBarByName(name) is not None:
330 index += 1
331 name = "{0}{1:d}".format(customPrefix, index)
332 toolBar.setObjectName(name)
333 self.__mainWindow.addToolBar(toolBar)
334
335 tbID = id(toolBar)
336 self.__customToolBars.append(toolBar)
337 self.__allToolBars[tbID] = toolBar
338 self.__toolBars[tbID] = []
339 self.__toolBarsWithSeparators[tbID] = []
340
341 if self.__ui is not None:
342 toolBar.setIconSize(self.__ui.getToolBarIconSize())
343 self.__ui.registerToolbar(name, title, toolBar)
344
345 return toolBar
346
347 def deleteToolBar(self, toolBar):
348 """
349 Public method to remove a custom toolbar created with createToolBar().
350
351 @param toolBar reference to the toolbar to be managed (QToolBar)
352 """
353 if toolBar is None:
354 return
355
356 tbID = id(toolBar)
357 if tbID not in self.__toolBars:
358 return
359 if tbID in self.__defaultToolBars:
360 return
361
362 if self.__ui is not None:
363 self.__ui.unregisterToolbar(toolBar.objectName())
364
365 self.setToolBar(toolBar, [])
366
367 del self.__allToolBars[tbID]
368 del self.__toolBars[tbID]
369 del self.__toolBarsWithSeparators[tbID]
370 self.__customToolBars.remove(toolBar)
371 self.__mainWindow.removeToolBar(toolBar)
372 del toolBar
373
374 def renameToolBar(self, toolBar, title):
375 """
376 Public method to give a toolbar a new title.
377
378 @param toolBar reference to the toolbar to be managed (QToolBar)
379 @param title title to be used for the toolbar (string)
380 """
381 if toolBar is None:
382 return
383
384 toolBar.setWindowTitle(title)
385
386 if self.__ui is not None:
387 self.__ui.reregisterToolbar(toolBar.objectName(), title)
388
389 def toolBars(self):
390 """
391 Public method to get all toolbars.
392
393 @return list of all toolbars (list of QToolBar)
394 """
395 return list(self.__allToolBars.values())
396
397 def addAction(self, action, category):
398 """
399 Public method to add an action to be managed.
400
401 @param action reference to the action to be managed (QAction)
402 @param category category for the toolbar (string)
403 """
404 if action is None:
405 return
406 if action.isSeparator():
407 return
408 if id(action) in self.__allActions:
409 return
410
411 if action.metaObject().className() == "QWidgetAction":
412 self.__widgetActions[id(action)] = None
413 self.__allWidgetActions[id(action)] = action
414 self.__allActions[id(action)] = action
415 if category not in self.__categoryToActions:
416 self.__categoryToActions[category] = []
417 self.__categoryToActions[category].append(action)
418 self.__actionToCategory[id(action)] = category
419 self.__actionToToolBars[id(action)] = []
420
421 def addActions(self, actions, category):
422 """
423 Public method to add actions to be managed.
424
425 @param actions list of actions to be managed
426 @type list of QAction
427 @param category category for the toolbar
428 @type str
429 """
430 for action in actions:
431 self.addAction(action, category)
432
433 def removeAction(self, action):
434 """
435 Public method to remove an action from the manager.
436
437 @param action reference to the action to be removed (QAction)
438 """
439 aID = id(action)
440
441 if aID not in self.__allActions:
442 return
443
444 toolBars = self.__actionToToolBars[aID]
445 for toolBar in toolBars:
446 tbID = id(toolBar)
447 self.__toolBars[tbID].remove(action)
448 self.__toolBarsWithSeparators[tbID].remove(action)
449 toolBar.removeAction(action)
450 if toolBar.isVisible():
451 toolBar.hide()
452 toolBar.show()
453
454 for tbID in self.__defaultToolBars:
455 if action in self.__defaultToolBars[tbID]:
456 self.__defaultToolBars[tbID].remove(action)
457
458 del self.__allActions[aID]
459 if aID in self.__widgetActions:
460 del self.__widgetActions[aID]
461 del self.__allWidgetActions[aID]
462 del self.__actionToCategory[aID]
463 del self.__actionToToolBars[aID]
464
465 for category in self.__categoryToActions:
466 if action in self.__categoryToActions[category]:
467 self.__categoryToActions[category].remove(action)
468
469 def removeCategoryActions(self, category):
470 """
471 Public method to remove the actions belonging to a category.
472
473 @param category category for the actions (string)
474 """
475 for action in self.categoryActions(category):
476 self.removeAction(action)
477
478 def saveState(self, version=0):
479 """
480 Public method to save the state of the toolbar manager.
481
482 @param version version number stored with the data (integer)
483 @return saved state as a byte array (QByteArray)
484 """
485 data = QByteArray()
486 stream = QDataStream(data, QIODevice.OpenModeFlag.WriteOnly)
487 stream.setVersion(QDataStream.Version.Qt_4_6)
488 stream.writeUInt16(EricToolBarManager.VersionMarker)
489 stream.writeUInt16(version)
490
491 # save default toolbars
492 stream.writeUInt16(EricToolBarManager.ToolBarMarker)
493 stream.writeUInt16(len(self.__defaultToolBars))
494 for tbID in self.__defaultToolBars:
495 tb = self.__allToolBars[tbID]
496 if tb.objectName():
497 stream.writeString(tb.objectName().encode("utf-8"))
498 else:
499 stream.writeString(tb.windowTitle().encode("utf-8"))
500 stream.writeUInt16(len(self.__toolBars[tbID]))
501 for action in self.__toolBars[tbID]:
502 if action is not None:
503 if action.objectName():
504 stream.writeString(action.objectName().encode("utf-8"))
505 else:
506 stream.writeString(action.text().encode("utf-8"))
507 else:
508 stream.writeString("".encode("utf-8"))
509
510 # save the custom toolbars
511 stream.writeUInt16(EricToolBarManager.CustomToolBarMarker)
512 stream.writeUInt16(len(self.__toolBars) - len(self.__defaultToolBars))
513 for tbID in self.__toolBars:
514 if tbID not in self.__defaultToolBars:
515 tb = self.__allToolBars[tbID]
516 stream.writeString(tb.objectName().encode("utf-8"))
517 stream.writeString(tb.windowTitle().encode("utf-8"))
518 stream.writeUInt16(len(self.__toolBars[tbID]))
519 for action in self.__toolBars[tbID]:
520 if action is not None:
521 if action.objectName():
522 stream.writeString(action.objectName()
523 .encode("utf-8"))
524 else:
525 stream.writeString(action.text().encode("utf-8"))
526 else:
527 stream.writeString("".encode("utf-8"))
528
529 return data
530
531 def restoreState(self, state, version=0):
532 """
533 Public method to restore the state of the toolbar manager.
534
535 @param state byte array containing the saved state (QByteArray)
536 @param version version number stored with the data (integer)
537 @return flag indicating success (boolean)
538 """
539 if state.isEmpty():
540 return False
541
542 data = QByteArray(state)
543 stream = QDataStream(data, QIODevice.OpenModeFlag.ReadOnly)
544 stream.setVersion(QDataStream.Version.Qt_4_6)
545 marker = stream.readUInt16()
546 vers = stream.readUInt16()
547 if marker != EricToolBarManager.VersionMarker or vers != version:
548 return False
549
550 tmarker = stream.readUInt16()
551 if tmarker != EricToolBarManager.ToolBarMarker:
552 return False
553
554 toolBarCount = stream.readUInt16()
555 for _i in range(toolBarCount):
556 objectName = Utilities.readStringFromStream(stream)
557 actionCount = stream.readUInt16()
558 actions = []
559 for _j in range(actionCount):
560 actionName = Utilities.readStringFromStream(stream)
561 if actionName:
562 action = self.__findAction(actionName)
563 if action is not None:
564 actions.append(action)
565 else:
566 actions.append(None)
567 toolBar = self.__findDefaultToolBar(objectName)
568 if toolBar is not None:
569 self.setToolBar(toolBar, actions)
570
571 cmarker = stream.readUInt16()
572 if cmarker != EricToolBarManager.CustomToolBarMarker:
573 return False
574
575 oldCustomToolBars = self.__customToolBars[:]
576
577 toolBarCount = stream.readUInt16()
578 for _i in range(toolBarCount):
579 objectName = Utilities.readStringFromStream(stream)
580 toolBarTitle = Utilities.readStringFromStream(stream)
581 actionCount = stream.readUInt16()
582 actions = []
583 for _j in range(actionCount):
584 actionName = Utilities.readStringFromStream(stream)
585 if actionName:
586 action = self.__findAction(actionName)
587 if action is not None:
588 actions.append(action)
589 else:
590 actions.append(None)
591 toolBar = self.__toolBarByName(objectName)
592 if toolBar is not None:
593 toolBar.setWindowTitle(toolBarTitle)
594 oldCustomToolBars.remove(toolBar)
595 else:
596 toolBar = self.createToolBar(toolBarTitle, objectName)
597 if toolBar is not None:
598 toolBar.setObjectName(objectName)
599 self.setToolBar(toolBar, actions)
600
601 for tb in oldCustomToolBars:
602 self.deleteToolBar(tb)
603
604 return True
605
606 def toolBarWidgetAction(self, action):
607 """
608 Public method to get the toolbar for a widget action.
609
610 @param action widget action to check for (QAction)
611 @return reference to the toolbar containing action (QToolBar)
612 """
613 aID = id(action)
614 if aID in self.__widgetActions:
615 return self.__widgetActions[aID]
616 return None
617
618 def removeWidgetActions(self, actions):
619 """
620 Public method to remove widget actions.
621
622 @param actions dictionary with toolbar id as key and
623 a list of widget actions as value
624 """
625 for tbID in list(actions.keys())[:]:
626 toolBar = self.__allToolBars[tbID]
627 newActions = self.__toolBars[tbID][:]
628 newActionsWithSeparators = self.__toolBarsWithSeparators[tbID][:]
629
630 removedActions = []
631 for action in actions[tbID]:
632 if (
633 action in newActions and
634 self.toolBarWidgetAction(action) == toolBar
635 ):
636 newActions.remove(action)
637 newActionsWithSeparators.remove(action)
638 removedActions.append(action)
639
640 self.__toolBars[tbID] = newActions
641 self.__toolBarsWithSeparators[tbID] = newActionsWithSeparators
642
643 for action in removedActions:
644 self.__widgetActions[id(action)] = None
645 self.__actionToToolBars[id(action)].remove(toolBar)
646 toolBar.removeAction(action)
647
648 def isWidgetAction(self, action):
649 """
650 Public method to check, if action is a widget action.
651
652 @param action reference to the action to be checked (QAction)
653 @return flag indicating a widget action (boolean)
654 """
655 return id(action) in self.__allWidgetActions
656
657 def categories(self):
658 """
659 Public method to get the list of categories.
660
661 @return list of categories (list of string)
662 """
663 return list(self.__categoryToActions.keys())
664
665 def categoryActions(self, category):
666 """
667 Public method to get the actions belonging to a category.
668
669 @param category category for the actions (string)
670 @return list of actions (list of QAction)
671 """
672 if category not in self.__categoryToActions:
673 return []
674
675 return self.__categoryToActions[category][:]
676
677 def actionById(self, aID):
678 """
679 Public method to get an action given its id.
680
681 @param aID id of the action object (integer)
682 @return reference to the action (QAction)
683 """
684 if aID not in self.__allActions:
685 return None
686 return self.__allActions[aID]
687
688 def toolBarById(self, tbID):
689 """
690 Public method to get a toolbar given its id.
691
692 @param tbID id of the toolbar object (integer)
693 @return reference to the toolbar (QToolBar)
694 """
695 if tbID not in self.__allToolBars:
696 return None
697 return self.__allToolBars[tbID]
698
699 def toolBarActions(self, tbID):
700 """
701 Public method to get a toolbar's actions given its id.
702
703 @param tbID id of the toolbar object (integer)
704 @return list of actions (list of QAction)
705 """
706 if tbID not in self.__toolBars:
707 return []
708 return self.__toolBars[tbID][:]
709
710 def toolBarsActions(self):
711 """
712 Public method to get all toolbars and their actions.
713
714 @return reference to dictionary of toolbar IDs as key and list
715 of actions as values
716 """
717 return self.__toolBars
718
719 def defaultToolBarActions(self, tbID):
720 """
721 Public method to get a default toolbar's actions given its id.
722
723 @param tbID id of the default toolbar object (integer)
724 @return list of actions (list of QAction)
725 """
726 if tbID not in self.__defaultToolBars:
727 return []
728 return self.__defaultToolBars[tbID][:]

eric ide

mercurial