예제 #1
0
    def build_menu_stimuli(self, visualization: bool, debugger: Optional[bool] = False) -> None:
        """Menu for stimuli delivery."""
        self.menubar = QMenuBar(self)
        self.menubar.clear()

        self.accent_menubar = QMenuBar(self)
        self.accent_menubar.clear()
        self.menubar.setMinimumWidth(1e4)

        # Title
        if debugger:
            menu_stimuli = QMenu(f"Debugging: {visualization}")
        else:
            if visualization:
                menu_stimuli = QMenu(visualization + ' 🞃')
            else:
                menu_stimuli = QMenu('Stimuli' + ' 🞃')

        for viz, path in self.extensions_list:
            if viz != visualization:
                menu_stimuli.addAction(QAction(viz, menu_stimuli,
                                               triggered=self.set_extension(path)))
        # self.menubar.addMenu(menu_stimuli)
        self.accent_menubar.addMenu(menu_stimuli)

        self.accent_menubar.setStyleSheet(f"""
        QMenuBar::item {{
            background-color: {os.getenv('QTMATERIAL_PRIMARYCOLOR', '#ffffff')};
            color: {os.getenv('QTMATERIAL_PRIMARYTEXTCOLOR', '#ffffff')};
        }}

        """)

        # self.menubar.addMenu(menu_stimuli)
        self.menubar.setCornerWidget(
            self.accent_menubar, corner=Qt.TopLeftCorner)

        # View
        menu_view = QMenu("View")
        if visualization:

            menu_view.addAction(
                QAction('Reload', menu_view, triggered=self.reload))

            if debugger:
                menu_view.addAction(
                    QAction('Open subwindow delivery', menu_view, triggered=debugger.open_subwindow))
            if not debugger:
                menu_view.addSeparator()
                menu_view.addAction(
                    QAction('Close', menu_view, triggered=self.remove))
        else:
            menu_view.setEnabled(False)
        self.menubar.addMenu(menu_view)
예제 #2
0
class SpreadsheetExportCSVMenu:
    def __init__(self):
        #     hiero.core.events.registerInterest("kShowContextMenu/kSpreadsheet", self.eventHandler)
        registerInterest("kShowContextMenu/kSpreadsheet", self.eventHandler)
        self._exportAllSpreadsheetToCSV = self.createMenuAction(
            "Spreadsheet To CSV", self.exportCSV)
        self._exportCSVMenu = QMenu('Export...')

    def createMenuAction(self, title, method):
        action = QAction(title, None)
        action.triggered.connect(method)
        return action

    def eventHandler(self, event):
        self.selection = event.sender.selection()
        enabled = True
        if (self.selection is None) or (len(self.selection) == 0):
            self.selection = ()
            enabled = False

        self._exportAllSpreadsheetToCSV.setEnabled(enabled)
        self._exportCSVMenu.setEnabled(enabled)

        # Insert the custom Menu, divided by a separator
        event.menu.addSeparator()

        event.menu.addMenu(self._exportCSVMenu)

        # Insert the action to the Export CSV menu
        self._exportCSVMenu.addAction(self._exportAllSpreadsheetToCSV)

    # Call the Method above to write the Sequence to a CSV file..
    def exportCSV(self):
        print 'exporting CSV...'

        # Ignore transitions from the selection
        self.selection = [
            item for item in self.selection if isinstance(item, TrackItem)
        ]
        seq = self.selection[0].parent().parent()
        print 'seq is', seq
        if isinstance(seq, Sequence):
            writeCSVFromSequence(seq)
        else:
            print 'Unable to Export Sequence'
