예제 #1
0
class Hello(QMainWindow):
    def __init__(self):
        super(Hello, self).__init__()
        self.initUI()
        self.show()

    def initUI(self):
        widget = self.initSubWindow()

        sub_window = QMdiSubWindow()
        sub_window.setWidget(widget)
        sub_window.setWindowTitle("MdiSubWindow")

        self.mdiArea = QMdiArea()
        self.setCentralWidget(self.mdiArea)
        self.mdiArea.addSubWindow(sub_window)
        self.setWindowTitle("MdiArea")

    def initSubWindow(self):
        label: QLabel = QLabel('こんにちは、世界!')
        font: QFont = QFont()
        font.setPointSize(24)
        label.setFont(font)
        label.setAlignment(QtCore.Qt.AlignCenter)
        layout: QVBoxLayout = QVBoxLayout()
        layout.addWidget(label)
        widget = QWidget()
        widget.setLayout(layout)
        return widget
예제 #2
0
class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()

        self.setWindowTitle("Pyside2 Simple Application")
        self.setGeometry(300, 300, 300, 300)
        self.mdi = QMdiArea()
        self.setCentralWidget(self.mdi)
        menu_bar = self.menuBar()
        file = menu_bar.addMenu("File")
        new = QAction("New", self)
        new.triggered.connect(self.newSlot)
        file.addAction(new)
        cascade = QAction("Cascade", self)
        cascade.triggered.connect(self.cascadeAction)
        file.addAction(cascade)
        tiled = QAction("Tiled", self)
        tiled.triggered.connect(self.tileAction)
        file.addAction(tiled)
        self.count = 0

    def newSlot(self):
        self.count += 1
        sub = QMdiSubWindow()
        sub.setWidget(QTextEdit())
        sub.setWindowTitle("Sub Window " + str(self.count))
        self.mdi.addSubWindow(sub)
        sub.show()

    def cascadeAction(self):
        self.mdi.cascadeSubWindows()

    def tileAction(self):
        self.mdi.tileSubWindows()
예제 #3
0
    def __init__(self, main_controller: "MainController",
                 camera_description: CamDesc, mdi_area: QtWidgets.QMdiArea):
        self.main_controller = main_controller
        self.camera_description = camera_description
        self.address_str = camera_description.host.toString()

        self.sub_window = sub_window = MdiSubWindowCloser()
        sub_window.setWindowTitle(self.address_str)
        sub_window.setAttribute(Qt.WA_DeleteOnClose)
        sub_window.closed.connect(self.window_closed)
        camera_control = CameraControl(parent=sub_window)

        self.status = camera_control.status

        for cmd in ZOOM_COMMANDS:
            control = getattr(camera_control, cmd)
            cmd_name = cmd.replace("_", "-")
            control.pressed.connect(
                partial(self.zoom_command_pressed, cmd_name))
            control.released.connect(self.zoom_command_released)

        sub_window.setWidget(camera_control)
        mdi_area.addSubWindow(sub_window)
        sub_window.show()

        self.base_url = QUrl(f"http://{self.address_str}/cam.cgi")
        self.base_request = QNetworkRequest()
        self.base_request.setRawHeader(QByteArray(b"Connection"),
                                       QByteArray(b"Keep-Alive"))
        self.base_request.setRawHeader(QByteArray(b"User-Agent"),
                                       QByteArray(b"Apache-HttpClient"))

        self.status_query_request = self.build_request({"mode": "getstate"})
        self.stop_zoom_request = self.build_request({
            "mode": "camcmd",
            "value": "zoomstop"
        })

        self.ping_timer = QTimer(self.sub_window)
        self.ping_timer.timeout.connect(self.request_device_state)

        self.initial_requests = Queue()
        self.build_initial_request_list()

        request = self.get_next_request()
        self.query(request)
예제 #4
0
    def __init__(self, close_event_callback: Event):
        super(MainWindow, self).__init__()
        self.close_event_callback = close_event_callback
        self.resize(700, 900)
        # main_wgt
        main_wgt = QWidget()
        self.setCentralWidget(main_wgt)
        self.setWindowTitle("RS Companion")
        # main_window/main_wgt <- main_grid

        grid = QGridLayout()
        main_wgt.setLayout(grid)

        # mdi
        self.mdi_win = mdi_win.MDIWidget()
        mdi = QMdiArea()

        # slider
        slider = QSlider()
        slider.setOrientation(Qt.Horizontal)

        # main_window/main_wgt-main_grid <- gb_control_bar
        grid.addWidget(self.set_controls(), 0, 0)
        grid.addWidget(self.set_key_flag(), 0, 1)
        grid.addWidget(self.set_notes(), 0, 2)
        grid.addWidget(self.set_information(), 0, 4)
        grid.addItem(
            QSpacerItem(300, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 0,
            3)
        grid.addWidget(mdi, 1, 0, 1, 5)
        grid.addWidget(slider, 2, 0, 1, 5)
        grid.setRowStretch(1, 1)

        # menu
        self.set_menu()

        # QMDI subwindow
        # sub = QMdiSubWindow()
        # sub.resize(655, 515)
        # self.mid_text = QLabel("Nothing yet...")
        # self.mid_text.setText("what??")
        # sub.setWidget(self.mid_text)
        # sub.setWindowTitle("VOG COM32")
        # sub.setWindowIcon(self.create_icon_by_color(QColor("transparent")))

        mdi.addSubWindow(self.mdi_win)
예제 #5
0
class Window(QMainWindow):

    count = 0

    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("Pyside2 MDI Window")
        self.setGeometry(100, 100, 900, 500)
        self.setWindowIcon(QIcon('icon.png'))

        self.mdi = QMdiArea()
        self.setCentralWidget(self.mdi)

        menu_bar = self.menuBar()

        file = menu_bar.addMenu("File")
        file.addAction("New")
        file.addAction("Cascade")
        file.addAction("Tiled")

        file.triggered[QAction].connect(self.windowTriggered)

    def windowTriggered(self, p):
        if p.text() == "New":
            Window.count = Window.count + 1
            sub = QMdiSubWindow()
            sub.setWidget(QTextEdit())
            sub.setWindowTitle("Sub Window " + str(Window.count))
            self.mdi.addSubWindow(sub)
            sub.show()

        if p.text() == "Cascade":
            self.mdi.cascadeSubWindows()

        if p.text() == "Tiled":
            self.mdi.tileSubWindows()
예제 #6
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.mdiArea = QMdiArea()
        self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setCentralWidget(self.mdiArea)

        self.mdiArea.subWindowActivated.connect(self.updateMenus)
        self.windowMapper = QSignalMapper(self)
        self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow)

        self.createActions()
        self.createMenus()
        self.createToolBars()
        self.createStatusBar()
        self.updateMenus()

        self.readSettings()

        self.setWindowTitle("MDI")

    def closeEvent(self, event):
        self.mdiArea.closeAllSubWindows()
        if self.mdiArea.currentSubWindow():
            event.ignore()
        else:
            self.writeSettings()
            event.accept()

    def newFile(self):
        child = self.createMdiChild()
        child.newFile()
        child.show()

    def open(self):
        fileName, _ = QFileDialog.getOpenFileName(self)
        if fileName:
            existing = self.findMdiChild(fileName)
            if existing:
                self.mdiArea.setActiveSubWindow(existing)
                return

            child = self.createMdiChild()
            if child.loadFile(fileName):
                self.statusBar().showMessage("File loaded", 2000)
                child.show()
            else:
                child.close()

    def save(self):
        if self.activeMdiChild() and self.activeMdiChild().save():
            self.statusBar().showMessage("File saved", 2000)

    def saveAs(self):
        if self.activeMdiChild() and self.activeMdiChild().saveAs():
            self.statusBar().showMessage("File saved", 2000)

    def cut(self):
        if self.activeMdiChild():
            self.activeMdiChild().cut()

    def copy(self):
        if self.activeMdiChild():
            self.activeMdiChild().copy()

    def paste(self):
        if self.activeMdiChild():
            self.activeMdiChild().paste()

    def about(self):
        QMessageBox.about(
            self, "About MDI",
            "The <b>MDI</b> example demonstrates how to write multiple "
            "document interface applications using Qt.")

    def updateMenus(self):
        hasMdiChild = (self.activeMdiChild() is not None)
        self.saveAct.setEnabled(hasMdiChild)
        self.saveAsAct.setEnabled(hasMdiChild)
        self.pasteAct.setEnabled(hasMdiChild)
        self.closeAct.setEnabled(hasMdiChild)
        self.closeAllAct.setEnabled(hasMdiChild)
        self.tileAct.setEnabled(hasMdiChild)
        self.cascadeAct.setEnabled(hasMdiChild)
        self.nextAct.setEnabled(hasMdiChild)
        self.previousAct.setEnabled(hasMdiChild)
        self.separatorAct.setVisible(hasMdiChild)

        hasSelection = (self.activeMdiChild() is not None
                        and self.activeMdiChild().textCursor().hasSelection())
        self.cutAct.setEnabled(hasSelection)
        self.copyAct.setEnabled(hasSelection)

    def updateWindowMenu(self):
        self.windowMenu.clear()
        self.windowMenu.addAction(self.closeAct)
        self.windowMenu.addAction(self.closeAllAct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction(self.tileAct)
        self.windowMenu.addAction(self.cascadeAct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction(self.nextAct)
        self.windowMenu.addAction(self.previousAct)
        self.windowMenu.addAction(self.separatorAct)

        windows = self.mdiArea.subWindowList()
        self.separatorAct.setVisible(len(windows) != 0)

        for i, window in enumerate(windows):
            child = window.widget()

            text = "%d %s" % (i + 1, child.userFriendlyCurrentFile())
            if i < 9:
                text = '&' + text

            action = self.windowMenu.addAction(text)
            action.setCheckable(True)
            action.setChecked(child is self.activeMdiChild())
            action.triggered.connect(self.windowMapper.map)
            self.windowMapper.setMapping(action, window)

    def createMdiChild(self):
        child = MdiChild()
        self.mdiArea.addSubWindow(child)

        child.copyAvailable.connect(self.cutAct.setEnabled)
        child.copyAvailable.connect(self.copyAct.setEnabled)

        return child

    def createActions(self):

        self.newAct = QAction(QIcon.fromTheme("document-new",
                                              QIcon(':/images/new.png')),
                              "&New",
                              self,
                              shortcut=QKeySequence.New,
                              statusTip="Create a new file",
                              triggered=self.newFile)

        self.openAct = QAction(QIcon.fromTheme("document-open",
                                               QIcon(':/images/open.png')),
                               "&Open...",
                               self,
                               shortcut=QKeySequence.Open,
                               statusTip="Open an existing file",
                               triggered=self.open)

        self.saveAct = QAction(QIcon.fromTheme("document-save",
                                               QIcon(':/images/save.png')),
                               "&Save",
                               self,
                               shortcut=QKeySequence.Save,
                               statusTip="Save the document to disk",
                               triggered=self.save)

        self.saveAsAct = QAction(
            "Save &As...",
            self,
            shortcut=QKeySequence.SaveAs,
            statusTip="Save the document under a new name",
            triggered=self.saveAs)

        self.exitAct = QAction(
            "E&xit",
            self,
            shortcut=QKeySequence.Quit,
            statusTip="Exit the application",
            triggered=QApplication.instance().closeAllWindows)

        self.cutAct = QAction(
            QIcon.fromTheme("edit-cut", QIcon(':/images/cut.png')),
            "Cu&t",
            self,
            shortcut=QKeySequence.Cut,
            statusTip="Cut the current selection's contents to the clipboard",
            triggered=self.cut)

        self.copyAct = QAction(
            QIcon.fromTheme("edit-copy", QIcon(':/images/copy.png')),
            "&Copy",
            self,
            shortcut=QKeySequence.Copy,
            statusTip="Copy the current selection's contents to the clipboard",
            triggered=self.copy)

        self.pasteAct = QAction(
            QIcon.fromTheme("edit-paste", QIcon(':/images/paste.png')),
            "&Paste",
            self,
            shortcut=QKeySequence.Paste,
            statusTip=
            "Paste the clipboard's contents into the current selection",
            triggered=self.paste)

        self.closeAct = QAction("Cl&ose",
                                self,
                                statusTip="Close the active window",
                                triggered=self.mdiArea.closeActiveSubWindow)

        self.closeAllAct = QAction("Close &All",
                                   self,
                                   statusTip="Close all the windows",
                                   triggered=self.mdiArea.closeAllSubWindows)

        self.tileAct = QAction("&Tile",
                               self,
                               statusTip="Tile the windows",
                               triggered=self.mdiArea.tileSubWindows)

        self.cascadeAct = QAction("&Cascade",
                                  self,
                                  statusTip="Cascade the windows",
                                  triggered=self.mdiArea.cascadeSubWindows)

        self.nextAct = QAction("Ne&xt",
                               self,
                               shortcut=QKeySequence.NextChild,
                               statusTip="Move the focus to the next window",
                               triggered=self.mdiArea.activateNextSubWindow)

        self.previousAct = QAction(
            "Pre&vious",
            self,
            shortcut=QKeySequence.PreviousChild,
            statusTip="Move the focus to the previous window",
            triggered=self.mdiArea.activatePreviousSubWindow)

        self.separatorAct = QAction(self)
        self.separatorAct.setSeparator(True)

        self.aboutAct = QAction("&About",
                                self,
                                statusTip="Show the application's About box",
                                triggered=self.about)

        self.aboutQtAct = QAction("About &Qt",
                                  self,
                                  statusTip="Show the Qt library's About box",
                                  triggered=QApplication.instance().aboutQt)

    def createMenus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.newAct)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.saveAct)
        self.fileMenu.addAction(self.saveAsAct)
        self.fileMenu.addSeparator()
        action = self.fileMenu.addAction("Switch layout direction")
        action.triggered.connect(self.switchLayoutDirection)
        self.fileMenu.addAction(self.exitAct)

        self.editMenu = self.menuBar().addMenu("&Edit")
        self.editMenu.addAction(self.cutAct)
        self.editMenu.addAction(self.copyAct)
        self.editMenu.addAction(self.pasteAct)

        self.windowMenu = self.menuBar().addMenu("&Window")
        self.updateWindowMenu()
        self.windowMenu.aboutToShow.connect(self.updateWindowMenu)

        self.menuBar().addSeparator()

        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

    def createToolBars(self):
        self.fileToolBar = self.addToolBar("File")
        self.fileToolBar.addAction(self.newAct)
        self.fileToolBar.addAction(self.openAct)
        self.fileToolBar.addAction(self.saveAct)

        self.editToolBar = self.addToolBar("Edit")
        self.editToolBar.addAction(self.cutAct)
        self.editToolBar.addAction(self.copyAct)
        self.editToolBar.addAction(self.pasteAct)

    def createStatusBar(self):
        self.statusBar().showMessage("Ready")

    def readSettings(self):
        settings = QSettings('Trolltech', 'MDI Example')
        pos = settings.value('pos', QPoint(200, 200))
        size = settings.value('size', QSize(400, 400))
        self.move(pos)
        self.resize(size)

    def writeSettings(self):
        settings = QSettings('Trolltech', 'MDI Example')
        settings.setValue('pos', self.pos())
        settings.setValue('size', self.size())

    def activeMdiChild(self):
        activeSubWindow = self.mdiArea.activeSubWindow()
        if activeSubWindow:
            return activeSubWindow.widget()
        return None

    def findMdiChild(self, fileName):
        canonicalFilePath = QFileInfo(fileName).canonicalFilePath()

        for window in self.mdiArea.subWindowList():
            if window.widget().currentFile() == canonicalFilePath:
                return window
        return None

    def switchLayoutDirection(self):
        if self.layoutDirection() == Qt.LeftToRight:
            QApplication.setLayoutDirection(Qt.RightToLeft)
        else:
            QApplication.setLayoutDirection(Qt.LeftToRight)

    def setActiveSubWindow(self, window):
        if window:
            self.mdiArea.setActiveSubWindow(window)
