eric6/E5Gui/E5ToolBarManager.py

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

eric ide

mercurial