예제 #3
0
    def build_menu_visualization(self, visualization: bool, debugger: Optional[bool] = False) -> None:
        """Menu for visualizations."""
        self.menubar = QMenuBar(self)
        self.menubar.clear()
        self.menubar.setMinimumWidth(1e4)

        self.accent_menubar = QMenuBar(self)

        # Title
        if debugger:
            menu_stimuli = QMenu(f"Debugging: {visualization}")
        else:
            if visualization:
                menu_stimuli = QMenu(f'{visualization } 🞃')
            else:
                menu_stimuli = QMenu('Data analysis 🞃')

        # Add visualizations
        for viz, path in self.extensions_list:
            if viz != visualization:
                menu_stimuli.addAction(QAction(viz, menu_stimuli,
                                               triggered=self.set_extension(path)))

        self.accent_menubar.addMenu(menu_stimuli)

        # Menu with accent color
        self.accent_menubar.setStyleSheet(f"""
        QMenuBar::item {{
            background-color: {os.getenv('QTMATERIAL_PRIMARYCOLOR', '#ffffff')};
            color: {os.getenv('QTMATERIAL_PRIMARYTEXTCOLOR', '#ffffff')};
            }}""")

        # Set the menu in first position
        self.menubar.setCornerWidget(
            self.accent_menubar, corner=Qt.TopLeftCorner)

        # View
        menu_view = QMenu("View")
        if visualization:

            menu_view.addAction(
                QAction('Reload', menu_view, triggered=self.reload))

            menu_view.addAction(
                QAction('Save capture', menu_view, triggered=self.save_img))
            if not debugger:
                menu_view.addSeparator()
                menu_view.addAction(
                    QAction('Close', menu_view, triggered=self.remove))
        else:
            menu_view.setEnabled(False)
        self.menubar.addMenu(menu_view)

        # DPI
        menu_dpi = QMenu("DPI (60)")
        if visualization:
            for dpi in [60, 70, 80, 90, 100, 110, 120, 130]:
                menu_dpi.addAction(QAction(
                    f'{dpi}', menu_dpi, checkable=True, triggered=self.set_dpi(menu_dpi, f'{dpi}', dpi)))
                if dpi == 60:
                    self.set_dpi(menu_dpi, f'{dpi}', dpi)()
        else:
            menu_dpi.setEnabled(False)
        self.menubar.addMenu(menu_dpi)
예제 #4
0
class NodeEditorViewMenu(QMenu):
    """The NodeEditorViewMenu class provides a menu for the options avaliable on the
    menu (Like duplicating nodes, adding them to windows, etc).
    """

    remove_nodes = Signal()
    duplicate_nodes = Signal()

    def __init__(
        self,
        graphics_scene: "GraphicsScene",
        nodes_windows_manager: "NodesWindowsGroup",
        parent: "QWidget" = None,
    ):
        super().__init__(parent)

        # Components
        self.__graphics_scene = graphics_scene
        self.__nodes_windows_manager = nodes_windows_manager

        # Actions
        self._remove_nodes_act = QAction("Remove nodes", self)
        self._remove_nodes_act.triggered.connect(
            lambda: self.remove_nodes.emit())

        self._duplicate_nodes_act = QAction("Duplicate nodes", self)
        self._duplicate_nodes_act.triggered.connect(
            lambda: self.duplicate_nodes.emit())

        self._add_nodes_to_new_window_act = QAction("Add nodes to new window",
                                                    self)
        self._add_nodes_to_new_window_act.triggered.connect(
            self.__add_selected_nodes_to_new_window)

        self._add_each_node_to_new_window_act = QAction(
            "Add each node to a new window", self)
        self._add_each_node_to_new_window_act.triggered.connect(
            self.__add_each_selected_node_to_new_window)

        self._add_nodes_to_existing_window_menu = QMenu(
            "Add nodes to existing window...", self)

        if len(self.__nodes_windows_manager.nodes_windows) == 0:
            self._add_nodes_to_existing_window_menu.setEnabled(False)

        # Add an entry for each opened window
        for window in self.__nodes_windows_manager.nodes_windows:
            action = self._add_nodes_to_existing_window_menu.addAction(
                window.name)
            action.triggered.connect(lambda _=None, window=window: (
                self.__add_selected_nodes_to_existing_window(window)))

        self.addAction(self._remove_nodes_act)
        self.addAction(self._duplicate_nodes_act)
        self.addSeparator()
        self.addAction(self._add_nodes_to_new_window_act)
        self.addAction(self._add_each_node_to_new_window_act)
        self.addMenu(self._add_nodes_to_existing_window_menu)

    def mouseReleaseEvent(self, event):
        """Ignore right clicks on the QMenu (Avoids unintentional clicks)"""
        if event.button() == Qt.RightButton:  # Ignore right clicks
            return

        super().mouseReleaseEvent(event)

    def __add_selected_nodes_to_new_window(self):
        """Add the selected nodes from a new NodesWindow object."""
        nodes_window = self.__nodes_windows_manager.new_nodes_window()
        self.__add_nodes_to_window(self.__graphics_scene.selectedItems(),
                                   nodes_window)

    def __add_selected_nodes_to_existing_window(self, window: "NodesWindow"):
        """Add the selected nodes to a currently existing NodesWindow object."""
        self.__add_nodes_to_window(self.__graphics_scene.selectedItems(),
                                   window)

    def __add_each_selected_node_to_new_window(self):
        """For each selected node, add it to a new window."""
        for item in self.__graphics_scene.selectedItems():
            if isinstance(item, GraphicsNode):
                nodes_window = self.__nodes_windows_manager.new_nodes_window(
                    name=item.title)
                self.__add_nodes_to_window([item], nodes_window)

    def __add_nodes_to_window(self, graphics_nodes, window):
        """Add the nodes to the passed window."""
        for graphics_node in graphics_nodes:
            if isinstance(graphics_node, GraphicsNode):
                window.add_graphics_node(graphics_node)
