eric7/Plugins/ViewManagerPlugins/Tabview/Tabview.py

branch
eric7
changeset 8312
800c432b34c8
parent 8259
2bbec88047dd
child 8314
e3642a6a1e71
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a tabbed viewmanager class.
8 """
9
10 import os
11
12 from PyQt5.QtCore import (
13 pyqtSlot, QPoint, QFileInfo, pyqtSignal, QEvent, QByteArray, QMimeData,
14 Qt, QSize
15 )
16 from PyQt5.QtGui import QColor, QDrag, QPixmap, QMouseEvent
17 from PyQt5.QtWidgets import (
18 QWidget, QHBoxLayout, QSplitter, QTabBar, QApplication, QToolButton,
19 QMenu, QLabel
20 )
21
22 from E5Gui.E5Application import e5App
23
24 from ViewManager.ViewManager import ViewManager
25
26 import QScintilla.Editor
27 from QScintilla.Editor import Editor
28 from QScintilla.EditorAssembly import EditorAssembly
29
30 import UI.PixmapCache
31
32 from E5Gui.E5TabWidget import E5TabWidget, E5WheelTabBar
33 from E5Gui.E5Led import E5Led
34
35 import Preferences
36 from Globals import isMacPlatform
37
38 from eric6config import getConfig
39
40
41 class TabBar(E5WheelTabBar):
42 """
43 Class implementing a customized tab bar supporting drag & drop.
44
45 @signal tabMoveRequested(int, int) emitted to signal a tab move request
46 giving the old and new index position
47 @signal tabRelocateRequested(str, int, int) emitted to signal a tab
48 relocation request giving the string encoded id of the old tab widget,
49 the index in the old tab widget and the new index position
50 @signal tabCopyRequested(str, int, int) emitted to signal a clone request
51 giving the string encoded id of the source tab widget, the index in the
52 source tab widget and the new index position
53 @signal tabCopyRequested(int, int) emitted to signal a clone request giving
54 the old and new index position
55 """
56 tabMoveRequested = pyqtSignal(int, int)
57 tabRelocateRequested = pyqtSignal(str, int, int)
58 tabCopyRequested = pyqtSignal((str, int, int), (int, int))
59
60 def __init__(self, parent=None):
61 """
62 Constructor
63
64 @param parent reference to the parent widget
65 @type QWidget
66 """
67 super().__init__(parent)
68 self.setAcceptDrops(True)
69
70 self.__dragStartPos = QPoint()
71
72 def mousePressEvent(self, event):
73 """
74 Protected method to handle mouse press events.
75
76 @param event reference to the mouse press event
77 @type QMouseEvent
78 """
79 if event.button() == Qt.MouseButton.LeftButton:
80 self.__dragStartPos = QPoint(event.pos())
81 super().mousePressEvent(event)
82
83 def mouseMoveEvent(self, event):
84 """
85 Protected method to handle mouse move events.
86
87 @param event reference to the mouse move event
88 @type QMouseEvent
89 """
90 if (
91 event.buttons() == Qt.MouseButtons(Qt.MouseButton.LeftButton) and
92 (event.pos() - self.__dragStartPos).manhattanLength() >
93 QApplication.startDragDistance()
94 ):
95 drag = QDrag(self)
96 mimeData = QMimeData()
97 index = self.tabAt(event.pos())
98 mimeData.setText(self.tabText(index))
99 mimeData.setData("action", b"tab-reordering")
100 mimeData.setData("tabbar-id", str(id(self)).encode("utf-8"))
101 mimeData.setData(
102 "source-index",
103 QByteArray.number(self.tabAt(self.__dragStartPos)))
104 mimeData.setData(
105 "tabwidget-id",
106 str(id(self.parentWidget())).encode("utf-8"))
107 drag.setMimeData(mimeData)
108 if event.modifiers() == Qt.KeyboardModifiers(
109 Qt.KeyboardModifier.ShiftModifier
110 ):
111 drag.exec(Qt.DropActions(Qt.DropAction.CopyAction))
112 elif event.modifiers() == Qt.KeyboardModifiers(
113 Qt.KeyboardModifier.NoModifier
114 ):
115 drag.exec(Qt.DropActions(Qt.DropAction.MoveAction))
116 super().mouseMoveEvent(event)
117
118 def dragEnterEvent(self, event):
119 """
120 Protected method to handle drag enter events.
121
122 @param event reference to the drag enter event
123 @type QDragEnterEvent
124 """
125 mimeData = event.mimeData()
126 formats = mimeData.formats()
127 if (
128 "action" in formats and
129 mimeData.data("action") == b"tab-reordering" and
130 "tabbar-id" in formats and
131 "source-index" in formats and
132 "tabwidget-id" in formats
133 ):
134 event.acceptProposedAction()
135 super().dragEnterEvent(event)
136
137 def dropEvent(self, event):
138 """
139 Protected method to handle drop events.
140
141 @param event reference to the drop event
142 @type QDropEvent
143 """
144 mimeData = event.mimeData()
145 oldID = int(mimeData.data("tabbar-id"))
146 fromIndex = int(mimeData.data("source-index"))
147 toIndex = self.tabAt(event.pos())
148 if oldID != id(self):
149 parentID = int(mimeData.data("tabwidget-id"))
150 if event.proposedAction() == Qt.DropAction.MoveAction:
151 self.tabRelocateRequested.emit(
152 str(parentID), fromIndex, toIndex)
153 event.acceptProposedAction()
154 elif event.proposedAction() == Qt.DropAction.CopyAction:
155 self.tabCopyRequested[str, int, int].emit(
156 str(parentID), fromIndex, toIndex)
157 event.acceptProposedAction()
158 else:
159 if fromIndex != toIndex:
160 if event.proposedAction() == Qt.DropAction.MoveAction:
161 self.tabMoveRequested.emit(fromIndex, toIndex)
162 event.acceptProposedAction()
163 elif event.proposedAction() == Qt.DropAction.CopyAction:
164 self.tabCopyRequested[int, int].emit(fromIndex, toIndex)
165 event.acceptProposedAction()
166 super().dropEvent(event)
167
168
169 class TabWidget(E5TabWidget):
170 """
171 Class implementing a custimized tab widget.
172 """
173 def __init__(self, vm):
174 """
175 Constructor
176
177 @param vm view manager widget
178 @type Tabview
179 """
180 super().__init__()
181
182 self.__tabBar = TabBar(self)
183 self.setTabBar(self.__tabBar)
184 iconSize = self.__tabBar.iconSize()
185 self.__tabBar.setIconSize(
186 QSize(2 * iconSize.width(), iconSize.height()))
187
188 self.setUsesScrollButtons(True)
189 self.setElideMode(Qt.TextElideMode.ElideNone)
190 if isMacPlatform():
191 self.setDocumentMode(True)
192
193 self.__tabBar.tabMoveRequested.connect(self.moveTab)
194 self.__tabBar.tabRelocateRequested.connect(self.__relocateTab)
195 self.__tabBar.tabCopyRequested[str, int, int].connect(
196 self.__copyTabOther)
197 self.__tabBar.tabCopyRequested[int, int].connect(self.__copyTab)
198
199 self.vm = vm
200 self.editors = []
201
202 self.indicator = E5Led(self)
203 self.setCornerWidget(self.indicator, Qt.Corner.TopLeftCorner)
204
205 self.rightCornerWidget = QWidget(self)
206 self.rightCornerWidgetLayout = QHBoxLayout(self.rightCornerWidget)
207 self.rightCornerWidgetLayout.setContentsMargins(0, 0, 0, 0)
208 self.rightCornerWidgetLayout.setSpacing(0)
209
210 self.__navigationMenu = QMenu(self)
211 self.__navigationMenu.aboutToShow.connect(self.__showNavigationMenu)
212 self.__navigationMenu.triggered.connect(self.__navigationMenuTriggered)
213
214 self.navigationButton = QToolButton(self)
215 self.navigationButton.setIcon(UI.PixmapCache.getIcon("1downarrow"))
216 self.navigationButton.setToolTip(self.tr("Show a navigation menu"))
217 self.navigationButton.setPopupMode(
218 QToolButton.ToolButtonPopupMode.InstantPopup)
219 self.navigationButton.setMenu(self.__navigationMenu)
220 self.navigationButton.setEnabled(False)
221 self.rightCornerWidgetLayout.addWidget(self.navigationButton)
222
223 self.tabCloseRequested.connect(self.__closeRequested)
224
225 self.setCornerWidget(self.rightCornerWidget, Qt.Corner.TopRightCorner)
226
227 self.__initMenu()
228 self.contextMenuEditor = None
229 self.contextMenuIndex = -1
230
231 self.setTabContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
232 self.customTabContextMenuRequested.connect(self.__showContextMenu)
233
234 ericPic = QPixmap(
235 os.path.join(getConfig('ericPixDir'), 'eric_small.png'))
236 self.emptyLabel = QLabel()
237 self.emptyLabel.setPixmap(ericPic)
238 self.emptyLabel.setAlignment(
239 Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignHCenter)
240 super().addTab(
241 self.emptyLabel,
242 UI.PixmapCache.getIcon("empty"), "")
243
244 def __initMenu(self):
245 """
246 Private method to initialize the tab context menu.
247 """
248 self.__menu = QMenu(self)
249 self.leftMenuAct = self.__menu.addAction(
250 UI.PixmapCache.getIcon("1leftarrow"),
251 self.tr('Move Left'), self.__contextMenuMoveLeft)
252 self.rightMenuAct = self.__menu.addAction(
253 UI.PixmapCache.getIcon("1rightarrow"),
254 self.tr('Move Right'), self.__contextMenuMoveRight)
255 self.firstMenuAct = self.__menu.addAction(
256 UI.PixmapCache.getIcon("2leftarrow"),
257 self.tr('Move First'), self.__contextMenuMoveFirst)
258 self.lastMenuAct = self.__menu.addAction(
259 UI.PixmapCache.getIcon("2rightarrow"),
260 self.tr('Move Last'), self.__contextMenuMoveLast)
261 self.__menu.addSeparator()
262 self.__menu.addAction(
263 UI.PixmapCache.getIcon("tabClose"),
264 self.tr('Close'), self.__contextMenuClose)
265 self.closeOthersMenuAct = self.__menu.addAction(
266 UI.PixmapCache.getIcon("tabCloseOther"),
267 self.tr("Close Others"), self.__contextMenuCloseOthers)
268 self.__menu.addAction(
269 self.tr('Close All'), self.__contextMenuCloseAll)
270 self.__menu.addSeparator()
271 self.saveMenuAct = self.__menu.addAction(
272 UI.PixmapCache.getIcon("fileSave"),
273 self.tr('Save'), self.__contextMenuSave)
274 self.__menu.addAction(
275 UI.PixmapCache.getIcon("fileSaveAs"),
276 self.tr('Save As...'), self.__contextMenuSaveAs)
277 self.__menu.addAction(
278 UI.PixmapCache.getIcon("fileSaveAll"),
279 self.tr('Save All'), self.__contextMenuSaveAll)
280 self.__menu.addSeparator()
281 self.openRejectionsMenuAct = self.__menu.addAction(
282 self.tr("Open 'rejection' file"),
283 self.__contextMenuOpenRejections)
284 self.__menu.addSeparator()
285 self.__menu.addAction(
286 UI.PixmapCache.getIcon("print"),
287 self.tr('Print'), self.__contextMenuPrintFile)
288 self.__menu.addSeparator()
289 self.copyPathAct = self.__menu.addAction(
290 self.tr("Copy Path to Clipboard"),
291 self.__contextMenuCopyPathToClipboard)
292
293 def __showContextMenu(self, coord, index):
294 """
295 Private slot to show the tab context menu.
296
297 @param coord the position of the mouse pointer
298 @type QPoint
299 @param index index of the tab the menu is requested for
300 @type int
301 """
302 if self.editors:
303 widget = self.widget(index)
304 if widget is not None:
305 self.contextMenuEditor = widget.getEditor()
306 if self.contextMenuEditor:
307 self.saveMenuAct.setEnabled(
308 self.contextMenuEditor.isModified())
309 fileName = self.contextMenuEditor.getFileName()
310 self.copyPathAct.setEnabled(bool(fileName))
311 if fileName:
312 rej = "{0}.rej".format(fileName)
313 self.openRejectionsMenuAct.setEnabled(
314 os.path.exists(rej))
315 else:
316 self.openRejectionsMenuAct.setEnabled(False)
317
318 self.contextMenuIndex = index
319 self.leftMenuAct.setEnabled(index > 0)
320 self.rightMenuAct.setEnabled(index < self.count() - 1)
321 self.firstMenuAct.setEnabled(index > 0)
322 self.lastMenuAct.setEnabled(index < self.count() - 1)
323
324 self.closeOthersMenuAct.setEnabled(self.count() > 1)
325
326 coord = self.mapToGlobal(coord)
327 self.__menu.popup(coord)
328
329 def __showNavigationMenu(self):
330 """
331 Private slot to show the navigation button menu.
332 """
333 self.__navigationMenu.clear()
334 for index in range(self.count()):
335 act = self.__navigationMenu.addAction(self.tabIcon(index),
336 self.tabText(index))
337 act.setData(index)
338
339 def __navigationMenuTriggered(self, act):
340 """
341 Private slot called to handle the navigation button menu selection.
342
343 @param act reference to the selected action
344 @type QAction
345 """
346 index = act.data()
347 if index is not None:
348 self.setCurrentIndex(index)
349
350 def showIndicator(self, on):
351 """
352 Public slot to set the indicator on or off.
353
354 @param on flag indicating the state of the indicator
355 @type bool
356 """
357 if on:
358 self.indicator.setColor(QColor("green"))
359 else:
360 self.indicator.setColor(QColor("red"))
361
362 def addTab(self, assembly, title):
363 """
364 Public method to add a new tab.
365
366 @param assembly editor assembly object to be added
367 @type QScintilla.EditorAssembly.EditorAssembly
368 @param title title for the new tab
369 @type str
370 """
371 editor = assembly.getEditor()
372 super().addTab(
373 assembly, UI.PixmapCache.getIcon("empty"), title)
374 self.setTabsClosable(True)
375 self.navigationButton.setEnabled(True)
376
377 if editor not in self.editors:
378 self.editors.append(editor)
379 editor.captionChanged.connect(self.__captionChange)
380 editor.cursorLineChanged.connect(
381 lambda lineno: self.__cursorLineChanged(lineno, editor))
382
383 emptyIndex = self.indexOf(self.emptyLabel)
384 if emptyIndex > -1:
385 self.removeTab(emptyIndex)
386
387 def insertWidget(self, index, assembly, title):
388 """
389 Public method to insert a new tab.
390
391 @param index index position for the new tab
392 @type int
393 @param assembly editor assembly object to be added
394 @type QScintilla.EditorAssembly.EditorAssembly
395 @param title title for the new tab
396 @type str
397 @return index of the inserted tab
398 @rtype int
399 """
400 editor = assembly.getEditor()
401 newIndex = super().insertTab(
402 index, assembly,
403 UI.PixmapCache.getIcon("empty"),
404 title)
405 self.setTabsClosable(True)
406 self.navigationButton.setEnabled(True)
407
408 if editor not in self.editors:
409 self.editors.append(editor)
410 editor.captionChanged.connect(self.__captionChange)
411 editor.cursorLineChanged.connect(
412 lambda lineno: self.__cursorLineChanged(lineno, editor))
413 emptyIndex = self.indexOf(self.emptyLabel)
414 if emptyIndex > -1:
415 self.removeTab(emptyIndex)
416
417 return newIndex
418
419 def __captionChange(self, cap, editor):
420 """
421 Private slot to handle Caption change signals from the editor.
422
423 Updates the tab text and tooltip text to reflect the new caption
424 information.
425
426 @param cap Caption for the editor
427 @type str
428 @param editor Editor to update the caption for
429 @type Editor
430 """
431 fn = editor.getFileName()
432 if fn:
433 if Preferences.getUI("TabViewManagerFilenameOnly"):
434 txt = os.path.basename(fn)
435 else:
436 txt = e5App().getObject("Project").getRelativePath(fn)
437
438 maxFileNameChars = Preferences.getUI(
439 "TabViewManagerFilenameLength")
440 if len(txt) > maxFileNameChars:
441 txt = "...{0}".format(txt[-maxFileNameChars:])
442 if editor.isReadOnly():
443 txt = self.tr("{0} (ro)").format(txt)
444
445 assembly = editor.parent()
446 index = self.indexOf(assembly)
447 if index > -1:
448 self.setTabText(index, txt)
449 self.setTabToolTip(index, fn)
450
451 def __cursorLineChanged(self, lineno, editor):
452 """
453 Private slot to handle a change of the current editor's cursor line.
454
455 @param lineno line number of the editor's cursor (zero based)
456 @type int
457 @param editor reference to the editor
458 @type Editor
459 """
460 if editor and isinstance(editor, QScintilla.Editor.Editor):
461 fn = editor.getFileName()
462 if fn:
463 self.vm.editorLineChanged.emit(fn, lineno + 1)
464 self.vm.editorLineChangedEd.emit(editor, lineno + 1)
465
466 def removeWidget(self, widget):
467 """
468 Public method to remove a widget.
469
470 @param widget widget to be removed
471 @type QWidget
472 """
473 if isinstance(widget, QScintilla.Editor.Editor):
474 widget.cursorLineChanged.disconnect()
475 widget.captionChanged.disconnect()
476 self.editors.remove(widget)
477 index = self.indexOf(widget.parent())
478 else:
479 index = self.indexOf(widget)
480 if index > -1:
481 self.removeTab(index)
482
483 if not self.editors:
484 super().addTab(
485 self.emptyLabel, UI.PixmapCache.getIcon("empty"), "")
486 self.emptyLabel.show()
487 self.setTabsClosable(False)
488 self.navigationButton.setEnabled(False)
489
490 def __relocateTab(self, sourceId, sourceIndex, targetIndex):
491 """
492 Private method to relocate an editor from another TabWidget.
493
494 @param sourceId id of the TabWidget to get the editor from
495 @type str
496 @param sourceIndex index of the tab in the old tab widget
497 @type int
498 @param targetIndex index position to place it to
499 @type int
500 """
501 tw = self.vm.getTabWidgetById(int(sourceId))
502 if tw is not None:
503 # step 1: get data of the tab of the source
504 toolTip = tw.tabToolTip(sourceIndex)
505 text = tw.tabText(sourceIndex)
506 icon = tw.tabIcon(sourceIndex)
507 whatsThis = tw.tabWhatsThis(sourceIndex)
508 assembly = tw.widget(sourceIndex)
509
510 # step 2: relocate the tab
511 tw.removeWidget(assembly.getEditor())
512 self.insertWidget(targetIndex, assembly, text)
513
514 # step 3: set the tab data again
515 self.setTabIcon(targetIndex, icon)
516 self.setTabToolTip(targetIndex, toolTip)
517 self.setTabWhatsThis(targetIndex, whatsThis)
518
519 # step 4: set current widget
520 self.setCurrentIndex(targetIndex)
521
522 def __copyTabOther(self, sourceId, sourceIndex, targetIndex):
523 """
524 Private method to copy an editor from another TabWidget.
525
526 @param sourceId id of the TabWidget to get the editor from
527 @type str
528 @param sourceIndex index of the tab in the old tab widget
529 @type int
530 @param targetIndex index position to place it to
531 @type int
532 """
533 tw = self.vm.getTabWidgetById(int(sourceId))
534 if tw is not None:
535 editor = tw.widget(sourceIndex).getEditor()
536 newEditor, assembly = self.vm.cloneEditor(
537 editor, editor.getFileType(), editor.getFileName())
538 self.vm.insertView(assembly, self, targetIndex,
539 editor.getFileName(), editor.getNoName())
540
541 def __copyTab(self, sourceIndex, targetIndex):
542 """
543 Private method to copy an editor.
544
545 @param sourceIndex index of the tab
546 @type int
547 @param targetIndex index position to place it to
548 @type int
549 """
550 editor = self.widget(sourceIndex).getEditor()
551 newEditor, assembly = self.vm.cloneEditor(
552 editor, editor.getFileType(), editor.getFileName())
553 self.vm.insertView(assembly, self, targetIndex,
554 editor.getFileName(), editor.getNoName())
555
556 def currentWidget(self):
557 """
558 Public method to return a reference to the current page.
559
560 @return reference to the current page
561 @rtype Editor
562 """
563 if not self.editors:
564 return None
565 else:
566 return super().currentWidget()
567
568 def setCurrentWidget(self, assembly):
569 """
570 Public method to set the current tab by the given editor assembly.
571
572 @param assembly editor assembly to determine current tab from
573 @type EditorAssembly.EditorAssembly
574 """
575 super().setCurrentWidget(assembly)
576
577 def indexOf(self, widget):
578 """
579 Public method to get the tab index of the given editor.
580
581 @param widget widget to get the index for
582 @type QLabel or Editor
583 @return tab index of the editor
584 @rtype int
585 """
586 if isinstance(widget, QScintilla.Editor.Editor):
587 widget = widget.parent()
588 return super().indexOf(widget)
589
590 def hasEditor(self, editor):
591 """
592 Public method to check for an editor.
593
594 @param editor editor object to check for
595 @type Editor
596 @return flag indicating, whether the editor to be checked belongs
597 to the list of editors managed by this tab widget.
598 @rtype bool
599 """
600 return editor in self.editors
601
602 def hasEditors(self):
603 """
604 Public method to test, if any editor is managed.
605
606 @return flag indicating editors are managed
607 @rtype bool
608 """
609 return len(self.editors) > 0
610
611 def __contextMenuClose(self):
612 """
613 Private method to close the selected tab.
614 """
615 if self.contextMenuEditor:
616 self.vm.closeEditorWindow(self.contextMenuEditor)
617
618 def __contextMenuCloseOthers(self):
619 """
620 Private method to close the other tabs.
621 """
622 index = self.contextMenuIndex
623 for i in (
624 list(range(self.count() - 1, index, -1)) +
625 list(range(index - 1, -1, -1))
626 ):
627 editor = self.widget(i).getEditor()
628 self.vm.closeEditorWindow(editor)
629
630 def __contextMenuCloseAll(self):
631 """
632 Private method to close all tabs.
633 """
634 savedEditors = self.editors[:]
635 for editor in savedEditors:
636 self.vm.closeEditorWindow(editor)
637
638 def __contextMenuSave(self):
639 """
640 Private method to save the selected tab.
641 """
642 if self.contextMenuEditor:
643 self.vm.saveEditorEd(self.contextMenuEditor)
644
645 def __contextMenuSaveAs(self):
646 """
647 Private method to save the selected tab to a new file.
648 """
649 if self.contextMenuEditor:
650 self.vm.saveAsEditorEd(self.contextMenuEditor)
651
652 def __contextMenuSaveAll(self):
653 """
654 Private method to save all tabs.
655 """
656 self.vm.saveEditorsList(self.editors)
657
658 def __contextMenuOpenRejections(self):
659 """
660 Private slot to open a rejections file associated with the selected
661 tab.
662 """
663 if self.contextMenuEditor:
664 fileName = self.contextMenuEditor.getFileName()
665 if fileName:
666 rej = "{0}.rej".format(fileName)
667 if os.path.exists(rej):
668 self.vm.openSourceFile(rej)
669
670 def __contextMenuPrintFile(self):
671 """
672 Private method to print the selected tab.
673 """
674 if self.contextMenuEditor:
675 self.vm.printEditor(self.contextMenuEditor)
676
677 def __contextMenuCopyPathToClipboard(self):
678 """
679 Private method to copy the file name of the selected tab to the
680 clipboard.
681 """
682 if self.contextMenuEditor:
683 fn = self.contextMenuEditor.getFileName()
684 if fn:
685 cb = QApplication.clipboard()
686 cb.setText(fn)
687
688 def __contextMenuMoveLeft(self):
689 """
690 Private method to move a tab one position to the left.
691 """
692 self.moveTab(self.contextMenuIndex, self.contextMenuIndex - 1)
693
694 def __contextMenuMoveRight(self):
695 """
696 Private method to move a tab one position to the right.
697 """
698 self.moveTab(self.contextMenuIndex, self.contextMenuIndex + 1)
699
700 def __contextMenuMoveFirst(self):
701 """
702 Private method to move a tab to the first position.
703 """
704 self.moveTab(self.contextMenuIndex, 0)
705
706 def __contextMenuMoveLast(self):
707 """
708 Private method to move a tab to the last position.
709 """
710 self.moveTab(self.contextMenuIndex, self.count() - 1)
711
712 def __closeButtonClicked(self):
713 """
714 Private method to handle the press of the close button.
715 """
716 self.vm.closeEditorWindow(self.currentWidget().getEditor())
717
718 def __closeRequested(self, index):
719 """
720 Private method to handle the press of the individual tab close button.
721
722 @param index index of the tab (integer)
723 """
724 if index >= 0:
725 self.vm.closeEditorWindow(self.widget(index).getEditor())
726
727 def mouseDoubleClickEvent(self, event):
728 """
729 Protected method handling double click events.
730
731 @param event reference to the event object (QMouseEvent)
732 """
733 self.vm.newEditor()
734
735
736 class Tabview(ViewManager):
737 """
738 Class implementing a tabbed viewmanager class embedded in a splitter.
739
740 @signal changeCaption(str) emitted if a change of the caption is necessary
741 @signal editorChanged(str) emitted when the current editor has changed
742 @signal editorChangedEd(Editor) emitted when the current editor has changed
743 @signal lastEditorClosed() emitted after the last editor window was closed
744 @signal editorOpened(str) emitted after an editor window was opened
745 @signal editorOpenedEd(Editor) emitted after an editor window was opened
746 @signal editorClosed(str) emitted just before an editor window gets closed
747 @signal editorClosedEd(Editor) emitted just before an editor window gets
748 closed
749 @signal editorRenamed(str) emitted after an editor was renamed
750 @signal editorRenamedEd(Editor) emitted after an editor was renamed
751 @signal editorSaved(str) emitted after an editor window was saved
752 @signal editorSavedEd(Editor) emitted after an editor window was saved
753 @signal checkActions(Editor) emitted when some actions should be checked
754 for their status
755 @signal cursorChanged(Editor) emitted after the cursor position of the
756 active window has changed
757 @signal breakpointToggled(Editor) emitted when a breakpoint is toggled.
758 @signal bookmarkToggled(Editor) emitted when a bookmark is toggled.
759 @signal syntaxerrorToggled(Editor) emitted when a syntax error is toggled.
760 @signal previewStateChanged(bool) emitted to signal a change in the
761 preview state
762 @signal previewStateChanged(bool) emitted to signal a change in the
763 preview state
764 @signal astViewerStateChanged(bool) emitted to signal a change in the
765 AST viewer state
766 @signal editorLanguageChanged(Editor) emitted to signal a change of an
767 editors language
768 @signal editorTextChanged(Editor) emitted to signal a change of an
769 editor's text
770 @signal editorLineChanged(str,int) emitted to signal a change of an
771 editor's current line (line is given one based)
772 @signal editorLineChangedEd(Editor,int) emitted to signal a change of an
773 editor's current line (line is given one based)
774 """
775 changeCaption = pyqtSignal(str)
776 editorChanged = pyqtSignal(str)
777 editorChangedEd = pyqtSignal(Editor)
778 lastEditorClosed = pyqtSignal()
779 editorOpened = pyqtSignal(str)
780 editorOpenedEd = pyqtSignal(Editor)
781 editorClosed = pyqtSignal(str)
782 editorClosedEd = pyqtSignal(Editor)
783 editorRenamed = pyqtSignal(str)
784 editorRenamedEd = pyqtSignal(Editor)
785 editorSaved = pyqtSignal(str)
786 editorSavedEd = pyqtSignal(Editor)
787 checkActions = pyqtSignal(Editor)
788 cursorChanged = pyqtSignal(Editor)
789 breakpointToggled = pyqtSignal(Editor)
790 bookmarkToggled = pyqtSignal(Editor)
791 syntaxerrorToggled = pyqtSignal(Editor)
792 previewStateChanged = pyqtSignal(bool)
793 astViewerStateChanged = pyqtSignal(bool)
794 editorLanguageChanged = pyqtSignal(Editor)
795 editorTextChanged = pyqtSignal(Editor)
796 editorLineChanged = pyqtSignal(str, int)
797 editorLineChangedEd = pyqtSignal(Editor, int)
798
799 def __init__(self, parent):
800 """
801 Constructor
802
803 @param parent parent widget
804 @type QWidget
805 """
806 self.tabWidgets = []
807
808 self.__splitter = QSplitter(parent)
809 ViewManager.__init__(self)
810 self.__splitter.setChildrenCollapsible(False)
811
812 tw = TabWidget(self)
813 self.__splitter.addWidget(tw)
814 self.tabWidgets.append(tw)
815 self.currentTabWidget = tw
816 self.currentTabWidget.showIndicator(True)
817 tw.currentChanged.connect(self.__currentChanged)
818 tw.installEventFilter(self)
819 tw.tabBar().installEventFilter(self)
820 self.__splitter.setOrientation(Qt.Orientation.Vertical)
821 self.__inRemoveView = False
822
823 self.maxFileNameChars = Preferences.getUI(
824 "TabViewManagerFilenameLength")
825 self.filenameOnly = Preferences.getUI("TabViewManagerFilenameOnly")
826
827 def mainWidget(self):
828 """
829 Public method to return a reference to the main Widget of a
830 specific view manager subclass.
831
832 @return reference to the main widget
833 @rtype QWidget
834 """
835 return self.__splitter
836
837 def canCascade(self):
838 """
839 Public method to signal if cascading of managed windows is available.
840
841 @return flag indicating cascading of windows is available
842 @rtype bool
843 """
844 return False
845
846 def canTile(self):
847 """
848 Public method to signal if tiling of managed windows is available.
849
850 @return flag indicating tiling of windows is available
851 @rtype bool
852 """
853 return False
854
855 def canSplit(self):
856 """
857 public method to signal if splitting of the view is available.
858
859 @return flag indicating splitting of the view is available.
860 @rtype bool
861 """
862 return True
863
864 def tile(self):
865 """
866 Public method to tile the managed windows.
867 """
868 pass
869
870 def cascade(self):
871 """
872 Public method to cascade the managed windows.
873 """
874 pass
875
876 def _removeAllViews(self):
877 """
878 Protected method to remove all views (i.e. windows).
879 """
880 for win in self.editors:
881 self._removeView(win)
882
883 def _removeView(self, win):
884 """
885 Protected method to remove a view (i.e. window).
886
887 @param win editor window to be removed
888 @type Editor
889 """
890 self.__inRemoveView = True
891 for tw in self.tabWidgets:
892 if tw.hasEditor(win):
893 tw.removeWidget(win)
894 break
895 win.closeIt()
896 self.__inRemoveView = False
897
898 # if this was the last editor in this view, switch to the next, that
899 # still has open editors
900 for i in (
901 list(range(self.tabWidgets.index(tw), -1, -1)) +
902 list(range(self.tabWidgets.index(tw) + 1,
903 len(self.tabWidgets)))
904 ):
905 if self.tabWidgets[i].hasEditors():
906 self.currentTabWidget.showIndicator(False)
907 self.currentTabWidget = self.tabWidgets[i]
908 self.currentTabWidget.showIndicator(True)
909 self.activeWindow().setFocus()
910 break
911
912 aw = self.activeWindow()
913 fn = aw and aw.getFileName() or None
914 if fn:
915 self.changeCaption.emit(fn)
916 self.editorChanged.emit(fn)
917 self.editorLineChanged.emit(fn, aw.getCursorPosition()[0] + 1)
918 else:
919 self.changeCaption.emit("")
920 self.editorChangedEd.emit(aw)
921
922 def _addView(self, win, fn=None, noName="", addNext=False, indexes=None):
923 """
924 Protected method to add a view (i.e. window).
925
926 @param win editor assembly to be added
927 @type EditorAssembly
928 @param fn filename of this editor
929 @type str
930 @param noName name to be used for an unnamed editor
931 @type str
932 @param addNext flag indicating to add the view next to the current
933 view
934 @type bool
935 @param indexes of the editor, first the split view index, second the
936 index within the view
937 @type tuple of two int
938 """
939 editor = win.getEditor()
940 if not fn:
941 if not noName:
942 self.untitledCount += 1
943 noName = self.tr("Untitled {0}").format(self.untitledCount)
944 if addNext:
945 index = self.currentTabWidget.currentIndex() + 1
946 self.currentTabWidget.insertWidget(index, win, noName)
947 elif indexes:
948 if indexes[0] < len(self.tabWidgets):
949 tw = self.tabWidgets[indexes[0]]
950 else:
951 tw = self.tabWidgets[-1]
952 tw.insertWidget(indexes[1], win, noName)
953 else:
954 self.currentTabWidget.addTab(win, noName)
955 editor.setNoName(noName)
956 else:
957 if self.filenameOnly:
958 txt = os.path.basename(fn)
959 else:
960 txt = e5App().getObject("Project").getRelativePath(fn)
961 if len(txt) > self.maxFileNameChars:
962 txt = "...{0}".format(txt[-self.maxFileNameChars:])
963 if not QFileInfo(fn).isWritable():
964 txt = self.tr("{0} (ro)").format(txt)
965 if addNext:
966 index = self.currentTabWidget.currentIndex() + 1
967 self.currentTabWidget.insertWidget(index, win, txt)
968 elif indexes:
969 if indexes[0] < len(self.tabWidgets):
970 tw = self.tabWidgets[indexes[0]]
971 else:
972 tw = self.tabWidgets[-1]
973 tw.insertWidget(indexes[1], win, txt)
974 else:
975 self.currentTabWidget.addTab(win, txt)
976 index = self.currentTabWidget.indexOf(win)
977 self.currentTabWidget.setTabToolTip(index, fn)
978 self.currentTabWidget.setCurrentWidget(win)
979 win.show()
980 editor.setFocus()
981 if fn:
982 self.changeCaption.emit(fn)
983 self.editorChanged.emit(fn)
984 self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
985 else:
986 self.changeCaption.emit("")
987 self.editorChangedEd.emit(editor)
988
989 def insertView(self, win, tabWidget, index, fn=None, noName=""):
990 """
991 Public method to add a view (i.e. window).
992
993 @param win editor assembly to be inserted
994 @type EditorAssembly
995 @param tabWidget reference to the tab widget to insert the editor into
996 @type TabWidget
997 @param index index position to insert at
998 @type int
999 @param fn filename of this editor
1000 @type str
1001 @param noName name to be used for an unnamed editor
1002 @type str
1003 """
1004 editor = win.getEditor()
1005 if fn is None:
1006 if not noName:
1007 self.untitledCount += 1
1008 noName = self.tr("Untitled {0}").format(self.untitledCount)
1009 tabWidget.insertWidget(index, win, noName)
1010 editor.setNoName(noName)
1011 else:
1012 if self.filenameOnly:
1013 txt = os.path.basename(fn)
1014 else:
1015 txt = e5App().getObject("Project").getRelativePath(fn)
1016 if len(txt) > self.maxFileNameChars:
1017 txt = "...{0}".format(txt[-self.maxFileNameChars:])
1018 if not QFileInfo(fn).isWritable():
1019 txt = self.tr("{0} (ro)").format(txt)
1020 nindex = tabWidget.insertWidget(index, win, txt)
1021 tabWidget.setTabToolTip(nindex, fn)
1022 tabWidget.setCurrentWidget(win)
1023 win.show()
1024 editor.setFocus()
1025 if fn:
1026 self.changeCaption.emit(fn)
1027 self.editorChanged.emit(fn)
1028 self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
1029 else:
1030 self.changeCaption.emit("")
1031 self.editorChangedEd.emit(editor)
1032
1033 self._modificationStatusChanged(editor.isModified(), editor)
1034 self._checkActions(editor)
1035
1036 def _showView(self, win, fn=None):
1037 """
1038 Protected method to show a view (i.e. window).
1039
1040 @param win editor assembly to be shown
1041 @type EditorAssembly
1042 @param fn filename of this editor
1043 @type str
1044 """
1045 win.show()
1046 editor = win.getEditor()
1047 for tw in self.tabWidgets:
1048 if tw.hasEditor(editor):
1049 tw.setCurrentWidget(win)
1050 self.currentTabWidget.showIndicator(False)
1051 self.currentTabWidget = tw
1052 self.currentTabWidget.showIndicator(True)
1053 break
1054 editor.setFocus()
1055
1056 def activeWindow(self):
1057 """
1058 Public method to return the active (i.e. current) window.
1059
1060 @return reference to the active editor
1061 @rtype Editor
1062 """
1063 cw = self.currentTabWidget.currentWidget()
1064 if cw:
1065 return cw.getEditor()
1066 else:
1067 return None
1068
1069 def showWindowMenu(self, windowMenu):
1070 """
1071 Public method to set up the viewmanager part of the Window menu.
1072
1073 @param windowMenu reference to the window menu
1074 @type QMenu
1075 """
1076 pass
1077
1078 def _initWindowActions(self):
1079 """
1080 Protected method to define the user interface actions for window
1081 handling.
1082 """
1083 pass
1084
1085 def setEditorName(self, editor, newName):
1086 """
1087 Public method to change the displayed name of the editor.
1088
1089 @param editor editor window to be changed
1090 @type Editor
1091 @param newName new name to be shown
1092 @type str
1093 """
1094 if newName:
1095 if self.filenameOnly:
1096 tabName = os.path.basename(newName)
1097 else:
1098 tabName = e5App().getObject("Project").getRelativePath(newName)
1099 if len(tabName) > self.maxFileNameChars:
1100 tabName = "...{0}".format(tabName[-self.maxFileNameChars:])
1101 index = self.currentTabWidget.indexOf(editor)
1102 self.currentTabWidget.setTabText(index, tabName)
1103 self.currentTabWidget.setTabToolTip(index, newName)
1104 self.changeCaption.emit(newName)
1105
1106 def _modificationStatusChanged(self, m, editor):
1107 """
1108 Protected slot to handle the modificationStatusChanged signal.
1109
1110 @param m flag indicating the modification status
1111 @type bool
1112 @param editor editor window changed
1113 @type Editor
1114 """
1115 for tw in self.tabWidgets:
1116 if tw.hasEditor(editor):
1117 break
1118 index = tw.indexOf(editor)
1119 keys = []
1120 if m:
1121 keys.append("fileModified")
1122 if editor.hasSyntaxErrors():
1123 keys.append("syntaxError22")
1124 elif editor.hasWarnings():
1125 keys.append("warning22")
1126 if not keys:
1127 keys.append("empty")
1128 tw.setTabIcon(index, UI.PixmapCache.getCombinedIcon(keys))
1129 self._checkActions(editor)
1130
1131 def _syntaxErrorToggled(self, editor):
1132 """
1133 Protected slot to handle the syntaxerrorToggled signal.
1134
1135 @param editor editor that sent the signal
1136 @type Editor
1137 """
1138 for tw in self.tabWidgets:
1139 if tw.hasEditor(editor):
1140 break
1141 index = tw.indexOf(editor)
1142 keys = []
1143 if editor.isModified():
1144 keys.append("fileModified")
1145 if editor.hasSyntaxErrors():
1146 keys.append("syntaxError22")
1147 elif editor.hasWarnings():
1148 keys.append("warning22")
1149 if not keys:
1150 keys.append("empty")
1151 tw.setTabIcon(index, UI.PixmapCache.getCombinedIcon(keys))
1152
1153 ViewManager._syntaxErrorToggled(self, editor)
1154
1155 def addSplit(self):
1156 """
1157 Public method used to split the current view.
1158 """
1159 tw = TabWidget(self)
1160 tw.show()
1161 self.__splitter.addWidget(tw)
1162 self.tabWidgets.append(tw)
1163 self.currentTabWidget.showIndicator(False)
1164 self.currentTabWidget = self.tabWidgets[-1]
1165 self.currentTabWidget.showIndicator(True)
1166 tw.currentChanged.connect(self.__currentChanged)
1167 tw.installEventFilter(self)
1168 tw.tabBar().installEventFilter(self)
1169 size = (
1170 self.width()
1171 if self.__splitter.orientation() == Qt.Orientation.Horizontal else
1172 self.height()
1173 )
1174 self.__splitter.setSizes(
1175 [int(size / len(self.tabWidgets))] * len(self.tabWidgets))
1176 self.splitRemoveAct.setEnabled(True)
1177 self.nextSplitAct.setEnabled(True)
1178 self.prevSplitAct.setEnabled(True)
1179
1180 @pyqtSlot()
1181 def removeSplit(self, index=-1):
1182 """
1183 Public method used to remove the current split view or a split view
1184 by index.
1185
1186 @param index index of the split to be removed (-1 means to
1187 delete the current split)
1188 @type int
1189 @return flag indicating successful deletion
1190 @rtype bool
1191 """
1192 if len(self.tabWidgets) > 1:
1193 if index == -1:
1194 tw = self.currentTabWidget
1195 else:
1196 if index < len(self.tabWidgets):
1197 tw = self.tabWidgets[index]
1198 else:
1199 tw = self.tabWidgets[-1]
1200 res = True
1201 savedEditors = tw.editors[:]
1202 for editor in savedEditors:
1203 res &= self.closeEditor(editor)
1204 if res:
1205 try:
1206 i = self.tabWidgets.index(tw)
1207 except ValueError:
1208 return True
1209 if i == len(self.tabWidgets) - 1:
1210 i -= 1
1211 self.tabWidgets.remove(tw)
1212 tw.close()
1213 self.currentTabWidget = self.tabWidgets[i]
1214 for tw in self.tabWidgets:
1215 tw.showIndicator(tw == self.currentTabWidget)
1216 if self.currentTabWidget is not None:
1217 assembly = self.currentTabWidget.currentWidget()
1218 if assembly is not None:
1219 editor = assembly.getEditor()
1220 if editor is not None:
1221 editor.setFocus(Qt.FocusReason.OtherFocusReason)
1222 if len(self.tabWidgets) == 1:
1223 self.splitRemoveAct.setEnabled(False)
1224 self.nextSplitAct.setEnabled(False)
1225 self.prevSplitAct.setEnabled(False)
1226 return True
1227
1228 return False
1229
1230 def splitCount(self):
1231 """
1232 Public method to get the number of splitted views.
1233
1234 @return number of splitted views
1235 @rtype int
1236 """
1237 return len(self.tabWidgets)
1238
1239 def setSplitCount(self, count):
1240 """
1241 Public method to set the number of split views.
1242
1243 @param count number of split views
1244 @type int
1245 """
1246 if count > self.splitCount():
1247 while self.splitCount() < count:
1248 self.addSplit()
1249 elif count < self.splitCount():
1250 while self.splitCount() > count:
1251 # use an arbitrarily large index to remove the last one
1252 self.removeSplit(index=100)
1253
1254 def getSplitOrientation(self):
1255 """
1256 Public method to get the orientation of the split view.
1257
1258 @return orientation of the split
1259 @rtype Qt.Orientation.Horizontal or Qt.Orientation.Vertical
1260 """
1261 return self.__splitter.orientation()
1262
1263 def setSplitOrientation(self, orientation):
1264 """
1265 Public method used to set the orientation of the split view.
1266
1267 @param orientation orientation of the split
1268 @type Qt.Orientation.Horizontal or Qt.Orientation.Vertical
1269 """
1270 self.__splitter.setOrientation(orientation)
1271
1272 def nextSplit(self):
1273 """
1274 Public slot used to move to the next split.
1275 """
1276 aw = self.activeWindow()
1277 _hasFocus = aw and aw.hasFocus()
1278 ind = self.tabWidgets.index(self.currentTabWidget) + 1
1279 if ind == len(self.tabWidgets):
1280 ind = 0
1281
1282 self.currentTabWidget.showIndicator(False)
1283 self.currentTabWidget = self.tabWidgets[ind]
1284 self.currentTabWidget.showIndicator(True)
1285 if _hasFocus:
1286 aw = self.activeWindow()
1287 if aw:
1288 aw.setFocus()
1289
1290 def prevSplit(self):
1291 """
1292 Public slot used to move to the previous split.
1293 """
1294 aw = self.activeWindow()
1295 _hasFocus = aw and aw.hasFocus()
1296 ind = self.tabWidgets.index(self.currentTabWidget) - 1
1297 if ind == -1:
1298 ind = len(self.tabWidgets) - 1
1299
1300 self.currentTabWidget.showIndicator(False)
1301 self.currentTabWidget = self.tabWidgets[ind]
1302 self.currentTabWidget.showIndicator(True)
1303 if _hasFocus:
1304 aw = self.activeWindow()
1305 if aw:
1306 aw.setFocus()
1307
1308 def __currentChanged(self, index):
1309 """
1310 Private slot to handle the currentChanged signal.
1311
1312 @param index index of the current tab
1313 @type int
1314 """
1315 if index == -1 or not self.editors:
1316 return
1317
1318 editor = self.activeWindow()
1319 if editor is None:
1320 return
1321
1322 self._checkActions(editor)
1323 editor.setFocus()
1324 fn = editor.getFileName()
1325 if fn:
1326 self.changeCaption.emit(fn)
1327 if not self.__inRemoveView:
1328 self.editorChanged.emit(fn)
1329 self.editorLineChanged.emit(
1330 fn, editor.getCursorPosition()[0] + 1)
1331 else:
1332 self.changeCaption.emit("")
1333 self.editorChangedEd.emit(editor)
1334
1335 def eventFilter(self, watched, event):
1336 """
1337 Public method called to filter the event queue.
1338
1339 @param watched the QObject being watched
1340 @type QObject
1341 @param event the event that occurred
1342 @type QEvent
1343 @return always False
1344 @rtype bool
1345 """
1346 if (
1347 event.type() == QEvent.Type.MouseButtonPress and
1348 isinstance(event, QMouseEvent) and
1349 event.button() != Qt.MouseButton.RightButton
1350 ):
1351 switched = True
1352 self.currentTabWidget.showIndicator(False)
1353 if isinstance(watched, E5TabWidget):
1354 switched = watched is not self.currentTabWidget
1355 self.currentTabWidget = watched
1356 elif isinstance(watched, QTabBar):
1357 switched = watched.parent() is not self.currentTabWidget
1358 self.currentTabWidget = watched.parent()
1359 if switched:
1360 index = self.currentTabWidget.selectTab(event.pos())
1361 switched = (
1362 self.currentTabWidget.widget(index) is
1363 self.activeWindow()
1364 )
1365 elif isinstance(watched, QScintilla.Editor.Editor):
1366 for tw in self.tabWidgets:
1367 if tw.hasEditor(watched):
1368 switched = tw is not self.currentTabWidget
1369 self.currentTabWidget = tw
1370 break
1371 self.currentTabWidget.showIndicator(True)
1372
1373 aw = self.activeWindow()
1374 if aw is not None:
1375 self._checkActions(aw)
1376 aw.setFocus()
1377 fn = aw.getFileName()
1378 if fn:
1379 self.changeCaption.emit(fn)
1380 if switched:
1381 self.editorChanged.emit(fn)
1382 self.editorLineChanged.emit(
1383 fn, aw.getCursorPosition()[0] + 1)
1384 else:
1385 self.changeCaption.emit("")
1386 self.editorChangedEd.emit(aw)
1387
1388 return False
1389
1390 def preferencesChanged(self):
1391 """
1392 Public slot to handle the preferencesChanged signal.
1393 """
1394 ViewManager.preferencesChanged(self)
1395
1396 self.maxFileNameChars = Preferences.getUI(
1397 "TabViewManagerFilenameLength")
1398 self.filenameOnly = Preferences.getUI("TabViewManagerFilenameOnly")
1399
1400 for tabWidget in self.tabWidgets:
1401 for index in range(tabWidget.count()):
1402 editor = tabWidget.widget(index)
1403 if isinstance(editor, QScintilla.Editor.Editor):
1404 fn = editor.getFileName()
1405 if fn:
1406 if self.filenameOnly:
1407 txt = os.path.basename(fn)
1408 else:
1409 txt = e5App().getObject(
1410 "Project").getRelativePath(fn)
1411 if len(txt) > self.maxFileNameChars:
1412 txt = "...{0}".format(txt[-self.maxFileNameChars:])
1413 if not QFileInfo(fn).isWritable():
1414 txt = self.tr("{0} (ro)").format(txt)
1415 tabWidget.setTabText(index, txt)
1416
1417 def getTabWidgetById(self, id_):
1418 """
1419 Public method to get a reference to a tab widget knowing its ID.
1420
1421 @param id_ id of the tab widget
1422 @type int
1423 @return reference to the tab widget
1424 @rtype TabWidget
1425 """
1426 for tw in self.tabWidgets:
1427 if id(tw) == id_:
1428 return tw
1429 return None
1430
1431 def getOpenEditorsForSession(self):
1432 """
1433 Public method to get a lists of all open editors.
1434
1435 The returned list contains one list per split view. If the view manager
1436 cannot split the view, only one list of editors is returned.
1437
1438 @return list of list of editor references
1439 @rtype list of list of Editor
1440 """
1441 editorLists = []
1442 for tabWidget in self.tabWidgets:
1443 editors = []
1444 for index in range(tabWidget.count()):
1445 widget = tabWidget.widget(index)
1446 if isinstance(widget, EditorAssembly):
1447 editor = widget.getEditor()
1448 editors.append(editor)
1449 editorLists.append(editors)
1450 return editorLists

eric ide

mercurial