eric7/Tools/TRPreviewer.py

branch
eric7
changeset 8312
800c432b34c8
parent 8243
cc717c2ae956
child 8318
962bce857696
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/Tools/TRPreviewer.py	Sat May 15 18:45:04 2021 +0200
@@ -0,0 +1,904 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2004 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the TR Previewer main window.
+"""
+
+import os
+import contextlib
+
+from PyQt5.QtCore import (
+    QDir, QTimer, QFileInfo, pyqtSignal, QEvent, QSize, QTranslator, QObject,
+    Qt, QCoreApplication
+)
+from PyQt5.QtGui import QKeySequence
+from PyQt5.QtWidgets import (
+    QSizePolicy, QSpacerItem, QWidget, QHBoxLayout, QWhatsThis, QMdiArea,
+    QApplication, QComboBox, QVBoxLayout, QAction, QLabel
+)
+from PyQt5 import uic
+
+
+from E5Gui import E5MessageBox, E5FileDialog
+from E5Gui.E5MainWindow import E5MainWindow
+from E5Gui.E5Application import e5App
+
+import UI.PixmapCache
+import UI.Config
+
+import Preferences
+
+
+noTranslationName = QCoreApplication.translate(
+    "TRPreviewer", "<No translation>")
+
+
+class TRPreviewer(E5MainWindow):
+    """
+    Class implementing the UI Previewer main window.
+    """
+    def __init__(self, filenames=None, parent=None, name=None):
+        """
+        Constructor
+        
+        @param filenames filenames of form and/or translation files to load
+        @param parent parent widget of this window (QWidget)
+        @param name name of this window (string)
+        """
+        self.mainWidget = None
+        self.currentFile = QDir.currentPath()
+        
+        super().__init__(parent)
+        if not name:
+            self.setObjectName("TRPreviewer")
+        else:
+            self.setObjectName(name)
+        
+        self.setStyle(Preferences.getUI("Style"),
+                      Preferences.getUI("StyleSheet"))
+        
+        self.resize(QSize(800, 600).expandedTo(self.minimumSizeHint()))
+        self.statusBar()
+        
+        self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
+        self.setWindowTitle(self.tr("Translations Previewer"))
+
+        self.cw = QWidget(self)
+        self.cw.setObjectName("qt_central_widget")
+        
+        self.TRPreviewerLayout = QVBoxLayout(self.cw)
+        self.TRPreviewerLayout.setContentsMargins(6, 6, 6, 6)
+        self.TRPreviewerLayout.setSpacing(6)
+        self.TRPreviewerLayout.setObjectName("TRPreviewerLayout")
+
+        self.languageLayout = QHBoxLayout()
+        self.languageLayout.setContentsMargins(0, 0, 0, 0)
+        self.languageLayout.setSpacing(6)
+        self.languageLayout.setObjectName("languageLayout")
+
+        self.languageLabel = QLabel(
+            self.tr("Select language file"), self.cw)
+        self.languageLabel.setObjectName("languageLabel")
+        self.languageLayout.addWidget(self.languageLabel)
+
+        self.languageCombo = QComboBox(self.cw)
+        self.languageCombo.setObjectName("languageCombo")
+        self.languageCombo.setEditable(False)
+        self.languageCombo.setToolTip(self.tr("Select language file"))
+        self.languageCombo.setSizePolicy(
+            QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
+        self.languageLayout.addWidget(self.languageCombo)
+        
+        languageSpacer = QSpacerItem(
+            40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
+        self.languageLayout.addItem(languageSpacer)
+        self.TRPreviewerLayout.addLayout(self.languageLayout)
+
+        self.preview = WidgetArea(self.cw)
+        self.preview.setObjectName("preview")
+        self.TRPreviewerLayout.addWidget(self.preview)
+        self.preview.lastWidgetClosed.connect(self.__updateActions)
+
+        self.setCentralWidget(self.cw)
+        
+        self.languageCombo.activated[int].connect(self.__setTranslation)
+        
+        self.translations = TranslationsDict(self.languageCombo, self)
+        self.translations.translationChanged.connect(
+            self.preview.rebuildWidgets)
+        
+        self.__initActions()
+        self.__initMenus()
+        self.__initToolbars()
+        
+        self.__updateActions()
+        
+        # fire up the single application server
+        from .TRSingleApplication import TRSingleApplicationServer
+        self.SAServer = TRSingleApplicationServer(self)
+        self.SAServer.loadForm.connect(self.preview.loadWidget)
+        self.SAServer.loadTranslation.connect(self.translations.add)
+        
+        # defere loading of a UI file until we are shown
+        self.filesToLoad = [] if filenames is None else filenames[:]
+        
+    def show(self):
+        """
+        Public slot to show this dialog.
+        
+        This overloaded slot loads a UI file to be previewed after
+        the main window has been shown. This way, previewing a dialog
+        doesn't interfere with showing the main window.
+        """
+        super().show()
+        if self.filesToLoad:
+            filenames, self.filesToLoad = (self.filesToLoad[:], [])
+            first = True
+            for fn in filenames:
+                fi = QFileInfo(fn)
+                if fi.suffix().lower() == 'ui':
+                    self.preview.loadWidget(fn)
+                elif fi.suffix().lower() == 'qm':
+                    self.translations.add(fn, first)
+                    first = False
+            
+            self.__updateActions()
+        
+    def closeEvent(self, event):
+        """
+        Protected event handler for the close event.
+        
+        @param event close event (QCloseEvent)
+        """
+        if self.SAServer is not None:
+            self.SAServer.shutdown()
+            self.SAServer = None
+        event.accept()
+        
+    def __initActions(self):
+        """
+        Private method to define the user interface actions.
+        """
+        self.openUIAct = QAction(
+            UI.PixmapCache.getIcon("openUI"),
+            self.tr('&Open UI Files...'), self)
+        self.openUIAct.setStatusTip(self.tr('Open UI files for display'))
+        self.openUIAct.setWhatsThis(self.tr(
+            """<b>Open UI Files</b>"""
+            """<p>This opens some UI files for display.</p>"""
+        ))
+        self.openUIAct.triggered.connect(self.__openWidget)
+        
+        self.openQMAct = QAction(
+            UI.PixmapCache.getIcon("openQM"),
+            self.tr('Open &Translation Files...'), self)
+        self.openQMAct.setStatusTip(self.tr(
+            'Open Translation files for display'))
+        self.openQMAct.setWhatsThis(self.tr(
+            """<b>Open Translation Files</b>"""
+            """<p>This opens some translation files for display.</p>"""
+        ))
+        self.openQMAct.triggered.connect(self.__openTranslation)
+        
+        self.reloadAct = QAction(
+            UI.PixmapCache.getIcon("reload"),
+            self.tr('&Reload Translations'), self)
+        self.reloadAct.setStatusTip(self.tr(
+            'Reload the loaded translations'))
+        self.reloadAct.setWhatsThis(self.tr(
+            """<b>Reload Translations</b>"""
+            """<p>This reloads the translations for the loaded"""
+            """ languages.</p>"""
+        ))
+        self.reloadAct.triggered.connect(self.translations.reload)
+        
+        self.exitAct = QAction(
+            UI.PixmapCache.getIcon("exit"), self.tr('&Quit'), self)
+        self.exitAct.setShortcut(QKeySequence(
+            self.tr("Ctrl+Q", "File|Quit")))
+        self.exitAct.setStatusTip(self.tr('Quit the application'))
+        self.exitAct.setWhatsThis(self.tr(
+            """<b>Quit</b>"""
+            """<p>Quit the application.</p>"""
+        ))
+        self.exitAct.triggered.connect(e5App().closeAllWindows)
+        
+        self.whatsThisAct = QAction(
+            UI.PixmapCache.getIcon("whatsThis"),
+            self.tr('&What\'s This?'), self)
+        self.whatsThisAct.setShortcut(QKeySequence(self.tr("Shift+F1")))
+        self.whatsThisAct.setStatusTip(self.tr('Context sensitive help'))
+        self.whatsThisAct.setWhatsThis(self.tr(
+            """<b>Display context sensitive help</b>"""
+            """<p>In What's This? mode, the mouse cursor shows an arrow"""
+            """ with a question mark, and you can click on the interface"""
+            """ elements to get a short description of what they do and"""
+            """ how to use them. In dialogs, this feature can be accessed"""
+            """ using the context help button in the titlebar.</p>"""
+        ))
+        self.whatsThisAct.triggered.connect(self.__whatsThis)
+
+        self.aboutAct = QAction(self.tr('&About'), self)
+        self.aboutAct.setStatusTip(self.tr(
+            'Display information about this software'))
+        self.aboutAct.setWhatsThis(self.tr(
+            """<b>About</b>"""
+            """<p>Display some information about this software.</p>"""
+        ))
+        self.aboutAct.triggered.connect(self.__about)
+        
+        self.aboutQtAct = QAction(self.tr('About &Qt'), self)
+        self.aboutQtAct.setStatusTip(
+            self.tr('Display information about the Qt toolkit'))
+        self.aboutQtAct.setWhatsThis(self.tr(
+            """<b>About Qt</b>"""
+            """<p>Display some information about the Qt toolkit.</p>"""
+        ))
+        self.aboutQtAct.triggered.connect(self.__aboutQt)
+        
+        self.tileAct = QAction(self.tr('&Tile'), self)
+        self.tileAct.setStatusTip(self.tr('Tile the windows'))
+        self.tileAct.setWhatsThis(self.tr(
+            """<b>Tile the windows</b>"""
+            """<p>Rearrange and resize the windows so that they are"""
+            """ tiled.</p>"""
+        ))
+        self.tileAct.triggered.connect(self.preview.tileSubWindows)
+        
+        self.cascadeAct = QAction(self.tr('&Cascade'), self)
+        self.cascadeAct.setStatusTip(self.tr('Cascade the windows'))
+        self.cascadeAct.setWhatsThis(self.tr(
+            """<b>Cascade the windows</b>"""
+            """<p>Rearrange and resize the windows so that they are"""
+            """ cascaded.</p>"""
+        ))
+        self.cascadeAct.triggered.connect(self.preview.cascadeSubWindows)
+        
+        self.closeAct = QAction(
+            UI.PixmapCache.getIcon("close"), self.tr('&Close'), self)
+        self.closeAct.setShortcut(QKeySequence(self.tr(
+            "Ctrl+W", "File|Close")))
+        self.closeAct.setStatusTip(self.tr('Close the current window'))
+        self.closeAct.setWhatsThis(self.tr(
+            """<b>Close Window</b>"""
+            """<p>Close the current window.</p>"""
+        ))
+        self.closeAct.triggered.connect(self.preview.closeWidget)
+        
+        self.closeAllAct = QAction(self.tr('Clos&e All'), self)
+        self.closeAllAct.setStatusTip(self.tr('Close all windows'))
+        self.closeAllAct.setWhatsThis(self.tr(
+            """<b>Close All Windows</b>"""
+            """<p>Close all windows.</p>"""
+        ))
+        self.closeAllAct.triggered.connect(self.preview.closeAllWidgets)
+
+    def __initMenus(self):
+        """
+        Private method to create the menus.
+        """
+        mb = self.menuBar()
+
+        menu = mb.addMenu(self.tr('&File'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.openUIAct)
+        menu.addAction(self.openQMAct)
+        menu.addAction(self.reloadAct)
+        menu.addSeparator()
+        menu.addAction(self.closeAct)
+        menu.addAction(self.closeAllAct)
+        menu.addSeparator()
+        menu.addAction(self.exitAct)
+        
+        self.windowMenu = mb.addMenu(self.tr('&Window'))
+        self.windowMenu.setTearOffEnabled(True)
+        self.windowMenu.aboutToShow.connect(self.__showWindowMenu)
+        self.windowMenu.triggered.connect(self.preview.toggleSelectedWidget)
+        
+        mb.addSeparator()
+        
+        menu = mb.addMenu(self.tr('&Help'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.aboutAct)
+        menu.addAction(self.aboutQtAct)
+        menu.addSeparator()
+        menu.addAction(self.whatsThisAct)
+
+    def __initToolbars(self):
+        """
+        Private method to create the toolbars.
+        """
+        filetb = self.addToolBar(self.tr("File"))
+        filetb.setIconSize(UI.Config.ToolBarIconSize)
+        filetb.addAction(self.openUIAct)
+        filetb.addAction(self.openQMAct)
+        filetb.addAction(self.reloadAct)
+        filetb.addSeparator()
+        filetb.addAction(self.closeAct)
+        filetb.addSeparator()
+        filetb.addAction(self.exitAct)
+        
+        helptb = self.addToolBar(self.tr("Help"))
+        helptb.setIconSize(UI.Config.ToolBarIconSize)
+        helptb.addAction(self.whatsThisAct)
+    
+    def __whatsThis(self):
+        """
+        Private slot called in to enter Whats This mode.
+        """
+        QWhatsThis.enterWhatsThisMode()
+        
+    def __updateActions(self):
+        """
+        Private slot to update the actions state.
+        """
+        if self.preview.hasWidgets():
+            self.closeAct.setEnabled(True)
+            self.closeAllAct.setEnabled(True)
+            self.tileAct.setEnabled(True)
+            self.cascadeAct.setEnabled(True)
+        else:
+            self.closeAct.setEnabled(False)
+            self.closeAllAct.setEnabled(False)
+            self.tileAct.setEnabled(False)
+            self.cascadeAct.setEnabled(False)
+        
+        if self.translations.hasTranslations():
+            self.reloadAct.setEnabled(True)
+        else:
+            self.reloadAct.setEnabled(False)
+
+    def __about(self):
+        """
+        Private slot to show the about information.
+        """
+        E5MessageBox.about(
+            self,
+            self.tr("TR Previewer"),
+            self.tr(
+                """<h3> About TR Previewer </h3>"""
+                """<p>The TR Previewer loads and displays Qt User-Interface"""
+                """ files and translation files and shows dialogs for a"""
+                """ selected language.</p>"""
+            )
+        )
+    
+    def __aboutQt(self):
+        """
+        Private slot to show info about Qt.
+        """
+        E5MessageBox.aboutQt(self, self.tr("TR Previewer"))
+    
+    def __openWidget(self):
+        """
+        Private slot to handle the Open Dialog action.
+        """
+        fileNameList = E5FileDialog.getOpenFileNames(
+            None,
+            self.tr("Select UI files"),
+            "",
+            self.tr("Qt User-Interface Files (*.ui)"))
+        
+        for fileName in fileNameList:
+            self.preview.loadWidget(fileName)
+        
+        self.__updateActions()
+    
+    def __openTranslation(self):
+        """
+        Private slot to handle the Open Translation action.
+        """
+        fileNameList = E5FileDialog.getOpenFileNames(
+            None,
+            self.tr("Select translation files"),
+            "",
+            self.tr("Qt Translation Files (*.qm)"))
+        
+        first = True
+        for fileName in fileNameList:
+            self.translations.add(fileName, first)
+            first = False
+        
+        self.__updateActions()
+    
+    def __setTranslation(self, index):
+        """
+        Private slot to activate a translation.
+        
+        @param index index of the selected entry
+        @type int
+        """
+        name = self.languageCombo.itemText(index)
+        self.translations.set(name)
+    
+    def __showWindowMenu(self):
+        """
+        Private slot to handle the aboutToShow signal of the window menu.
+        """
+        self.windowMenu.clear()
+        self.windowMenu.addAction(self.tileAct)
+        self.windowMenu.addAction(self.cascadeAct)
+        self.windowMenu.addSeparator()
+
+        self.preview.showWindowMenu(self.windowMenu)
+    
+    def reloadTranslations(self):
+        """
+        Public slot to reload all translations.
+        """
+        self.translations.reload()
+
+
+class Translation:
+    """
+    Class to store the properties of a translation.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        self.fileName = None
+        self.name = None
+        self.translator = None
+
+
+class TranslationsDict(QObject):
+    """
+    Class to store all loaded translations.
+    
+    @signal translationChanged() emit after a translator was set
+    """
+    translationChanged = pyqtSignal()
+    
+    def __init__(self, selector, parent):
+        """
+        Constructor
+        
+        @param selector reference to the QComboBox used to show the
+            available languages (QComboBox)
+        @param parent parent widget (QWidget)
+        """
+        super().__init__(parent)
+        
+        self.selector = selector
+        self.currentTranslator = None
+        self.selector.addItem(noTranslationName)
+        self.translations = []  # list of Translation objects
+    
+    def add(self, fileName, setTranslation=True):
+        """
+        Public method to add a translation to the list.
+        
+        If the translation file (*.qm) has not been loaded yet, it will
+        be loaded automatically.
+        
+        @param fileName name of the translation file to be added (string)
+        @param setTranslation flag indicating, if this should be set as
+            the active translation (boolean)
+        """
+        if not self.__haveFileName(fileName):
+            ntr = Translation()
+            ntr.fileName = fileName
+            ntr.name = self.__uniqueName(fileName)
+            if ntr.name is None:
+                E5MessageBox.warning(
+                    self.parent(),
+                    self.tr("Set Translator"),
+                    self.tr(
+                        """<p>The translation filename <b>{0}</b>"""
+                        """ is invalid.</p>""").format(fileName))
+                return
+            
+            ntr.translator = self.loadTransFile(fileName)
+            if ntr.translator is None:
+                return
+            
+            self.selector.addItem(ntr.name)
+            self.translations.append(ntr)
+        
+        if setTranslation:
+            tr = self.__findFileName(fileName)
+            self.set(tr.name)
+    
+    def set(self, name):
+        """
+        Public slot to set a translator by name.
+        
+        @param name name (language) of the translator to set (string)
+        """
+        nTranslator = None
+        
+        if name != noTranslationName:
+            trans = self.__findName(name)
+            if trans is None:
+                E5MessageBox.warning(
+                    self.parent(),
+                    self.tr("Set Translator"),
+                    self.tr(
+                        """<p>The translator <b>{0}</b> is not known.</p>""")
+                    .format(name))
+                return
+                
+            nTranslator = trans.translator
+        
+        if nTranslator == self.currentTranslator:
+            return
+        
+        if self.currentTranslator is not None:
+            QApplication.removeTranslator(self.currentTranslator)
+        if nTranslator is not None:
+            QApplication.installTranslator(nTranslator)
+        self.currentTranslator = nTranslator
+        
+        self.selector.blockSignals(True)
+        self.selector.setCurrentIndex(self.selector.findText(name))
+        self.selector.blockSignals(False)
+        
+        self.translationChanged.emit()
+    
+    def reload(self):
+        """
+        Public method to reload all translators.
+        """
+        cname = self.selector.currentText()
+        if self.currentTranslator is not None:
+            QApplication.removeTranslator(self.currentTranslator)
+            self.currentTranslator = None
+        
+        fileNames = []
+        for trans in self.translations:
+            trans.translator = None
+            fileNames.append(trans.fileName)
+        self.translations = []
+        self.selector.clear()
+        
+        self.selector.addItem(noTranslationName)
+        
+        for fileName in fileNames:
+            self.add(fileName, False)
+        
+        if self.__haveName(cname):
+            self.set(cname)
+        else:
+            self.set(noTranslationName)
+    
+    def __findFileName(self, transFileName):
+        """
+        Private method to find a translation by file name.
+        
+        @param transFileName file name of the translation file (string)
+        @return reference to a translation object or None
+        """
+        for trans in self.translations:
+            if trans.fileName == transFileName:
+                return trans
+        return None
+    
+    def __findName(self, name):
+        """
+        Private method to find a translation by name.
+        
+        @param name name (language) of the translation (string)
+        @return reference to a translation object or None
+        """
+        for trans in self.translations:
+            if trans.name == name:
+                return trans
+        return None
+    
+    def __haveFileName(self, transFileName):
+        """
+        Private method to check for the presence of a translation.
+        
+        @param transFileName file name of the translation file (string)
+        @return flag indicating the presence of the translation (boolean)
+        """
+        return self.__findFileName(transFileName) is not None
+    
+    def __haveName(self, name):
+        """
+        Private method to check for the presence of a named translation.
+        
+        @param name name (language) of the translation (string)
+        @return flag indicating the presence of the translation (boolean)
+        """
+        return self.__findName(name) is not None
+    
+    def __uniqueName(self, transFileName):
+        """
+        Private method to generate a unique name.
+        
+        @param transFileName file name of the translation file (string)
+        @return unique name (string or None)
+        """
+        name = os.path.basename(transFileName)
+        if not name:
+            return None
+        
+        uname = name
+        cnt = 1
+        while self.__haveName(uname):
+            cnt += 1
+            uname = "{0} <{1}>".format(name, cnt)
+        
+        return uname
+    
+    def __del(self, name):
+        """
+        Private method to delete a translator from the list of available
+        translators.
+        
+        @param name name of the translator to delete (string)
+        """
+        if name == noTranslationName:
+            return
+        
+        trans = self.__findName(name)
+        if trans is None:
+            return
+        
+        if self.selector().currentText() == name:
+            self.set(noTranslationName)
+        
+        self.translations.remove(trans)
+        del trans
+    
+    def loadTransFile(self, transFileName):
+        """
+        Public slot to load a translation file.
+        
+        @param transFileName file name of the translation file (string)
+        @return reference to the new translator object (QTranslator)
+        """
+        tr = QTranslator()
+        if tr.load(transFileName):
+            return tr
+        
+        E5MessageBox.warning(
+            self.parent(),
+            self.tr("Load Translator"),
+            self.tr("""<p>The translation file <b>{0}</b> could"""
+                    """ not be loaded.</p>""").format(transFileName))
+        return None
+
+    def hasTranslations(self):
+        """
+        Public method to check for loaded translations.
+        
+        @return flag signaling if any translation was loaded (boolean)
+        """
+        return len(self.translations) > 0
+
+
+class WidgetView(QWidget):
+    """
+    Class to show a dynamically loaded widget (or dialog).
+    """
+    def __init__(self, uiFileName, parent=None, name=None):
+        """
+        Constructor
+        
+        @param uiFileName name of the UI file to load (string)
+        @param parent parent widget (QWidget)
+        @param name name of this widget (string)
+        """
+        super().__init__(parent)
+        if name:
+            self.setObjectName(name)
+            self.setWindowTitle(name)
+        
+        self.__widget = None
+        self.__uiFileName = uiFileName
+        self.__layout = QHBoxLayout(self)
+        self.__valid = False
+        self.__timer = QTimer(self)
+        self.__timer.setSingleShot(True)
+        self.__timer.timeout.connect(self.buildWidget)
+    
+    def isValid(self):
+        """
+        Public method to return the validity of this widget view.
+        
+        @return flag indicating the validity (boolean)
+        """
+        return self.__valid
+    
+    def uiFileName(self):
+        """
+        Public method to retrieve the name of the UI file.
+        
+        @return filename of the loaded UI file (string)
+        """
+        return self.__uiFileName
+    
+    def buildWidget(self):
+        """
+        Public slot to load a UI file.
+        """
+        if self.__widget:
+            self.__widget.close()
+            self.__layout.removeWidget(self.__widget)
+            del self.__widget
+            self.__widget = None
+        
+        with contextlib.suppress(Exception):
+            self.__widget = uic.loadUi(self.__uiFileName)
+        
+        if not self.__widget:
+            E5MessageBox.warning(
+                self,
+                self.tr("Load UI File"),
+                self.tr(
+                    """<p>The file <b>{0}</b> could not be loaded.</p>""")
+                .format(self.__uiFileName))
+            self.__valid = False
+            return
+        
+        self.__widget.setParent(self)
+        self.__layout.addWidget(self.__widget)
+        self.__widget.show()
+        self.__valid = True
+        self.adjustSize()
+        
+        self.__timer.stop()
+    
+    def __rebuildWidget(self):
+        """
+        Private method to schedule a rebuild of the widget.
+        """
+        self.__timer.start(0)
+
+
+class WidgetArea(QMdiArea):
+    """
+    Specialized MDI area to show the loaded widgets.
+    
+    @signal lastWidgetClosed() emitted after the last widget was closed
+    @signal rebuildWidgets() emitted to indicate a change of loaded widgets
+    """
+    lastWidgetClosed = pyqtSignal()
+    rebuildWidgets = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent parent widget (QWidget)
+        """
+        super().__init__(parent)
+        
+        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
+        self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
+        
+        self.widgets = []
+    
+    def loadWidget(self, uiFileName):
+        """
+        Public slot to load a UI file.
+        
+        @param uiFileName name of the UI file to load (string)
+        """
+        wview = self.__findWidget(uiFileName)
+        if wview is None:
+            name = os.path.basename(uiFileName)
+            if not name:
+                E5MessageBox.warning(
+                    self,
+                    self.tr("Load UI File"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be loaded.</p>""")
+                    .format(uiFileName))
+                return
+            
+            uname = name
+            cnt = 1
+            while self.findChild(WidgetView, uname) is not None:
+                cnt += 1
+                uname = "{0} <{1}>".format(name, cnt)
+            name = uname
+            
+            wview = WidgetView(uiFileName, self, name)
+            wview.buildWidget()
+            if not wview.isValid():
+                del wview
+                return
+            
+            self.rebuildWidgets.connect(wview.buildWidget)
+            wview.installEventFilter(self)
+            
+            win = self.addSubWindow(wview)
+            self.widgets.append(win)
+        
+        wview.showNormal()
+    
+    def eventFilter(self, obj, ev):
+        """
+        Public method called to filter an event.
+        
+        @param obj object, that generated the event (QObject)
+        @param ev the event, that was generated by object (QEvent)
+        @return flag indicating if event was filtered out
+        """
+        if obj in self.widgets and ev.type() == QEvent.Type.Close:
+            with contextlib.suppress(ValueError):
+                self.widgets.remove(obj)
+                if len(self.widgets) == 0:
+                    self.lastWidgetClosed.emit()
+        
+        return QMdiArea.eventFilter(self, obj, ev)
+    
+    def __findWidget(self, uiFileName):
+        """
+        Private method to find a specific widget view.
+        
+        @param uiFileName filename of the loaded UI file (string)
+        @return reference to the widget (WidgetView) or None
+        """
+        wviewList = self.findChildren(WidgetView)
+        if wviewList is None:
+            return None
+        
+        for wview in wviewList:
+            if wview.uiFileName() == uiFileName:
+                return wview
+        
+        return None
+    
+    def closeWidget(self):
+        """
+        Public slot to close the active window.
+        """
+        aw = self.activeSubWindow()
+        if aw is not None:
+            aw.close()
+    
+    def closeAllWidgets(self):
+        """
+        Public slot to close all windows.
+        """
+        for w in self.widgets[:]:
+            w.close()
+    
+    def showWindowMenu(self, windowMenu):
+        """
+        Public method to set up the widgets part of the Window menu.
+        
+        @param windowMenu reference to the window menu
+        """
+        for idx, wid in enumerate(self.widgets):
+            act = windowMenu.addAction(wid.windowTitle())
+            act.setData(idx)
+            act.setCheckable(True)
+            act.setChecked(not wid.isHidden())
+    
+    def toggleSelectedWidget(self, act):
+        """
+        Public method to handle the toggle of a window.
+        
+        @param act reference to the action that triggered (QAction)
+        """
+        idx = act.data()
+        if idx is not None:
+            self.__toggleWidget(self.widgets[idx])
+    
+    def __toggleWidget(self, w):
+        """
+        Private method to toggle a workspace window.
+        
+        @param w window to be toggled
+        """
+        if w.isHidden():
+            w.show()
+        else:
+            w.hide()
+    
+    def hasWidgets(self):
+        """
+        Public method to check for loaded widgets.
+        
+        @return flag signaling if any widget was loaded (boolean)
+        """
+        return len(self.widgets) > 0

eric ide

mercurial