예제 #7
0
class MainWindow(QMainWindow):
    max_recent = 5

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        QApplication.setApplicationName('Sherloq')
        QApplication.setOrganizationName('Guido Bartoli')
        QApplication.setOrganizationDomain('www.guidobartoli.com')
        QApplication.setApplicationVersion(ToolTree().version)
        QApplication.setWindowIcon(QIcon('icons/sherloq_white.png'))
        self.setWindowTitle('{} {}'.format(QApplication.applicationName(),
                                           QApplication.applicationVersion()))
        self.mdi_area = QMdiArea()
        self.setCentralWidget(self.mdi_area)
        self.filename = None
        self.image = None
        modify_font(self.statusBar(), bold=True)

        tree_dock = QDockWidget(self.tr('TOOLS'), self)
        tree_dock.setObjectName('tree_dock')
        tree_dock.setAllowedAreas(Qt.LeftDockWidgetArea
                                  | Qt.RightDockWidgetArea)
        self.addDockWidget(Qt.LeftDockWidgetArea, tree_dock)
        self.tree_widget = ToolTree()
        self.tree_widget.setObjectName('tree_widget')
        self.tree_widget.itemDoubleClicked.connect(self.open_tool)
        tree_dock.setWidget(self.tree_widget)

        tools_action = tree_dock.toggleViewAction()
        tools_action.setText(self.tr('Show tools'))
        tools_action.setToolTip(self.tr('Toggle toolset visibility'))
        tools_action.setShortcut(QKeySequence(Qt.Key_Tab))
        tools_action.setObjectName('tools_action')
        tools_action.setIcon(QIcon('icons/tools.svg'))

        help_action = QAction(self.tr('Show help'), self)
        help_action.setToolTip(self.tr('Toggle online help'))
        help_action.setShortcut(QKeySequence.HelpContents)
        help_action.setObjectName('help_action')
        help_action.setIcon(QIcon('icons/help.svg'))
        help_action.setCheckable(True)
        help_action.setEnabled(False)

        load_action = QAction(self.tr('&Load image...'), self)
        load_action.setToolTip(self.tr('Load an image to analyze'))
        load_action.setShortcut(QKeySequence.Open)
        load_action.triggered.connect(self.load_file)
        load_action.setObjectName('load_action')
        load_action.setIcon(QIcon('icons/load.svg'))

        quit_action = QAction(self.tr('&Quit'), self)
        quit_action.setToolTip(self.tr('Exit from Sherloq'))
        quit_action.setShortcut(QKeySequence.Quit)
        quit_action.triggered.connect(self.close)
        quit_action.setObjectName('quit_action')
        quit_action.setIcon(QIcon('icons/quit.svg'))

        tabbed_action = QAction(self.tr('&Tabbed'), self)
        tabbed_action.setToolTip(self.tr('Toggle tabbed view for window area'))
        tabbed_action.setShortcut(QKeySequence(Qt.Key_F10))
        tabbed_action.setCheckable(True)
        tabbed_action.triggered.connect(self.toggle_view)
        tabbed_action.setObjectName('tabbed_action')
        tabbed_action.setIcon(QIcon('icons/tabbed.svg'))

        prev_action = QAction(self.tr('&Previous'), self)
        prev_action.setToolTip(self.tr('Select the previous tool window'))
        prev_action.setShortcut(QKeySequence.PreviousChild)
        prev_action.triggered.connect(self.mdi_area.activatePreviousSubWindow)
        prev_action.setObjectName('prev_action')
        prev_action.setIcon(QIcon('icons/previous.svg'))

        next_action = QAction(self.tr('&Next'), self)
        next_action.setToolTip(self.tr('Select the next tool window'))
        next_action.setShortcut(QKeySequence.NextChild)
        next_action.triggered.connect(self.mdi_area.activateNextSubWindow)
        next_action.setObjectName('next_action')
        next_action.setIcon(QIcon('icons/next.svg'))

        tile_action = QAction(self.tr('&Tile'), self)
        tile_action.setToolTip(
            self.tr('Arrange windows into non-overlapping views'))
        tile_action.setShortcut(QKeySequence(Qt.Key_F11))
        tile_action.triggered.connect(self.mdi_area.tileSubWindows)
        tile_action.setObjectName('tile_action')
        tile_action.setIcon(QIcon('icons/tile.svg'))

        cascade_action = QAction(self.tr('&Cascade'), self)
        cascade_action.setToolTip(
            self.tr('Arrange windows into overlapping views'))
        cascade_action.setShortcut(QKeySequence(Qt.Key_F12))
        cascade_action.triggered.connect(self.mdi_area.cascadeSubWindows)
        cascade_action.setObjectName('cascade_action')
        cascade_action.setIcon(QIcon('icons/cascade.svg'))

        close_action = QAction(self.tr('Close &All'), self)
        close_action.setToolTip(self.tr('Close all open tool windows'))
        close_action.setShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_W))
        close_action.triggered.connect(self.mdi_area.closeAllSubWindows)
        close_action.setObjectName('close_action')
        close_action.setIcon(QIcon('icons/close.svg'))

        self.full_action = QAction(self.tr('Full screen'), self)
        self.full_action.setToolTip(self.tr('Switch to full screen mode'))
        self.full_action.setShortcut(QKeySequence.FullScreen)
        self.full_action.triggered.connect(self.change_view)
        self.full_action.setObjectName('full_action')
        self.full_action.setIcon(QIcon('icons/full.svg'))

        self.normal_action = QAction(self.tr('Normal view'), self)
        self.normal_action.setToolTip(self.tr('Back to normal view mode'))
        self.normal_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_F12))
        self.normal_action.triggered.connect(self.change_view)
        self.normal_action.setObjectName('normal_action')
        self.normal_action.setIcon(QIcon('icons/normal.svg'))

        about_action = QAction(self.tr('&About...'), self)
        about_action.setToolTip(self.tr('Information about this program'))
        about_action.triggered.connect(self.show_about)
        about_action.setObjectName('about_action')
        about_action.setIcon(QIcon('icons/sherloq_alpha.png'))

        about_qt_action = QAction(self.tr('About &Qt'), self)
        about_qt_action.setToolTip(
            self.tr('Information about the Qt Framework'))
        about_qt_action.triggered.connect(QApplication.aboutQt)
        about_qt_action.setIcon(QIcon('icons/Qt.svg'))

        file_menu = self.menuBar().addMenu(self.tr('&File'))
        file_menu.addAction(load_action)
        file_menu.addSeparator()
        self.recent_actions = [None] * self.max_recent
        for i in range(len(self.recent_actions)):
            self.recent_actions[i] = QAction(self)
            self.recent_actions[i].setVisible(False)
            self.recent_actions[i].triggered.connect(self.open_recent)
            file_menu.addAction(self.recent_actions[i])
        file_menu.addSeparator()
        file_menu.addAction(quit_action)

        view_menu = self.menuBar().addMenu(self.tr('&View'))
        view_menu.addAction(tools_action)
        view_menu.addAction(help_action)
        view_menu.addSeparator()
        view_menu.addAction(self.full_action)
        view_menu.addAction(self.normal_action)

        window_menu = self.menuBar().addMenu(self.tr('&Window'))
        window_menu.addAction(prev_action)
        window_menu.addAction(next_action)
        window_menu.addSeparator()
        window_menu.addAction(tile_action)
        window_menu.addAction(cascade_action)
        window_menu.addAction(tabbed_action)
        window_menu.addSeparator()
        window_menu.addAction(close_action)

        help_menu = self.menuBar().addMenu(self.tr('&Help'))
        help_menu.addAction(help_action)
        help_menu.addSeparator()
        help_menu.addAction(about_action)
        help_menu.addAction(about_qt_action)

        main_toolbar = self.addToolBar(self.tr('&Toolbar'))
        main_toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        main_toolbar.addAction(load_action)
        main_toolbar.addSeparator()
        main_toolbar.addAction(tools_action)
        main_toolbar.addAction(help_action)
        main_toolbar.addSeparator()
        main_toolbar.addAction(prev_action)
        main_toolbar.addAction(next_action)
        main_toolbar.addSeparator()
        main_toolbar.addAction(tile_action)
        main_toolbar.addAction(cascade_action)
        main_toolbar.addAction(tabbed_action)
        main_toolbar.addAction(close_action)
        # main_toolbar.addSeparator()
        # main_toolbar.addAction(self.normal_action)
        # main_toolbar.addAction(self.full_action)
        main_toolbar.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        main_toolbar.setObjectName('main_toolbar')

        settings = QSettings()
        settings.beginGroup('main_window')
        self.restoreGeometry(settings.value('geometry'))
        self.restoreState(settings.value('state'))
        self.recent_files = settings.value('recent_files')
        if self.recent_files is None:
            self.recent_files = []
        elif not isinstance(self.recent_files, list):
            self.recent_files = [self.recent_files]
        self.update_recent()
        settings.endGroup()

        prev_action.setEnabled(False)
        next_action.setEnabled(False)
        tile_action.setEnabled(False)
        cascade_action.setEnabled(False)
        close_action.setEnabled(False)
        tabbed_action.setEnabled(False)
        self.tree_widget.setEnabled(False)
        self.showNormal()
        self.normal_action.setEnabled(False)
        self.show_message(self.tr('Ready'))

    def change_view(self):
        if self.isFullScreen():
            self.showNormal()
            self.showMaximized()
            self.full_action.setEnabled(True)
            self.normal_action.setEnabled(False)
        else:
            self.showFullScreen()
            self.full_action.setEnabled(False)
            self.normal_action.setEnabled(True)

    def closeEvent(self, event):
        settings = QSettings()
        settings.beginGroup('main_window')
        settings.setValue('geometry', self.saveGeometry())
        settings.setValue('state', self.saveState())
        settings.setValue('recent_files', self.recent_files)
        settings.endGroup()
        super(MainWindow, self).closeEvent(event)

    def update_recent(self):
        if not self.recent_files:
            return
        self.recent_files = [f for f in self.recent_files if os.path.isfile(f)]
        for i in range(len(self.recent_actions)):
            if i < len(self.recent_files):
                text = '&{} {}'.format(i + 1,
                                       os.path.basename(self.recent_files[i]))
                self.recent_actions[i].setText(text)
                self.recent_actions[i].setData(self.recent_files[i])
                self.recent_actions[i].setVisible(True)
            else:
                self.recent_actions[i].setVisible(False)

    def open_recent(self):
        action = self.sender()
        if action:
            filename, basename, image = load_image(self, action.data())
            self.initialize(filename, basename, image)

    def initialize(self, filename, basename, image):
        self.filename = filename
        self.image = image
        self.findChild(ToolTree, 'tree_widget').setEnabled(True)
        self.findChild(QAction, 'prev_action').setEnabled(True)
        self.findChild(QAction, 'next_action').setEnabled(True)
        self.findChild(QAction, 'tile_action').setEnabled(True)
        self.findChild(QAction, 'cascade_action').setEnabled(True)
        self.findChild(QAction, 'close_action').setEnabled(True)
        self.findChild(QAction, 'tabbed_action').setEnabled(True)
        self.setWindowTitle('[{}] - {} {}'.format(
            basename, QApplication.applicationName(),
            QApplication.applicationVersion()))
        if filename not in self.recent_files:
            self.recent_files.insert(0, filename)
            if len(self.recent_files) > self.max_recent:
                self.recent_files = self.recent_files[:self.max_recent]
            self.update_recent()
        self.show_message(
            self.tr('Image "{}" successfully loaded'.format(basename)))

        # FIXME: disable_bold della chiusura viene chiamato DOPO open_tool e nell'albero la voce NON diventa neretto
        self.mdi_area.closeAllSubWindows()
        self.open_tool(self.tree_widget.topLevelItem(0).child(0), None)

    def load_file(self):
        filename, basename, image = load_image(self)
        if filename is None:
            return
        self.initialize(filename, basename, image)

    def open_tool(self, item, _):
        if not item.data(0, Qt.UserRole):
            return
        group = item.data(0, Qt.UserRole + 1)
        tool = item.data(0, Qt.UserRole + 2)
        for sub_window in self.mdi_area.subWindowList():
            if sub_window.windowTitle() == item.text(0):
                sub_window.setWindowState(Qt.WindowActive)
                sub_window.setFocus()
                return

        if group == 0:
            if tool == 0:
                tool_widget = OriginalWidget(self.image)
            elif tool == 1:
                tool_widget = DigestWidget(self.filename, self.image)
            elif tool == 2:
                tool_widget = EditorWidget()
            elif tool == 3:
                tool_widget = ReverseWidget()
            else:
                return
        elif group == 1:
            if tool == 0:
                tool_widget = HeaderWidget(self.filename)
            elif tool == 1:
                tool_widget = ExifWidget(self.filename)
            elif tool == 2:
                tool_widget = ThumbWidget(self.filename, self.image)
            elif tool == 3:
                tool_widget = LocationWidget(self.filename)
            else:
                return
        elif group == 2:
            if tool == 0:
                tool_widget = MagnifierWidget(self.image)
            elif tool == 1:
                tool_widget = HistWidget(self.image)
            elif tool == 2:
                tool_widget = AdjustWidget(self.image)
            elif tool == 3:
                tool_widget = ComparisonWidget(self.filename, self.image)
            else:
                return
        elif group == 3:
            if tool == 0:
                tool_widget = GradientWidget(self.image)
            elif tool == 1:
                tool_widget = EchoWidget(self.image)
            elif tool == 2:
                tool_widget = WaveletWidget(self.image)
            else:
                return
        elif group == 4:
            if tool == 0:
                tool_widget = PlotsWidget(self.image)
            elif tool == 1:
                tool_widget = SpaceWidget(self.image)
            elif tool == 2:
                tool_widget = PcaWidget(self.image)
            elif tool == 3:
                tool_widget = StatsWidget(self.image)
            else:
                return
        elif group == 5:
            if tool == 0:
                tool_widget = NoiseWidget(self.image)
            elif tool == 1:
                tool_widget = MinMaxWidget(self.image)
            elif tool == 2:
                tool_widget = FrequencyWidget(self.image)
            elif tool == 3:
                tool_widget = PlanesWidget(self.image)
            else:
                return
        elif group == 6:
            if tool == 0:
                tool_widget = ElaWidget(self.image)
            elif tool == 1:
                tool_widget = QualityWidget(self.filename)
            elif tool == 2:
                tool_widget = MultipleWidget(self.image)
            else:
                return
        elif group == 7:
            if tool == 0:
                tool_widget = ContrastWidget(self.image)
            elif tool == 1:
                tool_widget = CloningWidget(self.image)
            elif tool == 2:
                # tool_widget = ResamplingWidget(self.image)
                pass
            else:
                return
        elif group == 8:
            if tool == 3:
                tool_widget = StereoWidget(self.image)
            else:
                return
        else:
            return

        # FIXME: Aggiungere un metodo init e dopo fare il connect, sennò i messaggi inviati nel costruttore non si vedono
        tool_widget.info_message.connect(self.show_message)

        sub_window = QMdiSubWindow()
        sub_window.setWidget(tool_widget)
        sub_window.setWindowTitle(item.text(0))
        sub_window.setObjectName(item.text(0))
        sub_window.setAttribute(Qt.WA_DeleteOnClose)
        sub_window.setWindowIcon(QIcon('icons/{}.svg'.format(group)))
        self.mdi_area.addSubWindow(sub_window)
        sub_window.show()
        sub_window.destroyed.connect(self.disable_bold)
        self.tree_widget.set_bold(item.text(0), enabled=True)

    def disable_bold(self, item):
        self.tree_widget.set_bold(item.windowTitle(), enabled=False)

    def toggle_view(self, tabbed):
        if tabbed:
            self.mdi_area.setViewMode(QMdiArea.TabbedView)
            self.mdi_area.setTabsClosable(True)
            self.mdi_area.setTabsMovable(True)
        else:
            self.mdi_area.setViewMode(QMdiArea.SubWindowView)
        self.findChild(QAction, 'tile_action').setEnabled(not tabbed)
        self.findChild(QAction, 'cascade_action').setEnabled(not tabbed)

    def show_about(self):
        message = '<h2>{} {}</h2>'.format(QApplication.applicationName(),
                                          QApplication.applicationVersion())
        message += '<h3>A digital image forensic toolkit</h3>'
        message += '<p>author: <a href="{}">{}</a></p>'.format(
            QApplication.organizationDomain(), QApplication.organizationName())
        message += '<p>source: <a href="https://github.com/GuidoBartoli/sherloq">GitHub repository</a></p>'
        QMessageBox.about(self, self.tr('About'), message)

    def show_message(self, message):
        self.statusBar().showMessage(message, 10000)
