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)
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'
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)
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)
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()
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()
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)