예제 #5
0
class FileMenu(QObject):
    recent_files_changed = Signal()
    new_document_count = 0
    current_progress_obj = ShowTreeViewProgressMessage(None)
    load_save_mgr = None
    supported_file_types = ('.xml', '.rksession', '.xlsx')

    viewer_app = Path(get_current_modules_dir()) / KNECHT_VIEWER_BIN
    schnuffi_app = Path(get_current_modules_dir()) / POS_SCHNUFFI_BIN

    def __init__(self, ui, menu: QMenu = None):
        """ The File menu

        :param modules.gui.main_ui.KnechtWindow ui:
        :param menu: Menu created setup in ui file
        """
        super(FileMenu, self).__init__(parent=ui)
        self.ui = ui
        self.view_mgr: UiViewManager = None

        self.menu = menu or ui.menuDatei
        self.menu.setEnabled(False)
        self.recent_menu = QMenu(_('Zuletzt geöffnet'), self.menu)
        self.import_menu = ImportMenu(self.ui)
        self.import_menu.new_model_ready.connect(self.model_loaded)

        self.xml_message_box = XmlFailedMsgBox(self.ui)

        self.setup_file_menu()
        QTimer.singleShot(1, self.delayed_setup)

    @Slot()
    def delayed_setup(self):
        """ Setup attributes that require a fully initialized ui"""
        self.view_mgr: UiViewManager = self.ui.view_mgr
        self.menu.setEnabled(True)

        self.load_save_mgr = SaveLoadController(self)
        self.load_save_mgr.model_loaded.connect(self.model_loaded)
        self.load_save_mgr.load_aborted.connect(self._load_aborted)

    @Slot(Path)
    def guess_open_file(self, local_file_path: Path) -> bool:
        if local_file_path.suffix.casefold() == '.xml':
            self.open_xml(local_file_path.as_posix())
            return True
        elif local_file_path.suffix.casefold() == '.rksession':
            self.import_menu.open_wizard(local_file_path)
            return True
        elif local_file_path.suffix.casefold() == '.xlsx':
            self.import_menu.open_xlsx(local_file_path)
            return True

        return False

    def setup_file_menu(self):
        insert_before = 0
        if self.ui.actionBeenden in self.menu.actions():
            insert_before = self.ui.actionBeenden
            self.ui.actionBeenden.setIcon(IconRsc.get_icon('sad'))
            self.ui.actionBeenden.setShortcut(QKeySequence('Ctrl+Q'))

        # ---- New file ----
        new_action = QAction(IconRsc.get_icon('document'), _('Neu\tStrg+N'),
                             self.menu)
        new_action.setShortcut(QKeySequence('Ctrl+N'))
        new_action.triggered.connect(self.new_document)
        self.menu.insertAction(insert_before, new_action)

        # ---- Open ----
        open_xml_action = QAction(_('Öffnen\tStrg+O'), self.menu)
        open_xml_action.setShortcut(QKeySequence('Ctrl+O'))
        open_xml_action.triggered.connect(self.open_xml)
        open_xml_action.setIcon(IconRsc.get_icon('folder'))
        self.menu.insertAction(insert_before, open_xml_action)

        # ---- Import Menu ----
        self.menu.insertMenu(insert_before, self.import_menu)

        # ---- Save ----
        save_xml_action = QAction(_('Speichern\tStrg+S'), self.menu)
        save_xml_action.setShortcut(QKeySequence('Ctrl+S'))
        save_xml_action.triggered.connect(self.save_xml)
        save_xml_action.setIcon(IconRsc.get_icon('disk'))
        self.menu.insertAction(insert_before, save_xml_action)

        save_as_action = QAction(_('Speichern unter ...\tStrg+Shift+S'),
                                 self.menu)
        save_as_action.setShortcut(QKeySequence('Ctrl+Shift+S'))
        save_as_action.triggered.connect(self.save_as_xml)
        save_as_action.setIcon(IconRsc.get_icon('save_alt'))
        self.menu.insertAction(insert_before, save_as_action)

        self.menu.insertSeparator(insert_before)

        # ---- Apps ----
        start_knecht_viewer = QAction(_('KnechtViewer starten'), self.menu)
        start_knecht_viewer.triggered.connect(self.start_knecht_viewer)
        start_knecht_viewer.setIcon(IconRsc.get_icon('img'))
        self.menu.insertAction(insert_before, start_knecht_viewer)
        if not path_exists(self.viewer_app):
            LOGGER.info('KnechtViewer executable could not be found: %s',
                        self.viewer_app.as_posix())
            start_knecht_viewer.setEnabled(False)

        start_schnuffi_app = QAction(_('POS Schnuffi starten'), self.menu)
        start_schnuffi_app.triggered.connect(self.start_schnuffi_app)
        start_schnuffi_app.setIcon(IconRsc.get_icon('dog'))
        self.menu.insertAction(insert_before, start_schnuffi_app)
        if not path_exists(self.schnuffi_app):
            LOGGER.info('KnechtViewer executable could not be found: %s',
                        self.schnuffi_app.as_posix())
            start_schnuffi_app.setEnabled(False)

        img_conv = QAction(_('Bilddaten konvertieren ...'))
        img_conv.triggered.connect(self.convert_image_directory)
        img_conv.setIcon(IconRsc.get_icon('render'))
        self.menu.insertAction(insert_before, img_conv)

        material_merger = QAction('AViT Material Merger')
        material_merger.triggered.connect(self.start_material_merger)
        material_merger.setIcon(IconRsc.get_icon('options'))
        self.menu.insertAction(insert_before, material_merger)

        self.menu.insertSeparator(insert_before)

        # ---- Recent files menu ----
        self.recent_menu.aboutToShow.connect(self.update_recent_files_menu)
        self.menu.insertMenu(insert_before, self.recent_menu)

        self.menu.insertSeparator(insert_before)

    def new_document(self):
        new_file = Path(
            _('Neues_Dokument_{:02d}.xml').format(self.new_document_count))
        self.view_mgr.create_view(None, new_file)

        self.new_document_count += 1

    def start_knecht_viewer(self):
        start_app(self.viewer_app)

    def start_schnuffi_app(self):
        start_app(self.schnuffi_app)

    def start_material_merger(self):
        material_merger = MaterialMerger(self.ui)
        GenericTabWidget(self.ui, material_merger)

    def save_xml(self):
        if not self.view_mgr.current_tab_is_document_tab():
            return

        self.enable_menus(False)

        file = self.view_mgr.current_file()

        if not file or not path_exists(file):
            if self._ask_save_as_file(file):
                # User agreed to set new save file
                self.save_as_xml()
                return
            # User aborted
            self.enable_menus(True)
            return

        self.save_as_xml(file)

    def save_as_xml(self, file: Path = None):
        if not self.view_mgr.current_tab_is_document_tab():
            return

        self.enable_menus(False)

        if not file:
            current_dir = Path(KnechtSettings.app['current_path'])
            file, file_type = FileDialog.save(self.ui,
                                              current_dir,
                                              file_key='xml')

        if not file:
            LOGGER.info('Save Xml File dialog canceled.')
            self.enable_menus(True)
            return

        file = Path(file)
        view = self.view_mgr.current_view()

        result, error = self.load_save_mgr.save(file, view)

        if result:
            LOGGER.debug('File saved: %s', file.as_posix())
            self.view_mgr.tab_view_saved(file)
            self.ui.msg(
                _('Datei gespeichert:{0}{1:.3}s').format(
                    f'\n{file.name}\n', self.load_save_mgr.last_progress_time))
        else:
            self._save_aborted(error, file)

        self.enable_menus(True)

    def open_xml(self, file: str = None) -> None:
        self.enable_menus(False)

        if not file:
            file = FileDialog.open(self.ui, None, 'xml')

        if not file:
            LOGGER.info('Open Xml File dialog canceled.')
            self.enable_menus(True)
            return

        # Check if the file is already opened
        file = Path(file)
        if self.view_mgr.file_mgr.already_open(file):
            LOGGER.info('File already open.')
            self.enable_menus(True)
            return

        # Update treeview progress
        view = self.view_mgr.current_view()
        view.progress_msg.msg(_('Daten werden gelesen'))
        view.progress_msg.show_progress()

        self.load_save_mgr.open(file)
        self.enable_menus(True)

    @Slot(KnechtModel, Path)
    @Slot(KnechtModel, Path, bool)
    def model_loaded(self,
                     model: KnechtModel,
                     file: Path,
                     reset_clean: bool = False):
        # Update progress
        view = self.view_mgr.current_view()
        view.progress_msg.hide_progress()

        # Create a new view inside a new tab or load into current view if view model is empty
        new_view = self.view_mgr.create_view(model, file)

        # Refresh model data
        if reset_clean:
            new_view.undo_stack.resetClean()

        self.ui.statusBar().showMessage(
            _('{0} in {1:.3}s geladen.').format(
                file.name, self.load_save_mgr.last_progress_time))

    @Slot(str, Path)
    def _load_aborted(self, error_msg: str, file: Path):
        # Update progress
        view = self.view_mgr.current_view()
        view.progress_msg.hide_progress()

        self.xml_message_box.set_error_msg(error_msg, Path(file))

        self.ui.play_warning_sound()
        self.xml_message_box.exec_()

    def _save_aborted(self, error_msg: str, file: Path):
        self.xml_message_box.set_error_msg(error_msg, Path(file))

        self.ui.play_warning_sound()
        self.xml_message_box.exec_()

    def enable_menus(self, enabled: bool = True):
        for a in self.menu.actions():
            a.setEnabled(enabled)

        self.recent_menu.setEnabled(enabled)

    def _open_recent_xml_file(self):
        recent_action = self.sender()
        self.open_xml(recent_action.file)

    def _open_recent_xlsx_file(self):
        recent_action = self.sender()
        self.import_menu.open_xlsx(recent_action.file)

    def _open_recent_rksession(self):
        recent_action = self.sender()
        self.import_menu.open_wizard(recent_action.file)

    def _ask_save_as_file(self, file: Path):
        """ User hits save but file to save does not exist yet """
        msg_box = AskToContinue(self.ui)

        if not msg_box.ask(
                _('Zieldatei zum Speichern festlegen?'),
                _('Die Datei: <i>{}</i><br>'
                  'Pfad: <i>{}</i><br>'
                  'wurde entfernt oder existiert nicht mehr.<br><br>'
                  'Neue Zieldatei zum Speichern festlegen?'
                  '').format(file.name, file.parent.as_posix()),
                _('Speichern unter..')):
            # User wants to abort save as
            return False
        return True

    def convert_image_directory(self):
        img_dir = FileDialog.open_dir(self.ui, None)

        if not img_dir or not path_exists(img_dir):
            self.ui.msg(
                _('Zu konvertierendes Verzeichnis ist nicht erreichbar.'),
                8000)
            return

        img_dir = Path(img_dir)
        out_dir = img_dir / 'converted'

        try:
            out_dir.mkdir(exist_ok=True)
        except Exception as e:
            self.ui.msg(
                _('Konnte Bild Konvertierung Ausgabeverzeichnis nicht erstellen.'
                  ), 8000)
            LOGGER.warning(e)

        img_converter = KnechtImage(self)
        img_converter.conversion_result.connect(self._conversion_result)

        if img_converter.convert_directory(img_dir, out_dir):
            self.ui.msg(
                _('Bildkonvertierung gestartet.<br /><i>{}</i>').format(
                    img_dir), 5000)
        else:
            self.ui.msg(
                _('Bildkonvertierung konnte nicht gestartet werden. Keine konvertierbaren Dateien gefunden.'
                  ), 10000)

    def _conversion_result(self, result: str):
        result = result.replace('\n', '<br />')
        title = _("Ergebnis der Bildkonvertierung:")
        self.ui.overlay.display_confirm(f'<b>{title}</b><br />{result}',
                                        (('[X]', None), ))

    def update_recent_files_menu(self):
        self.recent_menu.clear()

        if not len(KnechtSettings.app['recent_files']):
            no_entries_dummy = QAction(_("Keine Einträge vorhanden"),
                                       self.recent_menu)
            no_entries_dummy.setEnabled(False)
            self.recent_menu.addAction(no_entries_dummy)

        for idx, entry in enumerate(KnechtSettings.app['recent_files']):
            if idx >= 20:
                break

            file, file_type = entry
            file_name = Path(file).stem

            if not path_exists(file):
                # Skip and remove non existing files
                KnechtSettings.app['recent_files'].pop(idx)
                continue

            recent_action = QAction(f'{file_name} - {file_type}',
                                    self.recent_menu)
            recent_action.file = Path(file)

            if file_type == 'xml':
                recent_action.setText(f'{file_name} - Xml Presets')
                recent_action.setIcon(IconRsc.get_icon('document'))
                recent_action.triggered.connect(self._open_recent_xml_file)
            elif file_type == 'xlsx':
                recent_action.setText(f'{file_name} - Excel Import')
                recent_action.setIcon(IconRsc.get_icon('excel'))
                recent_action.triggered.connect(self._open_recent_xlsx_file)
            elif file_type == 'rksession':
                recent_action.setText(f'{file_name} - Preset Wizard Session')
                recent_action.setIcon(IconRsc.get_icon('qub_button'))
                recent_action.triggered.connect(self._open_recent_rksession)

            self.recent_menu.addAction(recent_action)

        self.recent_files_changed.emit()