예제 #8
0
class MainWindow(QMainWindow):
    """
    Main Window service for the nexxT frameworks. Other services usually create dock windows, filters use the
    subplot functionality to create grid-layouted views.
    """
    mdiSubWindowCreated = Signal(
        QMdiSubWindow
    )  # TODO: remove, is not necessary anymore with subplot feature
    aboutToClose = Signal(object)

    def __init__(self, config):
        super().__init__()
        self.config = config
        self.config.appActivated.connect(self._appActivated)
        self.mdi = QMdiArea(self)
        self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setCentralWidget(self.mdi)
        self.menu = self.menuBar().addMenu("&Windows")
        self.aboutMenu = QMenuBar(self)
        self.menuBar().setCornerWidget(self.aboutMenu)
        m = self.aboutMenu.addMenu("&About")
        self.aboutNexxT = QAction("About nexxT ...")
        self.aboutQt = QAction("About Qt ...")
        self.aboutPython = QAction("About Python ...")
        m.addActions([self.aboutNexxT, self.aboutQt, self.aboutPython])
        self.aboutNexxT.triggered.connect(lambda: QMessageBox.about(
            self, "About nexxT", """\
This program uses <b>nexxT</b> %(version)s, a generic hybrid python/c++ framework for developing computer vision 
algorithms.<br><br>

nexxT is available under the <a href='https://github.com/ifm/nexxT/blob/master/LICENSE'>Apache 2.0 License</a> together 
with the <a href='https://github.com/ifm/nexxT/blob/master/NOTICE'>notice</a>. 
""" % dict(version=nexxT.__version__)))
        self.aboutQt.triggered.connect(lambda: QMessageBox.aboutQt(self))
        self.aboutPython.triggered.connect(self._aboutPython)
        self.toolbar = None
        self.managedMdiWindows = []
        self.managedSubplots = {}
        self.windows = {}
        self.activeApp = None
        self._ignoreCloseEvent = False

    def closeEvent(self, closeEvent):
        """
        Override from QMainWindow, saves the state.

        :param closeEvent: a QCloseEvent instance
        :return:
        """
        self._ignoreCloseEvent = False
        self.aboutToClose.emit(self)
        if self._ignoreCloseEvent:
            logger.info("Ignoring event")
            closeEvent.ignore()
            return
        closeEvent.accept()
        self.saveState()
        self.saveMdiState()
        super().closeEvent(closeEvent)

    def ignoreCloseEvent(self):
        """
        Can be called in slots connected to aboutToClose for requesting to ignore the event.
        Use case is the "There are unsaved changes" dialog.

        :return:
        """
        self._ignoreCloseEvent = True

    def restoreState(self):
        """
        restores the state of the main window including the dock windows of Services

        :return:
        """
        logger.info("restoring main window's state")
        settings = QSettings()
        v = settings.value("MainWindowState")
        if v is not None:
            super().restoreState(v)
        v = settings.value("MainWindowGeometry")
        if v is not None:
            self.restoreGeometry(v)
        if self.toolbar is not None:
            # TODO: add toolbar to windows menu, so we don't need this
            self.toolbar.show()

    def saveState(self):
        """
        saves the state of the main window including the dock windows of Services

        :return:
        """
        logger.info("saving main window's state")
        settings = QSettings()
        settings.setValue("MainWindowState", super().saveState())
        settings.setValue("MainWindowGeometry", self.saveGeometry())

    def saveMdiState(self):
        """
        saves the state of the individual MDI windows

        :return:
        """
        for i in self.managedMdiWindows:
            window = i["window"]
            propColl = i["propColl"]
            prefix = i["prefix"]
            logger.debug("save window geometry %s: %s", prefix,
                         window.geometry())
            geom = str(window.saveGeometry().toBase64(), "ascii")
            visible = self.windows[shiboken2.getCppPointer(window)
                                   [0]].isChecked()  # pylint: disable=no-member
            propColl.setProperty(prefix + "_geom", geom)
            logger.debug("%s is visible: %d", prefix, int(visible))
            propColl.setProperty(prefix + "_visible", int(visible))
        self.managedMdiWindows = []

    def __del__(self):
        logging.getLogger(__name__).debug("deleting MainWindow")

    @Slot()
    def getToolBar(self):
        """
        Get the main toolbar (adds seperators as appropriate).

        :return:
        """
        if self.toolbar is None:
            self.toolbar = self.addToolBar("NexxT")
            self.toolbar.setObjectName("NexxT_main_toolbar")
        else:
            self.toolbar.addSeparator()
        return self.toolbar

    @Slot(str, QObject, int, int)
    def newDockWidget(self,
                      name,
                      parent,
                      defaultArea,
                      allowedArea=Qt.LeftDockWidgetArea
                      | Qt.BottomDockWidgetArea,
                      defaultLoc=None):
        """
        This function is supposed to be called by services

        :param name: the name of the dock window
        :param parent: the parent (usually None)
        :param defaultArea: the default dock area
        :param allowedArea: the allowed dock areas
        :return: a new QDockWindow instance
        """
        res = NexxTDockWidget(name, parent if parent is not None else self)
        res.setAllowedAreas(allowedArea)
        res.setAttribute(Qt.WA_DeleteOnClose, False)
        self.addDockWidget(defaultArea, res)
        self._registerWindow(res, res.objectNameChanged)
        res.setObjectName(name)
        if defaultLoc is not None:
            dl = self.findChild(QDockWidget, defaultLoc)
            if dl is not None:
                self.tabifyDockWidget(dl, res)
        return res

    @staticmethod
    def parseWindowId(windowId):
        """
        convers a subplot window id into windowTitle, row and column

        :param windowId: the window id
        :return: title, row, column
        """
        regexp = re.compile(r"([^\[]+)\[(\d+),\s*(\d+)\]")
        match = regexp.match(windowId)
        if not match is None:
            return match.group(1), int(match.group(2)), int(match.group(3))
        return windowId, 0, 0

    @Slot(str, QObject, QWidget)
    def subplot(self, windowId, theFilter, widget):
        """
        Adds widget to the GridLayout specified by windowId.

        :param windowId: a string with the format "<windowTitle>[<row>,<col>]" where <windowTitle> is the caption
                         of the MDI window (and it is used as identifier for saving/restoring window state) and
                         <row>, <col> are the coordinates of the addressed subplots (starting at 0)
        :param theFilter: a Filter instance which is requesting the subplot
        :param widget:   a QWidget which shall be placed into the grid layout. Note that this widget is reparented
                         as a result of this operation and the parents can be used to get access to the MDI sub window.
                         Use releaseSubplot to remove the window
        :return: None
        """
        logger.internal("subplot '%s'", windowId)
        title, row, col = self.parseWindowId(windowId)
        if title == "":
            title = "(view)"
        if title in self.managedSubplots and (
                row, col) in self.managedSubplots[title]["plots"]:
            logger.warning(
                "subplot %s[%d,%d] is already registered. Creating a new window for the plot.",
                title, row, col)
            i = 2
            while "%s(%d)" % (title, i) in self.managedSubplots:
                i += 1
            title = "%s(%d)" % (title, i)
            row = 0
            col = 0
        if title not in self.managedSubplots:
            subWindow = self._newMdiSubWindow(theFilter, title)
            swwidget = QWidget()
            subWindow.setWidget(swwidget)
            layout = QGridLayout(swwidget)
            swwidget.setLayout(layout)
            self.managedSubplots[title] = dict(mdiSubWindow=subWindow,
                                               layout=layout,
                                               swwidget=swwidget,
                                               plots={})
        self.managedSubplots[title]["layout"].addWidget(widget, row, col)
        self.managedSubplots[title]["mdiSubWindow"].updateGeometry()
        widget.setParent(self.managedSubplots[title]["swwidget"])
        QTimer.singleShot(
            0, lambda:
            (self.managedSubplots[title]["mdiSubWindow"].adjustSize()
             if widget.parent().size().height() < widget.minimumSizeHint().
             height() or widget.parent().size().height() < widget.minimumSize(
             ).height() else None))
        self.managedSubplots[title]["plots"][row, col] = widget

    @Slot(QWidget)
    @Slot(str)
    def releaseSubplot(self, arg):
        """
        This needs to be called to release the previously allocated subplot called windowId.
        The managed widget is deleted as a consequence of this function.

        :param arg: the widget as passed to subplot. Passing the windowId is also supported, but deprecated.
        :return:
        """
        if isinstance(arg, str):
            windowId = arg
            logger.warning(
                "Using deprecated API to release a subplot. Please pass the widget instead of the windowId."
            )
            logger.internal("releaseSubplot '%s'", windowId)
            title, row, col = self.parseWindowId(windowId)
            if title not in self.managedSubplots or (
                    row, col) not in self.managedSubplots[title]["plots"]:
                logger.warning("releasSubplot: cannot find %s", windowId)
                return
            widget = self.managedSubplots[title]["plots"][row, col]
        elif isinstance(arg, QWidget):
            widget = arg
            found = False
            for title in self.managedSubplots:
                for row, col in self.managedSubplots[title]["plots"]:
                    if self.managedSubplots[title]["plots"][row,
                                                            col] is widget:
                        found = True
                        break
                if found:
                    break
            if not found:
                raise RuntimeError(
                    "cannot find widget given for releaseSubplot.")
        else:
            raise RuntimeError(
                "arg of releaseSubplot must be either a string or a QWidget instance."
            )
        self.managedSubplots[title]["layout"].removeWidget(widget)
        self.managedSubplots[title]["plots"][row, col].deleteLater()
        del self.managedSubplots[title]["plots"][row, col]
        if len(self.managedSubplots[title]["plots"]) == 0:
            self.managedSubplots[title]["layout"].deleteLater()
            self.managedSubplots[title]["swwidget"].deleteLater()
            self.managedSubplots[title]["mdiSubWindow"].deleteLater()
            del self.managedSubplots[title]

    @Slot(QObject)
    @Slot(QObject, str)
    def newMdiSubWindow(self, filterOrService, windowTitle=None):
        """
        Deprectated (use subplot(...) instead): This function is supposed to be called by filters.

        :param filterOrService: a Filter instance
        :param windowTitle: the title of the window (might be None)
        :return: a new QMdiSubWindow instance
        """
        logger.warning(
            "This function is deprecated. Use subplot function instead.")
        return self._newMdiSubWindow(filterOrService, windowTitle)

    def _newMdiSubWindow(self, filterOrService, windowTitle):
        res = NexxTMdiSubWindow(None)
        res.setAttribute(Qt.WA_DeleteOnClose, False)
        self.mdi.addSubWindow(res)
        self._registerWindow(res, res.windowTitleChanged)
        if isinstance(filterOrService, Filter):
            propColl = filterOrService.guiState()
            res.setWindowTitle(
                propColl.objectName() if windowTitle is None else windowTitle)
        else:
            app = Application.activeApplication.getApplication()
            propColl = app.guiState("services/MainWindow")
            res.setWindowTitle(
                "<unnamed>" if windowTitle is None else windowTitle)
        prefix = re.sub(r'[^A-Za-z_0-9]', '_',
                        "MainWindow_MDI_" + res.windowTitle())
        i = dict(window=res, propColl=propColl, prefix=prefix)
        self.managedMdiWindows.append(i)
        window = i["window"]
        propColl = i["propColl"]
        prefix = i["prefix"]
        propColl.defineProperty(prefix + "_geom", "", "Geometry of MDI window")
        b = QByteArray.fromBase64(
            bytes(propColl.getProperty(prefix + "_geom"), "ascii"))
        window.restoreGeometry(b)
        logger.debug("restored geometry %s:%s (%s)", prefix, window.geometry(),
                     b)
        propColl.defineProperty(prefix + "_visible", 1,
                                "Visibility of MDI window")
        if propColl.getProperty(prefix + "_visible"):
            window.show()
        else:
            window.hide()
        self.mdiSubWindowCreated.emit(res)
        return res

    def _registerWindow(self, window, nameChangedSignal):
        act = QAction("<unnamed>", self)
        act.setCheckable(True)
        act.toggled.connect(window.setVisible)
        window.visibleChanged.connect(act.setChecked)
        nameChangedSignal.connect(act.setText)
        self.windows[shiboken2.getCppPointer(window)[0]] = act  # pylint: disable=no-member
        self.menu.addAction(act)
        logger.debug("Registering window %s, new len=%d",
                     shiboken2.getCppPointer(window), len(self.windows))  # pylint: disable=no-member
        window.destroyed.connect(self._windowDestroyed)

    def _windowDestroyed(self, obj):
        logger.internal("_windowDestroyed")
        ptr = shiboken2.getCppPointer(obj)  # pylint: disable=no-member
        try:
            ptr = ptr[0]
        except TypeError:
            pass
        logger.debug("Deregistering window %s, old len=%d", ptr,
                     len(self.windows))
        self.windows[ptr].deleteLater()
        del self.windows[ptr]
        logger.debug("Deregistering window %s done", ptr)

    def _appActivated(self, name, app):
        if app is not None:
            self.activeApp = name
            app.aboutToClose.connect(self.saveMdiState, Qt.UniqueConnection)
        else:
            self.activeApp = None

    def _aboutPython(self):
        piplic = subprocess.check_output(
            [sys.executable, "-m", "piplicenses", "--format=plain"],
            encoding="utf-8").replace("\n", "<br>").replace(" ", "&nbsp;")
        QMessageBox.about(
            self, "About python", """\
This program uses <b>python</b> %(version)s and the following installed python packages.<br><br>
<pre>
%(table)s
</pre>
""" % dict(version=sys.version, table=piplic))
예제 #9
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.mdiArea = QMdiArea()
        self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setCentralWidget(self.mdiArea)

        self.mdiArea.subWindowActivated.connect(self.updateMenus)
        self.windowMapper = QSignalMapper(self)
        self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow)

        self.createActions()
        self.createMenus()
        self.createToolBars()
        self.createStatusBar()
        self.updateMenus()

        self.readSettings()

        self.setWindowTitle("MDI")

    def closeEvent(self, event):
        self.mdiArea.closeAllSubWindows()
        if self.mdiArea.currentSubWindow():
            event.ignore()
        else:
            self.writeSettings()
            event.accept()

    def newFile(self):
        child = self.createMdiChild()
        child.newFile()
        child.show()

    def open(self):
        fileName, _ = QFileDialog.getOpenFileName(self)
        if fileName:
            existing = self.findMdiChild(fileName)
            if existing:
                self.mdiArea.setActiveSubWindow(existing)
                return

            child = self.createMdiChild()
            if child.loadFile(fileName):
                self.statusBar().showMessage("File loaded", 2000)
                child.show()
            else:
                child.close()

    def save(self):
        if self.activeMdiChild() and self.activeMdiChild().save():
            self.statusBar().showMessage("File saved", 2000)

    def saveAs(self):
        if self.activeMdiChild() and self.activeMdiChild().saveAs():
            self.statusBar().showMessage("File saved", 2000)

    def cut(self):
        if self.activeMdiChild():
            self.activeMdiChild().cut()

    def copy(self):
        if self.activeMdiChild():
            self.activeMdiChild().copy()

    def paste(self):
        if self.activeMdiChild():
            self.activeMdiChild().paste()

    def about(self):
        QMessageBox.about(self, "About MDI",
                "The <b>MDI</b> example demonstrates how to write multiple "
                "document interface applications using Qt.")

    def updateMenus(self):
        hasMdiChild = (self.activeMdiChild() is not None)
        self.saveAct.setEnabled(hasMdiChild)
        self.saveAsAct.setEnabled(hasMdiChild)
        self.pasteAct.setEnabled(hasMdiChild)
        self.closeAct.setEnabled(hasMdiChild)
        self.closeAllAct.setEnabled(hasMdiChild)
        self.tileAct.setEnabled(hasMdiChild)
        self.cascadeAct.setEnabled(hasMdiChild)
        self.nextAct.setEnabled(hasMdiChild)
        self.previousAct.setEnabled(hasMdiChild)
        self.separatorAct.setVisible(hasMdiChild)

        hasSelection = (self.activeMdiChild() is not None and
                        self.activeMdiChild().textCursor().hasSelection())
        self.cutAct.setEnabled(hasSelection)
        self.copyAct.setEnabled(hasSelection)

    def updateWindowMenu(self):
        self.windowMenu.clear()
        self.windowMenu.addAction(self.closeAct)
        self.windowMenu.addAction(self.closeAllAct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction(self.tileAct)
        self.windowMenu.addAction(self.cascadeAct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction(self.nextAct)
        self.windowMenu.addAction(self.previousAct)
        self.windowMenu.addAction(self.separatorAct)

        windows = self.mdiArea.subWindowList()
        self.separatorAct.setVisible(len(windows) != 0)

        for i, window in enumerate(windows):
            child = window.widget()

            text = "%d %s" % (i + 1, child.userFriendlyCurrentFile())
            if i < 9:
                text = '&' + text

            action = self.windowMenu.addAction(text)
            action.setCheckable(True)
            action.setChecked(child is self.activeMdiChild())
            action.triggered.connect(self.windowMapper.map)
            self.windowMapper.setMapping(action, window)

    def createMdiChild(self):
        child = MdiChild()
        self.mdiArea.addSubWindow(child)

        child.copyAvailable.connect(self.cutAct.setEnabled)
        child.copyAvailable.connect(self.copyAct.setEnabled)

        return child

    def createActions(self):

        self.newAct = QAction(QIcon.fromTheme("document-new", QIcon(':/images/new.png')), "&New", self,
                shortcut=QKeySequence.New, statusTip="Create a new file",
                triggered=self.newFile)

        self.openAct = QAction(QIcon.fromTheme("document-open", QIcon(':/images/open.png')), "&Open...", self,
                shortcut=QKeySequence.Open, statusTip="Open an existing file",
                triggered=self.open)

        self.saveAct = QAction(QIcon.fromTheme("document-save", QIcon(':/images/save.png')), "&Save", self,
                shortcut=QKeySequence.Save,
                statusTip="Save the document to disk", triggered=self.save)

        self.saveAsAct = QAction("Save &As...", self,
                shortcut=QKeySequence.SaveAs,
                statusTip="Save the document under a new name",
                triggered=self.saveAs)

        self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit,
                statusTip="Exit the application",
                triggered=QApplication.instance().closeAllWindows)

        self.cutAct = QAction(QIcon.fromTheme("edit-cut", QIcon(':/images/cut.png')), "Cu&t", self,
                shortcut=QKeySequence.Cut,
                statusTip="Cut the current selection's contents to the clipboard",
                triggered=self.cut)

        self.copyAct = QAction(QIcon.fromTheme("edit-copy", QIcon(':/images/copy.png')), "&Copy", self,
                shortcut=QKeySequence.Copy,
                statusTip="Copy the current selection's contents to the clipboard",
                triggered=self.copy)

        self.pasteAct = QAction(QIcon.fromTheme("edit-paste", QIcon(':/images/paste.png')), "&Paste", self,
                shortcut=QKeySequence.Paste,
                statusTip="Paste the clipboard's contents into the current selection",
                triggered=self.paste)

        self.closeAct = QAction("Cl&ose", self,
                statusTip="Close the active window",
                triggered=self.mdiArea.closeActiveSubWindow)

        self.closeAllAct = QAction("Close &All", self,
                statusTip="Close all the windows",
                triggered=self.mdiArea.closeAllSubWindows)

        self.tileAct = QAction("&Tile", self, statusTip="Tile the windows",
                triggered=self.mdiArea.tileSubWindows)

        self.cascadeAct = QAction("&Cascade", self,
                statusTip="Cascade the windows",
                triggered=self.mdiArea.cascadeSubWindows)

        self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild,
                statusTip="Move the focus to the next window",
                triggered=self.mdiArea.activateNextSubWindow)

        self.previousAct = QAction("Pre&vious", self,
                shortcut=QKeySequence.PreviousChild,
                statusTip="Move the focus to the previous window",
                triggered=self.mdiArea.activatePreviousSubWindow)

        self.separatorAct = QAction(self)
        self.separatorAct.setSeparator(True)

        self.aboutAct = QAction("&About", self,
                statusTip="Show the application's About box",
                triggered=self.about)

        self.aboutQtAct = QAction("About &Qt", self,
                statusTip="Show the Qt library's About box",
                triggered=QApplication.instance().aboutQt)

    def createMenus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.newAct)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.saveAct)
        self.fileMenu.addAction(self.saveAsAct)
        self.fileMenu.addSeparator()
        action = self.fileMenu.addAction("Switch layout direction")
        action.triggered.connect(self.switchLayoutDirection)
        self.fileMenu.addAction(self.exitAct)

        self.editMenu = self.menuBar().addMenu("&Edit")
        self.editMenu.addAction(self.cutAct)
        self.editMenu.addAction(self.copyAct)
        self.editMenu.addAction(self.pasteAct)

        self.windowMenu = self.menuBar().addMenu("&Window")
        self.updateWindowMenu()
        self.windowMenu.aboutToShow.connect(self.updateWindowMenu)

        self.menuBar().addSeparator()

        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

    def createToolBars(self):
        self.fileToolBar = self.addToolBar("File")
        self.fileToolBar.addAction(self.newAct)
        self.fileToolBar.addAction(self.openAct)
        self.fileToolBar.addAction(self.saveAct)

        self.editToolBar = self.addToolBar("Edit")
        self.editToolBar.addAction(self.cutAct)
        self.editToolBar.addAction(self.copyAct)
        self.editToolBar.addAction(self.pasteAct)

    def createStatusBar(self):
        self.statusBar().showMessage("Ready")

    def readSettings(self):
        settings = QSettings('Trolltech', 'MDI Example')
        pos = settings.value('pos', QPoint(200, 200))
        size = settings.value('size', QSize(400, 400))
        self.move(pos)
        self.resize(size)

    def writeSettings(self):
        settings = QSettings('Trolltech', 'MDI Example')
        settings.setValue('pos', self.pos())
        settings.setValue('size', self.size())

    def activeMdiChild(self):
        activeSubWindow = self.mdiArea.activeSubWindow()
        if activeSubWindow:
            return activeSubWindow.widget()
        return None

    def findMdiChild(self, fileName):
        canonicalFilePath = QFileInfo(fileName).canonicalFilePath()

        for window in self.mdiArea.subWindowList():
            if window.widget().currentFile() == canonicalFilePath:
                return window
        return None

    def switchLayoutDirection(self):
        if self.layoutDirection() == Qt.LeftToRight:
            QApplication.setLayoutDirection(Qt.RightToLeft)
        else:
            QApplication.setLayoutDirection(Qt.LeftToRight)

    def setActiveSubWindow(self, window):
        if window:
            self.mdiArea.setActiveSubWindow(window)
예제 #10
0
class WindowWidget(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.formCoin = None
        self.formCliente = None

        # title, geoemtry and icon for this window
        self.setWindowTitle("Pyside2 MDI Window")
        self.setGeometry(100, 100, 900, 500)
        self.setWindowIcon(QIcon("pyicon.png"))

        # creating object of MDI
        self.mdi = QMdiArea()
        self.setCentralWidget(self.mdi)

        # our menu bar
        menu_bar = self.menuBar()

        # our menu items
        file = menu_bar.addMenu("File")
        file.addAction("Coin")
        file.addAction("Cliente")
        file.addAction("Cascade")
        # file.addAction("Tiled")
        file.triggered[QAction].connect(self.window_triggered)
        self.showMaximized()

    def loadSubWindow(self, widget):
        window = self.mdi.addSubWindow(widget)
        window.setFixedSize(window.size())
        window.setWindowFlags(
            QtCore.Qt.Dialog
            | QtCore.Qt.WindowCloseButtonHint
            | QtCore.Qt.WindowMinimizeButtonHint
            | QtCore.Qt.CustomizeWindowHint
        )
        window.move(
            (self.mdi.width() - window.width() - 10) / 2,
            (self.mdi.height() - window.height() - 90) / 2,
        )
        window.show()

    def findMdiChild(self, fileName):
        for window in self.mdi.subWindowList():
            if window.widget().windowTitle() == fileName:
                return window
        return None

    def window_triggered(self, p):
         if p.text() == "Coin":
            existing = self.findMdiChild('Coin')
            if existing is None:
                self.formCoin = Coin("coin.ui")
                self.loadSubWindow(self.formCoin)
            else:
                self.formCoin.setFocus()
 
         if p.text() == "Cliente":
            existing = self.findMdiChild('Cliente')
            if existing is None:
                self.formCliente = Cliente("clienteform.ui")
                self.loadSubWindow(self.formCliente)
            else:
                self.formCliente.setFocus()

         if p.text() == "Cascade":            
             self.mdi.cascadeSubWindows()
         if p.text() == "Tiled":
             self.mdi.tileSubWindows()
예제 #11
0
class MainWindow(QMainWindow):

    def __init__(self, parent=None):
        super().__init__(parent)

        self.setWindowIcon(QIcon(":/icons/apps/16/tabulator.svg"))

        self._recentDocuments = []
        self._actionRecentDocuments = []
        self._keyboardShortcutsDialog = None

        self._preferences = Preferences()
        self._preferences.loadSettings()

        self._createActions()
        self._createMenus()
        self._createToolBars()

        self._loadSettings()

        self._updateActions()
        self._updateActionFullScreen()
        self._updateMenuOpenRecent()

        # Central widget
        self._documentArea = QMdiArea()
        self._documentArea.setViewMode(QMdiArea.TabbedView)
        self._documentArea.setTabsMovable(True)
        self._documentArea.setTabsClosable(True)
        self.setCentralWidget(self._documentArea)
        self._documentArea.subWindowActivated.connect(self._onDocumentWindowActivated)


    def closeEvent(self, event):

        if True:
            # Store application properties and preferences
            self._saveSettings()
            self._preferences.saveSettings()

            event.accept()
        else:
            event.ignore()


    def _loadSettings(self):

        settings = QSettings()

        # Recent documents
        size = settings.beginReadArray("RecentDocuments")
        for idx in range(size-1, -1, -1):
            settings.setArrayIndex(idx)
            canonicalName = QFileInfo(settings.value("Document")).canonicalFilePath()
            self._updateRecentDocuments(canonicalName)
        settings.endArray()

        # Application properties: Geometry
        geometry = settings.value("Application/Geometry", QByteArray()) if self._preferences.restoreApplicationGeometry() else QByteArray()
        if not geometry.isEmpty():
            self.restoreGeometry(geometry)
        else:
            availableGeometry = self.screen().availableGeometry()
            self.resize(availableGeometry.width() * 2/3, availableGeometry.height() * 2/3)
            self.move((availableGeometry.width() - self.width()) / 2, (availableGeometry.height() - self.height()) / 2)

        # Application properties: State
        state = settings.value("Application/State", QByteArray()) if self._preferences.restoreApplicationState() else QByteArray()
        if not state.isEmpty():
            self.restoreState(state)
        else:
            self._toolbarApplication.setVisible(True)
            self._toolbarDocument.setVisible(True)
            self._toolbarEdit.setVisible(True)
            self._toolbarTools.setVisible(True)
            self._toolbarView.setVisible(False)
            self._toolbarHelp.setVisible(False)


    def _saveSettings(self):

        settings = QSettings()

        # Recent documents
        if not self._preferences.restoreRecentDocuments():
            self._recentDocuments.clear()
        settings.remove("RecentDocuments")
        settings.beginWriteArray("RecentDocuments")
        for idx in range(len(self._recentDocuments)):
            settings.setArrayIndex(idx)
            settings.setValue("Document", self._recentDocuments[idx])
        settings.endArray()

        # Application properties: Geometry
        geometry = self.saveGeometry() if self._preferences.restoreApplicationGeometry() else QByteArray()
        settings.setValue("Application/Geometry", geometry)

        # Application properties: State
        state = self.saveState() if self._preferences.restoreApplicationState() else QByteArray()
        settings.setValue("Application/State", state)


    def _createActions(self):

        #
        # Actions: Application

        self._actionAbout = QAction(self.tr("About {0}").format(QApplication.applicationName()), self)
        self._actionAbout.setObjectName("actionAbout")
        self._actionAbout.setIcon(QIcon(":/icons/apps/16/tabulator.svg"))
        self._actionAbout.setIconText(self.tr("About"))
        self._actionAbout.setToolTip(self.tr("Brief description of the application"))
        self._actionAbout.triggered.connect(self._onActionAboutTriggered)

        self._actionColophon = QAction(self.tr("Colophon"), self)
        self._actionColophon.setObjectName("actionColophon")
        self._actionColophon.setToolTip(self.tr("Lengthy description of the application"))
        self._actionColophon.triggered.connect(self._onActionColophonTriggered)

        self._actionPreferences = QAction(self.tr("Preferences…"), self)
        self._actionPreferences.setObjectName("actionPreferences")
        self._actionPreferences.setIcon(QIcon.fromTheme("configure", QIcon(":/icons/actions/16/application-configure.svg")))
        self._actionPreferences.setToolTip(self.tr("Customize the appearance and behavior of the application"))
        self._actionPreferences.triggered.connect(self._onActionPreferencesTriggered)

        self._actionQuit = QAction(self.tr("Quit"), self)
        self._actionQuit.setObjectName("actionQuit")
        self._actionQuit.setIcon(QIcon.fromTheme("application-exit", QIcon(":/icons/actions/16/application-exit.svg")))
        self._actionQuit.setShortcut(QKeySequence.Quit)
        self._actionQuit.setToolTip(self.tr("Quit the application"))
        self._actionQuit.triggered.connect(self.close)

        #
        # Actions: Document

        self._actionNew = QAction(self.tr("New"), self)
        self._actionNew.setObjectName("actionNew")
        self._actionNew.setIcon(QIcon.fromTheme("document-new", QIcon(":/icons/actions/16/document-new.svg")))
        self._actionNew.setShortcut(QKeySequence.New)
        self._actionNew.setToolTip(self.tr("Create new document"))
        self._actionNew.triggered.connect(self._onActionNewTriggered)

        self._actionOpen = QAction(self.tr("Open…"), self)
        self._actionOpen.setObjectName("actionOpen")
        self._actionOpen.setIcon(QIcon.fromTheme("document-open", QIcon(":/icons/actions/16/document-open.svg")))
        self._actionOpen.setShortcut(QKeySequence.Open)
        self._actionOpen.setToolTip(self.tr("Open an existing document"))
        self._actionOpen.triggered.connect(self._onActionOpenTriggered)

        self._actionOpenRecentClear = QAction(self.tr("Clear List"), self)
        self._actionOpenRecentClear.setObjectName("actionOpenRecentClear")
        self._actionOpenRecentClear.setToolTip(self.tr("Clear document list"))
        self._actionOpenRecentClear.triggered.connect(self._onActionOpenRecentClearTriggered)

        self._actionSave = QAction(self.tr("Save"), self)
        self._actionSave.setObjectName("actionSave")
        self._actionSave.setIcon(QIcon.fromTheme("document-save", QIcon(":/icons/actions/16/document-save.svg")))
        self._actionSave.setShortcut(QKeySequence.Save)
        self._actionSave.setToolTip(self.tr("Save document"))
        self._actionSave.triggered.connect(self._onActionSaveTriggered)

        self._actionSaveAs = QAction(self.tr("Save As…"), self)
        self._actionSaveAs.setObjectName("actionSaveAs")
        self._actionSaveAs.setIcon(QIcon.fromTheme("document-save-as", QIcon(":/icons/actions/16/document-save-as.svg")))
        self._actionSaveAs.setShortcut(QKeySequence.SaveAs)
        self._actionSaveAs.setToolTip(self.tr("Save document under a new name"))
        self._actionSaveAs.triggered.connect(self._onActionSaveAsTriggered)

        self._actionSaveAsDelimiterColon = QAction(self.tr("Colon"), self)
        self._actionSaveAsDelimiterColon.setObjectName("actionSaveAsDelimiterColon")
        self._actionSaveAsDelimiterColon.setCheckable(True)
        self._actionSaveAsDelimiterColon.setToolTip(self.tr("Save document with colon as delimiter under a new name"))
        self._actionSaveAsDelimiterColon.setData("colon")
        self._actionSaveAsDelimiterColon.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("colon") )

        self._actionSaveAsDelimiterComma = QAction(self.tr("Comma"), self)
        self._actionSaveAsDelimiterComma.setObjectName("actionSaveAsDelimiterComma")
        self._actionSaveAsDelimiterComma.setCheckable(True)
        self._actionSaveAsDelimiterComma.setToolTip(self.tr("Save document with comma as delimiter under a new name"))
        self._actionSaveAsDelimiterComma.setData("comma")
        self._actionSaveAsDelimiterComma.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("comma") )

        self._actionSaveAsDelimiterSemicolon = QAction(self.tr("Semicolon"), self)
        self._actionSaveAsDelimiterSemicolon.setObjectName("actionSaveAsDelimiterSemicolon")
        self._actionSaveAsDelimiterSemicolon.setCheckable(True)
        self._actionSaveAsDelimiterSemicolon.setToolTip(self.tr("Save document with semicolon as delimiter under a new name"))
        self._actionSaveAsDelimiterSemicolon.setData("semicolon")
        self._actionSaveAsDelimiterSemicolon.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("semicolon") )

        self._actionSaveAsDelimiterTab = QAction(self.tr("Tab"), self)
        self._actionSaveAsDelimiterTab.setObjectName("actionSaveAsDelimiterTab")
        self._actionSaveAsDelimiterTab.setCheckable(True)
        self._actionSaveAsDelimiterTab.setToolTip(self.tr("Save document with tab as delimiter under a new name"))
        self._actionSaveAsDelimiterTab.setData("tab")
        self._actionSaveAsDelimiterTab.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("tab") )

        self._actionSaveAsDelimiter = QActionGroup(self)
        self._actionSaveAsDelimiter.setObjectName("actionSaveAsDelimiter")
        self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterColon)
        self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterComma)
        self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterSemicolon)
        self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterTab)

        self._actionSaveCopyAs = QAction(self.tr("Save Copy As…"), self)
        self._actionSaveCopyAs.setObjectName("actionSaveCopyAs")
        self._actionSaveCopyAs.setIcon(QIcon.fromTheme("document-save-as", QIcon(":/icons/actions/16/document-save-as.svg")))
        self._actionSaveCopyAs.setToolTip(self.tr("Save copy of document under a new name"))
        self._actionSaveCopyAs.triggered.connect(self._onActionSaveCopyAsTriggered)

        self._actionSaveAll = QAction(self.tr("Save All"), self)
        self._actionSaveAll.setObjectName("actionSaveAll")
        self._actionSaveAll.setIcon(QIcon.fromTheme("document-save-all", QIcon(":/icons/actions/16/document-save-all.svg")))
        self._actionSaveAll.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L))
        self._actionSaveAll.setToolTip(self.tr("Save all documents"))
        self._actionSaveAll.triggered.connect(self._onActionSaveAllTriggered)

        self._actionClose = QAction(self.tr("Close"), self)
        self._actionClose.setObjectName("actionClose")
        self._actionClose.setIcon(QIcon.fromTheme("document-close", QIcon(":/icons/actions/16/document-close.svg")))
        self._actionClose.setShortcut(QKeySequence.Close)
        self._actionClose.setToolTip(self.tr("Close document"))
        self._actionClose.triggered.connect(self._onActionCloseTriggered)

        self._actionCloseOther = QAction(self.tr("Close Other"), self)
        self._actionCloseOther.setObjectName("actionCloseOther")
        self._actionCloseOther.setToolTip(self.tr("Close all other documents"))
        self._actionCloseOther.triggered.connect(self._onActionCloseOtherTriggered)

        self._actionCloseAll = QAction(self.tr("Close All"), self)
        self._actionCloseAll.setObjectName("actionCloseAll")
        self._actionCloseAll.setShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_W))
        self._actionCloseAll.setToolTip(self.tr("Close all documents"))
        self._actionCloseAll.triggered.connect(self._onActionCloseAllTriggered)

        #
        # Actions: View

        self._actionFullScreen = QAction(self)
        self._actionFullScreen.setObjectName("actionFullScreen")
        self._actionFullScreen.setIconText(self.tr("Full Screen"))
        self._actionFullScreen.setCheckable(True)
        self._actionFullScreen.setShortcuts([QKeySequence(Qt.Key_F11), QKeySequence.FullScreen])
        self._actionFullScreen.triggered.connect(self._onActionFullScreenTriggered)

        self._actionTitlebarFullPath = QAction(self.tr("Show Path in Titlebar"), self)
        self._actionTitlebarFullPath.setObjectName("actionTitlebarFullPath")
        self._actionTitlebarFullPath.setCheckable(True)
        self._actionTitlebarFullPath.setChecked(True)
        self._actionTitlebarFullPath.setToolTip(self.tr("Display the full path of the document in the titlebar"))
        self._actionTitlebarFullPath.triggered.connect(self._onActionTitlebarFullPathTriggered)

        self._actionToolbarApplication = QAction(self.tr("Show Application Toolbar"), self)
        self._actionToolbarApplication.setObjectName("actionToolbarApplication")
        self._actionToolbarApplication.setCheckable(True)
        self._actionToolbarApplication.setToolTip(self.tr("Display the Application toolbar"))
        self._actionToolbarApplication.toggled.connect(lambda checked: self._toolbarApplication.setVisible(checked))

        self._actionToolbarDocument = QAction(self.tr("Show Document Toolbar"), self)
        self._actionToolbarDocument.setObjectName("actionToolbarDocument")
        self._actionToolbarDocument.setCheckable(True)
        self._actionToolbarDocument.setToolTip(self.tr("Display the Document toolbar"))
        self._actionToolbarDocument.toggled.connect(lambda checked: self._toolbarDocument.setVisible(checked))

        self._actionToolbarEdit = QAction(self.tr("Show Edit Toolbar"), self)
        self._actionToolbarEdit.setObjectName("actionToolbarEdit")
        self._actionToolbarEdit.setCheckable(True)
        self._actionToolbarEdit.setToolTip(self.tr("Display the Edit toolbar"))
        self._actionToolbarEdit.toggled.connect(lambda checked: self._toolbarEdit.setVisible(checked))

        self._actionToolbarTools = QAction(self.tr("Show Tools Toolbar"), self)
        self._actionToolbarTools.setObjectName("actionToolbarTools")
        self._actionToolbarTools.setCheckable(True)
        self._actionToolbarTools.setToolTip(self.tr("Display the Tools toolbar"))
        self._actionToolbarTools.toggled.connect(lambda checked: self._toolbarTools.setVisible(checked))

        self._actionToolbarView = QAction(self.tr("Show View Toolbar"), self)
        self._actionToolbarView.setObjectName("actionToolbarView")
        self._actionToolbarView.setCheckable(True)
        self._actionToolbarView.setToolTip(self.tr("Display the View toolbar"))
        self._actionToolbarView.toggled.connect(lambda checked: self._toolbarView.setVisible(checked))

        self._actionToolbarHelp = QAction(self.tr("Show Help Toolbar"), self)
        self._actionToolbarHelp.setObjectName("actionToolbarHelp")
        self._actionToolbarHelp.setCheckable(True)
        self._actionToolbarHelp.setToolTip(self.tr("Display the Help toolbar"))
        self._actionToolbarHelp.toggled.connect(lambda checked: self._toolbarHelp.setVisible(checked))

        #
        # Actions: Help

        self._actionKeyboardShortcuts = QAction(self.tr("Keyboard Shortcuts"), self)
        self._actionKeyboardShortcuts.setObjectName("actionKeyboardShortcuts")
        self._actionKeyboardShortcuts.setIcon(QIcon.fromTheme("help-keyboard-shortcuts", QIcon(":/icons/actions/16/help-keyboard-shortcuts.svg")))
        self._actionKeyboardShortcuts.setIconText(self.tr("Shortcuts"))
        self._actionKeyboardShortcuts.setToolTip(self.tr("List of all keyboard shortcuts"))
        self._actionKeyboardShortcuts.triggered.connect(self._onActionKeyboardShortcutsTriggered)


    def _createMenus(self):

        # Menu: Application
        menuApplication = self.menuBar().addMenu(self.tr("Application"))
        menuApplication.setObjectName("menuApplication")
        menuApplication.addAction(self._actionAbout)
        menuApplication.addAction(self._actionColophon)
        menuApplication.addSeparator()
        menuApplication.addAction(self._actionPreferences)
        menuApplication.addSeparator()
        menuApplication.addAction(self._actionQuit)

        #
        # Menu: Document

        self._menuOpenRecent = QMenu(self.tr("Open Recent"), self)
        self._menuOpenRecent.setObjectName("menuOpenRecent")
        self._menuOpenRecent.setIcon(QIcon.fromTheme("document-open-recent", QIcon(":/icons/actions/16/document-open-recent.svg")))
        self._menuOpenRecent.setToolTip(self.tr("Open a document which was recently opened"))

        self._menuSaveAsDelimiter = QMenu(self.tr("Save As with Delimiter…"), self)
        self._menuSaveAsDelimiter.setObjectName("menuSaveAsDelimiter")
        self._menuSaveAsDelimiter.setIcon(QIcon.fromTheme("document-save-as", QIcon(":/icons/actions/16/document-save-as.svg")))
        self._menuSaveAsDelimiter.setToolTip(self.tr("Save document with specific delimiter under a new name"))
        self._menuSaveAsDelimiter.addActions(self._actionSaveAsDelimiter.actions())

        menuDocument = self.menuBar().addMenu(self.tr("Document"))
        menuDocument.setObjectName("menuDocument")
        menuDocument.addAction(self._actionNew)
        menuDocument.addSeparator()
        menuDocument.addAction(self._actionOpen)
        menuDocument.addMenu(self._menuOpenRecent)
        menuDocument.addSeparator()
        menuDocument.addAction(self._actionSave)
        menuDocument.addAction(self._actionSaveAs)
        menuDocument.addMenu(self._menuSaveAsDelimiter)
        menuDocument.addAction(self._actionSaveCopyAs)
        menuDocument.addAction(self._actionSaveAll)
        menuDocument.addSeparator()
        menuDocument.addAction(self._actionClose)
        menuDocument.addAction(self._actionCloseOther)
        menuDocument.addAction(self._actionCloseAll)

        # Menu: Edit
        menuEdit = self.menuBar().addMenu(self.tr("Edit"))
        menuEdit.setObjectName("menuEdit")

        # Menu: Tools
        menuTools = self.menuBar().addMenu(self.tr("Tools"))
        menuTools.setObjectName("menuTools")

        # Menu: View
        menuView = self.menuBar().addMenu(self.tr("View"))
        menuView.setObjectName("menuView")
        menuView.addAction(self._actionFullScreen)
        menuView.addSeparator()
        menuView.addAction(self._actionTitlebarFullPath)
        menuView.addSeparator()
        menuView.addAction(self._actionToolbarApplication)
        menuView.addAction(self._actionToolbarDocument)
        menuView.addAction(self._actionToolbarEdit)
        menuView.addAction(self._actionToolbarTools)
        menuView.addAction(self._actionToolbarView)
        menuView.addAction(self._actionToolbarHelp)

        # Menu: Help
        menuHelp = self.menuBar().addMenu(self.tr("Help"))
        menuHelp.setObjectName("menuHelp")
        menuHelp.addAction(self._actionKeyboardShortcuts)


    def _createToolBars(self):

        # Toolbar: Application
        self._toolbarApplication = self.addToolBar(self.tr("Application Toolbar"))
        self._toolbarApplication.setObjectName("toolbarApplication")
        self._toolbarApplication.addAction(self._actionAbout)
        self._toolbarApplication.addAction(self._actionPreferences)
        self._toolbarApplication.addSeparator()
        self._toolbarApplication.addAction(self._actionQuit)
        self._toolbarApplication.visibilityChanged.connect(lambda visible: self._actionToolbarApplication.setChecked(visible))

        # Toolbar: Document
        self._toolbarDocument = self.addToolBar(self.tr("Document Toolbar"))
        self._toolbarDocument.setObjectName("toolbarDocument")
        self._toolbarDocument.addAction(self._actionNew)
        self._toolbarDocument.addAction(self._actionOpen)
        self._toolbarDocument.addSeparator()
        self._toolbarDocument.addAction(self._actionSave)
        self._toolbarDocument.addAction(self._actionSaveAs)
        self._toolbarDocument.addSeparator()
        self._toolbarDocument.addAction(self._actionClose)
        self._toolbarDocument.visibilityChanged.connect(lambda visible: self._actionToolbarDocument.setChecked(visible))

        # Toolbar: Edit
        self._toolbarEdit = self.addToolBar(self.tr("Edit Toolbar"))
        self._toolbarEdit.setObjectName("toolbarEdit")
        self._toolbarEdit.visibilityChanged.connect(lambda visible: self._actionToolbarEdit.setChecked(visible))

        # Toolbar: Tools
        self._toolbarTools = self.addToolBar(self.tr("Tools Toolbar"))
        self._toolbarTools.setObjectName("toolbarTools")
        self._toolbarTools.visibilityChanged.connect(lambda visible: self._actionToolbarTools.setChecked(visible))

        # Toolbar: View
        self._toolbarView = self.addToolBar(self.tr("View Toolbar"))
        self._toolbarView.setObjectName("toolbarView")
        self._toolbarView.addAction(self._actionFullScreen)
        self._toolbarView.visibilityChanged.connect(lambda visible: self._actionToolbarView.setChecked(visible))

        # Toolbar: Help
        self._toolbarHelp = self.addToolBar(self.tr("Help Toolbar"))
        self._toolbarHelp.setObjectName("toolbarHelp")
        self._toolbarHelp.addAction(self._actionKeyboardShortcuts)
        self._toolbarHelp.visibilityChanged.connect(lambda visible: self._actionToolbarHelp.setChecked(visible))


    def _updateActions(self, subWindowCount=0):

        hasDocument = subWindowCount >= 1
        hasDocuments = subWindowCount >= 2

        # Actions: Document
        self._actionSave.setEnabled(hasDocument)
        self._actionSaveAs.setEnabled(hasDocument)
        self._menuSaveAsDelimiter.setEnabled(hasDocument)
        self._actionSaveCopyAs.setEnabled(hasDocument)
        self._actionSaveAll.setEnabled(hasDocument)
        self._actionClose.setEnabled(hasDocument)
        self._actionCloseOther.setEnabled(hasDocuments)
        self._actionCloseAll.setEnabled(hasDocument)


    def _updateActionFullScreen(self):

        if not self.isFullScreen():
            self._actionFullScreen.setText(self.tr("Full Screen Mode"))
            self._actionFullScreen.setIcon(QIcon.fromTheme("view-fullscreen", QIcon(":/icons/actions/16/view-fullscreen.svg")))
            self._actionFullScreen.setChecked(False)
            self._actionFullScreen.setToolTip(self.tr("Display the window in full screen"))
        else:
            self._actionFullScreen.setText(self.tr("Exit Full Screen Mode"))
            self._actionFullScreen.setIcon(QIcon.fromTheme("view-restore", QIcon(":/icons/actions/16/view-restore.svg")))
            self._actionFullScreen.setChecked(True)
            self._actionFullScreen.setToolTip(self.tr("Exit the full screen mode"))


    def _updateActionRecentDocuments(self):

        # Add items to the list, if necessary
        for idx in range(len(self._actionRecentDocuments)+1, self._preferences.maximumRecentDocuments()+1):

            actionRecentDocument = QAction(self)
            actionRecentDocument.setObjectName(f"actionRecentDocument_{idx}")
            actionRecentDocument.triggered.connect(lambda data=actionRecentDocument.data(): self._onActionOpenRecentDocumentTriggered(data))

            self._actionRecentDocuments.append(actionRecentDocument)

        # Remove items from the list, if necessary
        while len(self._actionRecentDocuments) > self._preferences.maximumRecentDocuments():
            self._actionRecentDocuments.pop()

        # Update items
        for idx in range(len(self._actionRecentDocuments)):
            text = None
            data = None
            show = False

            if idx < len(self._recentDocuments):
                text = self.tr("{0} [{1}]").format(QFileInfo(self._recentDocuments[idx]).fileName(), self._recentDocuments[idx])
                data = self._recentDocuments[idx]
                show = True

            self._actionRecentDocuments[idx].setText(text)
            self._actionRecentDocuments[idx].setData(data)
            self._actionRecentDocuments[idx].setVisible(show)


    def _updateMenuOpenRecent(self):

        self._menuOpenRecent.clear()

        if self._preferences.maximumRecentDocuments() > 0:
            # Document list wanted; show the menu
            self._menuOpenRecent.menuAction().setVisible(True)

            if len(self._recentDocuments) > 0:
                # Document list has items; enable the menu
                self._menuOpenRecent.setEnabled(True)

                self._menuOpenRecent.addActions(self._actionRecentDocuments)
                self._menuOpenRecent.addSeparator()
                self._menuOpenRecent.addAction(self._actionOpenRecentClear)
            else:
                # Document list is empty; disable the menu
                self._menuOpenRecent.setEnabled(False)
        else:
            # No document list wanted; hide the menu
            self._menuOpenRecent.menuAction().setVisible(False)


    def _updateTitleBar(self):

        title = None

        document = self._activeDocument()
        if document:
            title = document.canonicalName() if self._actionTitlebarFullPath.isChecked() and document.canonicalName() else document.documentTitle()

        self.setWindowTitle(title)


    def _onActionAboutTriggered(self):

        dialog = AboutDialog(self)
        dialog.exec_()


    def _onActionColophonTriggered(self):

        dialog = ColophonDialog(self)
        dialog.exec_()


    def _onActionPreferencesTriggered(self):

        dialog = PreferencesDialog(self)
        dialog.setPreferences(self._preferences)
        dialog.exec_()

        self._preferences = dialog.preferences()

        self._updateRecentDocuments(None)
        self._updateMenuOpenRecent()


    def _onActionNewTriggered(self):

        self._loadDocument("")


    def _onActionOpenTriggered(self):

        fileNames = QFileDialog.getOpenFileNames(self, self.tr("Open Document"),
                        QStandardPaths.writableLocation(QStandardPaths.HomeLocation),
                        self.tr("CSV Files (*.csv);;All Files (*.*)"))[0]

        for fileName in fileNames:
            self._openDocument(fileName)


    def _onActionOpenRecentDocumentTriggered(self, canonicalName):
        pass