예제 #6
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()
예제 #7
0
class Mixin:
    def make_file_actions(self):
        self.file_new_action = make_action(
            self, get_icon(DOCUMENT_NEW_SVG), '&New...', self.file_new,
            QKeySequence.New, f'Create a new SQLite or {APPNAME} database')
        self.file_open_action = make_action(
            self, get_icon(DOCUMENT_OPEN_SVG), '&Open...', self.file_open,
            QKeySequence.Open,
            f'Open an existing SQLite or {APPNAME} database')
        self.file_open_recent_action = make_action(self,
                                                   get_icon(DOCUMENT_OPEN_SVG),
                                                   'Open &Recent')
        self.file_open_recent_menu = QMenu(self)
        self.file_open_recent_action.setMenu(self.file_open_recent_menu)
        self.file_save_action = make_action(
            self, get_icon(FILESAVE_SVG), '&Save', self.file_save,
            QKeySequence.Save, 'Save any unsaved changes to the database')
        self.file_saveas_action = make_action(
            self, get_icon(FILESAVEAS_SVG), 'Save &As...', self.file_saveas,
            QKeySequence.SaveAs, 'Save the database under a new name and '
            'open the database with the new name')
        self.file_backup_action = make_action(
            self,
            get_icon(FILESAVEAS_SVG),
            '&Backup...',
            self.file_backup,
            tip='Save a copy of the database')
        self.file_import_action = make_action(
            self,
            get_icon(IMPORT_SVG),
            '&Import...',
            self.file_import,
            tip='Import an external data file (e.g., CSV) as a new '
            'table in the current database')
        self.file_export_action = make_action(
            self,
            get_icon(EXPORT_SVG),
            '&Export...',
            self.file_export,
            tip="Export a table, view or SELECT query's data as an "
            'external data file (e.g., CSV)')
        self.file_quit_action = make_action(
            self, get_icon(SHUTDOWN_SVG), '&Quit', self.close,
            QKeySequence(Qt.CTRL + Qt.Key_Q),
            'Save any unsaved changes and quit')

    @property
    def file_actions_for_menu(self):
        return (self.file_new_action, self.file_open_action,
                self.file_open_recent_action, self.file_save_action,
                self.file_saveas_action, self.file_backup_action, None,
                self.file_import_action, self.file_export_action, None,
                self.file_quit_action)

    @property
    def file_actions_for_toolbar(self):
        return (self.file_new_action, self.file_open_action,
                self.file_save_action, None, self.file_import_action,
                self.file_export_action)

    def file_update_ui(self):
        enable = bool(self.db)
        for action in (self.file_save_action, self.file_saveas_action,
                       self.file_backup_action, self.file_export_action):
            action.setEnabled(enable)
        QTimer.singleShot(0, self.file_populate_open_recent_menu)

    def file_populate_open_recent_menu(self):
        self.file_open_recent_menu.clear()
        filenames = [str(filename) for filename in list(self.recent_files)]
        self.file_open_recent_menu.setEnabled(bool(filenames))
        icon = get_icon(DOCUMENT_OPEN_SVG)
        for i, filename in enumerate(filenames, 1):
            action = make_action(
                self,
                icon,
                '&{} {}'.format(i, filename),
                lambda *_, filename=filename: self.file_load(filename),
                tip=f'Open {filename}')
            self.file_open_recent_menu.addAction(action)
        if filenames:
            self.file_open_recent_menu.addSeparator()
            self.file_open_recent_menu.addAction(
                make_action(self,
                            get_icon(EDIT_CLEAR_SVG),
                            '&Clear',
                            self.file_clear_recent_files,
                            tip='Clear the list of recently opened databases'))

    def file_clear_recent_files(self):
        self.recent_files.clear()
        self.file_update_ui()

    def file_new(self):
        if filename := self._file_new_or_open('New',
                                              QFileDialog.getSaveFileName):
            if filename.exists():
                QMessageBox.warning(
                    self, f'Database exists — {APPNAME}',
                    f'Will not overwrite an existing database '
                    '({filename}) with a new one')
            else:
                self.file_load(filename, new=True)