#        self.openDocument(canonicalName)


    def _onActionOpenRecentClearTriggered(self):

        self._recentDocuments.clear()

        self._updateRecentDocuments(None)
        self._updateMenuOpenRecent()


    def _onActionSaveTriggered(self):
        pass


    def _onActionSaveAsTriggered(self):
        pass


    def _onActionSaveAsDelimiterTriggered(self, delimiter):
        pass


    def _onActionSaveCopyAsTriggered(self):
        pass


    def _onActionSaveAllTriggered(self):
        pass


    def _onActionCloseTriggered(self):

        self._documentArea.closeActiveSubWindow()


    def _onActionCloseOtherTriggered(self):

        for subWindow in self._documentArea.subWindowList():
            if subWindow != self._documentArea.activeSubWindow():
                subWindow.close()


    def _onActionCloseAllTriggered(self):

        self._documentArea.closeAllSubWindows()


    def _onActionFullScreenTriggered(self):

        if not self.isFullScreen():
            self.setWindowState(self.windowState() | Qt.WindowFullScreen)
        else:
            self.setWindowState(self.windowState() & ~Qt.WindowFullScreen)

        self._updateActionFullScreen()


    def _onActionTitlebarFullPathTriggered(self):

        self._updateTitleBar()


    def _onActionKeyboardShortcutsTriggered(self):

        if not self._keyboardShortcutsDialog:
            self._keyboardShortcutsDialog = KeyboardShortcutsDialog(self)

        self._keyboardShortcutsDialog.show()
        self._keyboardShortcutsDialog.raise_()
        self._keyboardShortcutsDialog.activateWindow()


    def _onDocumentWindowActivated(self, subWindow):

        # Update the application window
        self._updateActions(len(self._documentArea.subWindowList()))
        self._updateTitleBar()

        if not subWindow:
            return


    def _onDocumentAboutToClose(self, canonicalName):

        # Workaround to show subwindows always maximized
        for subWindow in self._documentArea.subWindowList():
            if not subWindow.isMaximized():
                subWindow.showMaximized()

        # Update menu items without the emitter
        self._updateActions(len(self._documentArea.subWindowList()) - 1)


    def _createDocument(self):

        document = Document()
        document.setPreferences(self._preferences)
        document.aboutToClose.connect(self._onDocumentAboutToClose)

        subWindow = self._documentArea.addSubWindow(document)
        subWindow.setWindowIcon(QIcon())
        subWindow.showMaximized()

        return document


    def _createDocumentIndex(self, canonicalName):

        fileName = QFileInfo(canonicalName).fileName()
        canonicalIndex = 0

        for subWindow in self._documentArea.subWindowList():
            if QFileInfo(subWindow.widget().canonicalName()).fileName() == fileName:
                if subWindow.widget().canonicalIndex() > canonicalIndex:
                    canonicalIndex = subWindow.widget().canonicalIndex()

        return canonicalIndex + 1


    def _findDocumentWindow(self, canonicalName):

        for subWindow in self._documentArea.subWindowList():
            if subWindow.widget().canonicalName() == canonicalName:
                return subWindow

        return None


    def _activeDocument(self):

        subWindow = self._documentArea.activeSubWindow()

        return subWindow.widget() if subWindow else None


    def _openDocument(self, fileName):

        canonicalName = QFileInfo(fileName).canonicalFilePath()

        subWindow = self._findDocumentWindow(canonicalName)
        if subWindow:
            # Given document is already loaded; activate the subwindow
            self._documentArea.setActiveSubWindow(subWindow)

            # Update list of recent documents
            self._updateRecentDocuments(canonicalName)
            self._updateMenuOpenRecent()
            return True

        return self._loadDocument(canonicalName);


    def _loadDocument(self, canonicalName):

        document = self._createDocument()

        succeeded = document.load(canonicalName)
        if succeeded:
            document.setCanonicalIndex(self._createDocumentIndex(canonicalName))
            document.updateDocumentTitle()
            document.show()

            # Update list of recent documents
            self._updateRecentDocuments(canonicalName)
            self._updateMenuOpenRecent()

            # Update the application window
            self._updateActions(len(self._documentArea.subWindowList()))
            self._updateTitleBar()
        else:
            document.close()

        return succeeded


    def _updateRecentDocuments(self, canonicalName):

        if canonicalName:
            while canonicalName in self._recentDocuments:
                self._recentDocuments.remove(canonicalName)
            self._recentDocuments.insert(0, canonicalName)

        # Remove items from the list, if necessary
        while len(self._recentDocuments) > self._preferences.maximumRecentDocuments():
            self._recentDocuments.pop()

        self._updateActionRecentDocuments()