def _execTreeViewContextMenu(self, point): index = self.treeView.indexAt(point) item = self.model.data(index, ITEM_ROLE) if isinstance(item, ConfigurationModel.SubConfigContent): m = QMenu() a1 = QAction("Edit graph") m.addAction(a1) a1.triggered.connect(lambda: self._addGraphView(item.subConfig)) if self.model.isApplication(index): a2 = QAction("Select Application") a2.triggered.connect(lambda: self.changeActiveApp( self.model.data(index, Qt.DisplayRole))) a3 = QAction("Init Application") a3.triggered.connect(lambda: self._changeActiveAppAndInit( self.model.data(index, Qt.DisplayRole))) m.addActions([a2, a3]) pbsrv = Services.getService("PlaybackControl") m2 = m.addMenu("Init and load sequence") m3 = m.addMenu("Init, load and play") s1 = [] s2 = [] for a in pbsrv.recentSeqs: assert isinstance(a, QAction) if a.isVisible(): # pylint: disable=cell-var-from-loop # the below statements are tested and work aseq = QAction(a.text()) aseq.triggered.connect(lambda arg1=a.data( ), seq=a.data(): self._changeActiveAppInitAndLoad( self.model.data(index, Qt.DisplayRole), seq, False) ) s1.append(aseq) aseq = QAction(a.text()) aseq.triggered.connect(lambda arg1=a.data( ), seq=a.data(): self._changeActiveAppInitAndLoad( self.model.data(index, Qt.DisplayRole), seq, True)) # pylint: enable=cell-var-from-loop s2.append(aseq) m2.addActions(s1) m3.addActions(s2) m.exec_(self.treeView.mapToGlobal(point)) return if self.model.isSubConfigParent( index) == Configuration.CONFIG_TYPE_APPLICATION: m = QMenu() a = QAction("Add application") m.addAction(a) a = m.exec_(self.treeView.mapToGlobal(point)) if a is not None: self._configuration.addNewApplication() return if self.model.isSubConfigParent( index) == Configuration.CONFIG_TYPE_COMPOSITE: m = QMenu() a = QAction("Add composite filter") m.addAction(a) a = m.exec_(self.treeView.mapToGlobal(point)) if a is not None: self._configuration.addNewCompositeFilter() return
def show_rightclick_menu(self): """ :type self: SteamAccountSwitcherGui """ right_menu = QMenu() selected = self.accounts_list.currentItem() if not self.accounts_list.selectedItems(): add_account_action = QAction(_("Add account"), self) add_account_action.triggered.connect( lambda: self.account_dialog(True)) right_menu.addAction(add_account_action) right_menu.exec_(QCursor.pos()) return login_name = selected.data(5) account = self.switcher.settings["users"].get(login_name, {}) login_action = QAction(_("Login"), self) edit_action = QAction(_("Edit"), self) delete_action = QAction(_("Delete"), self) open_profile_action = QAction(_("Steam profile"), self) steampage_menu = QMenu(_("Steam profile"), self) edit_action.setIcon(QIcon.fromTheme("document-edit")) delete_action.setIcon(QIcon.fromTheme("edit-delete")) open_profile_action.setIcon(QIcon.fromTheme("internet-web-browser")) right_menu.addActions([login_action, edit_action, delete_action]) right_menu.addSeparator() right_menu.addAction(open_profile_action) right_menu.addMenu(steampage_menu) login_action.triggered.connect(lambda: self.steam_login(login_name)) edit_action.triggered.connect(lambda: self.account_dialog()) delete_action.triggered.connect( lambda: self.remove_account(login_name)) open_profile_action.triggered.connect( lambda: self.open_steam_profile(account)) steampage_menu.triggered.connect( lambda: self.open_steam_profile(account)) steampage_menu_actions = QActionGroup(steampage_menu) steampage_menu_inventory = QAction(_('Inventory'), steampage_menu_actions, checkable=True, data="nothing") open_profile_action.setDisabled(True) if account.get("steam_user", {}).get("profileurl"): open_profile_action.setEnabled(True) steampage_menu.addActions([steampage_menu_inventory]) if self.accounts_list.selectedItems(): right_menu.exec_(QCursor.pos())
class NavToolButton(QToolButton): """ Widget to allow navigating a JumpHistory stack """ triggered = Signal() triggeredFromMenu = Signal(int) def __init__(self, jump_history: JumpHistory, direction_forward: bool = False, parent=None): super().__init__(parent) self._dir_fwd = direction_forward self._jump_history = jump_history self.setPopupMode(QToolButton.MenuButtonPopup) self._init_menu() if direction_forward: lbl = 'Forward' ico = QIcon(os.path.join(IMG_LOCATION, 'toolbar-forward.png')) else: lbl = 'Back' ico = QIcon(os.path.join(IMG_LOCATION, 'toolbar-previous.png')) a = QAction(ico, lbl, self) a.triggered.connect(self._on_button_activated) self.setDefaultAction(a) def _on_button_activated(self): self.triggered.emit() def _on_menu_action_activated(self, checked): # pylint:disable=unused-argument pos = self.sender().data() self.triggeredFromMenu.emit(pos) def _init_menu(self): self._menu = QMenu() pos = self._jump_history.pos if pos < 0: pos += len(self._jump_history.history) actions = [] for i, point in enumerate(self._jump_history.history): a = QAction(f'{i}: {point:x}', self) a.setData(i) a.setCheckable(True) a.setChecked(pos == i) a.triggered.connect(self._on_menu_action_activated) actions.append(a) actions.reverse() self._menu.addActions(actions) self.setMenu(self._menu) def mousePressEvent(self, e: QMouseEvent): self._init_menu() super().mousePressEvent(e)
def __init__(self, parent): super().__init__(QIcon("resources/translate-icon.svg"), ) self.parent = parent self.show() self.clear_action = QAction("Clear", self) self.enable_action = QAction("Enable clipboard translation", self) self.enable_action.setCheckable(True) self.show_panel_action = QAction("Show panel", self) self.show_panel_action.setCheckable(True) self.on_top_action = QAction("Always on top", self) self.on_top_action.setCheckable(True) self.not_fix_action = QAction("Move to avoid mouse", self) self.not_fix_action.setCheckable(True) self.follow_cursor_action = QAction("Move to follow mouse", self) self.follow_cursor_action.setCheckable(True) close_action = QAction("Exit", self) QObject.connect(self.clear_action, SIGNAL("triggered()"), self.parent.clear_button, SLOT("click()")) QObject.connect(self.on_top_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_on_top(bool)")) QObject.connect(self.show_panel_action, SIGNAL("triggered(bool)"), self.parent, SLOT("show_interface(bool)")) QObject.connect(self.not_fix_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_not_fix(bool)")) QObject.connect(self.enable_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_enable(bool)")) QObject.connect(self.follow_cursor_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_follow_cursor(bool)")) QObject.connect(close_action, SIGNAL("triggered()"), self.parent, SLOT("close()")) menu = QMenu() menu.addActions([ self.clear_action, self.enable_action, self.show_panel_action, self.on_top_action, self.not_fix_action, self.show_panel_action, self.follow_cursor_action, close_action ]) self.setContextMenu(menu) QObject.connect(menu, SIGNAL("aboutToShow()"), self, SLOT("refresh()"))
def get_context_menu(self): if self.document() is None: return QMenu() # TODO: Anything in sel? # Get the highlighted item under_cursor = self.node_under_cursor() mnu = QMenu() self._selected_node = None if isinstance(under_cursor, CBinaryOp) \ and "vex_stmt_idx" in under_cursor.tags \ and "vex_block_addr" in under_cursor.tags: # operator in selection self._selected_node = under_cursor mnu.addActions(self.operator_actions) if isinstance(under_cursor, CFunctionCall) \ and "vex_block_addr" in under_cursor.tags \ and "ins_addr" in under_cursor.tags: # function call in selection self._selected_node = under_cursor mnu.addActions(self.call_actions) if isinstance(under_cursor, CVariable): # variable in selection self._selected_node = under_cursor mnu.addActions(self.variable_actions) else: mnu.addActions(self.default_actions) return mnu
def createWorkbenchPopupMenu(self, index: QModelIndex) -> None: # Create a popup menu when workbench is right-clicked over a valid frame name # Menu display delete and remove options frameName: str = index.data(Qt.DisplayRole) pMenu = QMenu(self) # Reuse MainWindow actions csvAction = self.parentWidget().aWriteCsv pickleAction = self.parentWidget().aWritePickle # Set correct args for the clicked row csvAction.setOperationArgs(w=self.workbenchModel, frameName=frameName) pickleAction.setOperationArgs(w=self.workbenchModel, frameName=frameName) deleteAction = QAction('Remove', pMenu) deleteAction.triggered.connect( lambda: self.workbenchModel.removeRow(index.row())) pMenu.addActions([csvAction, pickleAction, deleteAction]) pMenu.popup(QtGui.QCursor.pos())
def get_context_menu(self): if self.document() is None: return QMenu() # TODO: Anything in sel? # Get the highlighted item under_cursor = self.node_under_cursor() mnu = QMenu() self._selected_node = None if isinstance(under_cursor, CBinaryOp) \ and "vex_stmt_idx" in under_cursor.tags \ and "vex_block_addr" in under_cursor.tags: # operator in selection self._selected_node = under_cursor mnu.addActions(self.operator_actions) if isinstance(under_cursor, CFunctionCall) \ and "vex_block_addr" in under_cursor.tags \ and "ins_addr" in under_cursor.tags: # function call in selection self._selected_node = under_cursor mnu.addActions(self.call_actions) if isinstance( under_cursor, (CVariable, CIndexedVariable, CVariableField, CStructField)): # variable in selection self._selected_node = under_cursor mnu.addActions(self.variable_actions) if isinstance(under_cursor, CFunction): # decompiled function name in selection self._selected_node = under_cursor mnu.addActions(self.function_name_actions) for entry in self.workspace.plugins.build_context_menu_functions( [self.workspace.instance.kb.functions[under_cursor.name]]): Menu.translate_element(mnu, entry) else: mnu.addActions(self.default_actions) for entry in self.workspace.plugins.build_context_menu_node( under_cursor): Menu.translate_element(mnu, entry) return mnu
def contextMenuEvent(self, event): """Shows context menu. Args: event (QContextMenuEvent) """ index = self.indexAt(event.pos()) index = self.model().mapToSource(index) if not index.isValid() or self.source_model.index_within_top_left( index): pivot_menu = QMenu(self) title = TitleWidgetAction("Pivot", self._spine_db_editor) pivot_menu.addAction(title) pivot_menu.addActions( self._spine_db_editor.pivot_action_group.actions()) pivot_menu.exec_(event.globalPos()) return self._refresh_selected_indexes() self._update_actions_availability() _prepare_plot_in_window_menu(self._plot_in_window_menu) self._menu.exec_(event.globalPos())
def get_context_menu(self): if self.document() is None: return QMenu() doc: 'QCodeDocument' = self.document() # determine the current status cursor = self.textCursor() pos = cursor.position() current_node = doc.get_node_at_position(pos) if current_node is not None: under_cursor = current_node else: # nothing is under the cursor under_cursor = None # TODO: Anything in sel? # Get the highlighted item mnu = QMenu() if isinstance(under_cursor, CBinaryOp) \ and "vex_stmt_idx" in under_cursor.tags \ and "vex_block_addr" in under_cursor.tags: # operator in selection self._selected_node = under_cursor mnu.addActions(self.operator_actions) if isinstance(under_cursor, CFunctionCall) \ and "vex_block_addr" in under_cursor.tags \ and "ins_addr" in under_cursor.tags: # function call in selection self._selected_node = under_cursor mnu.addActions(self.call_actions) else: mnu.addActions(self.default_actions) return mnu
def contextMenuEvent(self, event): menu = QMenu() menu.setStyleSheet(Theme.contextMenu.style) menu.setFont(Theme.contextMenu.font) menu.addActions(self.actions()) menu.exec_(event.globalPos())
class FE14CharacterEditor(Ui_FE14CharacterEditor): def __init__(self, is_person=False, parent=None): super().__init__(parent) self.is_person = is_person self.module: TableModule = locator.get_scoped( "ModuleService").get_module("Characters") self.proxy_model = QSortFilterProxyModel() self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.proxy_model.setSourceModel(self.module.entries_model) self.characters_list_view.setModel(self.proxy_model) self.selection: Optional[PropertyContainer] = None self.character_details_form_1 = PropertyForm( self.module.element_template, category="character_description_1") self.character_details_form_contents_1.setLayout( self.character_details_form_1) self.character_details_form_2 = PropertyForm( self.module.element_template, category="character_description_2") self.character_details_form_contents_2.setLayout( self.character_details_form_2) self.character_details_form_2.fix_editor_width(100) self.stats_editor = MergedStatsEditor( ["Bases", "Growths", "Modifiers", "Penalties", "Bonuses"]) self.stats_form = PropertyForm(self.module.element_template, category="stats") self.stats_layout.addWidget(self.stats_editor) self.stats_layout.addLayout(self.stats_form) self.skills_form = PropertyForm(self.module.element_template, category="skills", sort_editors=True) self.skills_contents.setLayout(self.skills_form) self.flags_editor = MergedFlagsEditor( ["Bitflags (1)", "Bitflags (2)", "Bitflags (3)", "Bitflags (4)"], self.module.element_template) self.flags_editor_2 = MergedFlagsEditor( ["Bitflags (5)", "Bitflags (6)", "Bitflags (7)", "Bitflags (8)"], self.module.element_template) self.misc_form = PropertyForm(self.module.element_template, category="misc") self.misc_layout.addWidget(self.flags_editor) self.misc_layout.addWidget(self.flags_editor_2) self.misc_layout.addLayout(self.misc_form) self.ids_form = PropertyForm(self.module.element_template, category="ids") self.ids_tab.setLayout(self.ids_form) self.classes_form = PropertyForm(self.module.element_template, category="classes", sort_editors=True) self.classes_tab.setLayout(self.classes_form) if not self.is_person: self.dialogue_tab = DialogueEditor() self.supports_tab = QWidget() self.supports_layout = QHBoxLayout() self.supports_widget = FE14SupportWidget() self.supports_scroll = QScrollArea() self.supports_scroll_contents = QWidget() self.supports_scroll.setWidget(self.supports_scroll_contents) self.supports_scroll.setWidgetResizable(True) self.supports_layout.addWidget(self.supports_widget) self.supports_layout.addWidget(self.supports_scroll) self.supports_tab.setLayout(self.supports_layout) self.supports_form = PropertyForm(self.module.element_template, category="supports") self.supports_scroll_contents.setLayout(self.supports_form) self.tab_widget.addTab(self.supports_tab, "Supports") self.tab_widget.addTab(self.dialogue_tab, "Dialogue") self.context_menu = QMenu(self) self.context_menu.addActions( [self.action_add, self.action_remove, self.action_copy_to]) self.clear_selection_shortcut = QShortcut(QKeySequence.Cancel, self) self._install_signals() self._clear() def set_module(self, module: TableModule): self.module = module if self.module: self.proxy_model.setSourceModel(self.module.entries_model) else: self.proxy_model.setSourceModel(None) self.setEnabled(self.module is not None) self._clear() def _on_context_menu_requested(self, point: QPoint): self.context_menu.exec_(self.characters_list_view.mapToGlobal(point)) def _install_signals(self): self.characters_list_view.selectionModel().currentRowChanged.connect( self._update_selection) self.characters_list_view.customContextMenuRequested.connect( self._on_context_menu_requested) self.search_bar.textChanged.connect(self._update_filter) self.action_add.triggered.connect(self._on_add_character_triggered) self.action_remove.triggered.connect( self._on_remove_character_triggered) self.action_copy_to.triggered.connect(self._on_copy_to_triggered) self.clear_selection_shortcut.activated.connect(self._clear) if self.character_details_form_1.editors['Name'] != None: self.character_details_form_1.editors[ 'Name'].value_editor.editingFinished.connect( self._update_conversation_widget) def _clear(self): self.characters_list_view.clearSelection() self.characters_list_view.selectionModel().clearCurrentIndex() def _update_selection(self, index: QModelIndex): self.selection = self.proxy_model.data(index, QtCore.Qt.UserRole) self.portraits_tab.update_target(self.selection) self.character_details_form_1.update_target(self.selection) self.character_details_form_2.update_target(self.selection) self.stats_editor.update_target(self.selection) self.ids_form.update_target(self.selection) self.classes_form.update_target(self.selection) self.stats_form.update_target(self.selection) self.skills_form.update_target(self.selection) self.flags_editor.update_target(self.selection) self.flags_editor_2.update_target(self.selection) self.misc_form.update_target(self.selection) if not self.is_person: self.dialogue_tab.update_target(self.selection) self.supports_widget.update_target(self.selection) self.supports_form.update_target(self.selection) if self.selection: locator.get_scoped("SpriteService").get_sprite_for_character( self.selection, 0) self.action_remove.setEnabled(self.selection is not None) self.action_copy_to.setEnabled(self.selection is not None) self._update_portrait_box() def _update_portrait_box(self): portrait_service = locator.get_scoped("PortraitService") mini_portraits = portrait_service.get_sorted_portraits_for_character( self.selection, "bu") if mini_portraits: _, texture = mini_portraits[0] scene = QGraphicsScene() scene.addPixmap(QPixmap.fromImage(texture.image())) self.portrait_display.setScene(scene) else: self.portrait_display.setScene(None) def _update_filter(self): self.proxy_model.setFilterRegExp(self.search_bar.text()) def _on_add_character_triggered(self): model = self.module.entries_model model.insertRow(model.rowCount()) source = self.module.entries[0] destination = self.module.entries[-1] source.copy_to(destination) # Update any present conversation widget with the new obj list self._update_conversation_widget() def _on_remove_character_triggered(self): if self.characters_list_view.currentIndex().isValid(): model = self.module.entries_model model.removeRow(self.characters_list_view.currentIndex().row()) model.beginResetModel() model.endResetModel() # Update any present conversation widget with the new obj list self._update_conversation_widget() def _update_conversation_widget(self): for editor in self.supports_widget.service._conversation_editors: editor: FE14ConversationEditor character_list = list() [ character_list.append(child[1]) for child in self.module.children() ] editor.text_area._character_list = character_list def _on_copy_to_triggered(self): if not self.selection: return logging.info("Beginning copy to for " + self.module.name) choices = [] for i in range(0, len(self.module.entries)): choices.append( str(i + 1) + ". " + self.module.entries[i].get_display_name()) choice = QInputDialog.getItem(self, "Select Destination", "Destination", choices) if choice[1]: for i in range(0, len(choices)): if choice[0] == choices[i]: self.selection.copy_to(self.module.entries[i]) # Update any present conversation widget with the new obj list self._update_conversation_widget() else: logging.info("No choice selected for " + self.module.name + " copy to. Aborting.")
class SchnuffiWindow(QtWidgets.QMainWindow): # Error signal err_sig = QtCore.Signal(str) export_sig = QtCore.Signal() non_exportable_widgets = list() def __init__(self, pos_app): """ :param modules.main_app.SchnuffiApp pos_app: """ super(SchnuffiWindow, self).__init__() self.pos_app = pos_app LOGGER.setLevel(logging.ERROR) SetupWidget.from_ui_file(self, Resource.ui_paths.get(UI_MAIN_WINDOW)) LOGGER.setLevel(logging.DEBUG) self.file_win = None # -- Comparision thread -- self.cmp_thread = QtCore.QThread(self) self.cmp_queue = Queue(-1) # -- Timer -- self.intro_timer = QtCore.QTimer() self.intro_timer.setSingleShot(True) self.intro_timer.setInterval(500) # -- Add item worker -- self.item_worker = QtCore.QTimer() self.item_worker.setInterval(15) self.remaining_items = 0 self.item_chunk_size = 35 self.export = ExportActionList(self, self) self.info_overlay = InfoOverlay(self) self.undo_grp = QUndoGroup(self) self.widget_list = [self.AddedWidget, self.ModifiedWidget, self.RemovedWidget, self.switchesWidget, self.looksWidget, self.posOldWidget, self.posNewWidget] self.setup_widgets() # --- Create undo menu --- self.undo_menu = QMenu(_('Undo'), self) # Create undo/redo actions from undo_grp self.redo = self.undo_grp.createRedoAction(self, _('Wiederherstellen')) self.redo.setShortcut(QKeySequence('Ctrl+Y')) self.undo = self.undo_grp.createUndoAction(self, _('Rückgängig')) self.undo.setShortcut(QKeySequence('Ctrl+Z')) # add menu self.undo_menu.addActions((self.undo, self.redo)) self.menuBar().addMenu(self.undo_menu) self.non_exportable_widgets = (self.switchesWidget, self.looksWidget, self.errorTextWidget, self.AddedWidget, self.RemovedWidget) self.show() def setup_widgets(self): # Buttons self.filterLabel.mouseDoubleClickEvent = self.sort_all_headers self.expandBtn.pressed.connect(self.expand_all_items) # Work Timer self.item_worker.timeout.connect(self.add_widget_item) self.progressBar.hide() # Menu self.actionOpen.triggered.connect(self.open_file_window) self.actionBeenden.triggered.connect(self.close) self.actionExport.triggered.connect(self.export.export_selection) self.actionExportPos.triggered.connect(self.export.export_updated_pos_xml) # File display self.file_name_box: QGroupBox self.file_name_box.setTitle(_('Dateien Alt - Neu')) # Filter Line Edit self.lineEditFilter: QLineEdit self.lineEditFilter.setPlaceholderText(_('Zum filtern im Baum tippen. Leerzeichen separierte Begriffe werden ' 'mit UND gefunden. zB. t_mirko ks_bunt findet alle bunten Mirkos.')) for widget in self.widget_list: widget.clear() widget.undo_stack = QUndoStack(self.undo_grp) widget.setItemDelegate(KnechtValueDelegate(widget)) # Overlay widget.info_overlay = InfoOverlay(widget) # Setup Filtering widget.filter = TreeWidgetFilter(self, widget, self.lineEditFilter) widget.setAlternatingRowColors(True) self.intro_timer.timeout.connect(self.show_intro_msg) self.intro_timer.start() # Exporter signals self.err_sig.connect(self.error_msg) self.export_sig.connect(self.export_success) # Tab Changed self.widgetTabs.currentChanged.connect(self.tab_changed) def widget_with_focus(self): """ Return the current or last QTreeWidget in focus """ return self.pos_app.tree_with_focus() def tab_changed(self, idx): tab_widget = self.widgetTabs.widget(idx) LOGGER.debug('Tab change: %s, %s', idx, tab_widget.objectName()) self.menuExport.setEnabled(True) # Apply filter to active tab for widget in tab_widget.children(): if widget in self.widget_list: widget.filter.start() if widget in self.non_exportable_widgets: self.menuExport.setEnabled(False) def closeEvent(self, close_event): if self.cmp_thread: self.cmp_thread.quit() self.cmp_thread.wait(800) close_event.accept() def show_intro_msg(self): self.info_overlay.display_confirm( _('<h4><img src=":/main/assignment.svg" width="21" height="21" style="float: left;">' 'POS Schnuffi</h4>' '<p>Lädt zwei POS Xml Dateien und vergleicht hinzugefügte, entfernte und geänderte ' 'Action Listen.</p>' '<p>Zeigt nur Änderungen in actors vom Typ <i>appearance</i> und <i>switch</i> an! ' 'State Objects werden ignoriert da sie nur innerhalb derselben Xml relevant sind.</p>'), (('[X]', None), )) def export_success(self): self.info_overlay.display('Export succeeded.') def error_msg(self, error_str): # self.widgetTabs.setCurrentIndex(0) self.info_overlay.display_exit() self.info_overlay.display_confirm(error_str, (('[X]', None), )) def sort_all_headers(self, event=None): for widget in self.widget_list: sort_widget(widget) def expand_all_items(self): for widget in self.widget_list: widget.hide() widget.expandAll() for widget in self.widget_list: widget.show() def open_file_window(self): self.file_win = FileWindow(self, self) def compare(self): self.clear_item_queue() if self.cmp_thread is not None: if self.cmp_thread.isRunning(): self.error_msg(_('POS Schnuffi Vergleichsthread läuft bereits.')) return for widget in self.widget_list: widget.clear() widget.hide() self.cmp_thread = GuiCompare(self.file_win.old_file_dlg.path, self.file_win.new_file_dlg.path, self.widget_list, self.cmp_queue) self.cmp_thread.add_item.connect(self.request_item_add) self.cmp_thread.no_difference.connect(self.no_difference_msg) self.cmp_thread.finished.connect(self.finished_compare) self.cmp_thread.error_report.connect(self.add_error_report) # Prepare add item worker self.item_worker.stop() self.remaining_items = 0 self.progressBar.setMaximum(0) self.progressBar.setValue(0) self.cmp_thread.start() self.statusBar().showMessage(_('POS Daten werden geladen und verglichen...'), 8000) def finished_compare(self): self.sort_all_headers() self.statusBar().showMessage(_('POS Daten laden und vergleichen abgeschlossen. Bäume werden befüllt.'), 8000) self.old_file_label.setText(Path(self.file_win.old_file_dlg.path).name) self.new_file_label.setText(Path(self.file_win.new_file_dlg.path).name) def _item_worker_finished(self): for widget in self.widget_list: widget.show() # self.widgetTabs.setCurrentIndex(0) # self.info_overlay.display_exit() self.info_overlay.display(_('POS Daten laden und vergleichen abgeschlossen.'), 5000, True) def no_difference_msg(self): self.info_overlay.display_confirm(_('Keine Unterschiede gefunden.'), (('[X]', None),)) self.statusBar().showMessage(_('POS Daten laden und vergleichen abgeschlossen. Keine Unterschiede gefunden.') , 8000) self._item_worker_finished() def request_item_add(self): self.remaining_items += 1 self.progressBar.setMaximum(max(self.remaining_items, self.progressBar.maximum())) if not self.item_worker.isActive(): self.item_worker.start() self.progressBar.show() def add_widget_item(self): if not self.remaining_items: self.item_worker.stop() self.progressBar.hide() self._item_worker_finished() return count = 0 while self.remaining_items: item, target_widget = self.cmp_queue.get() self.color_items(item) target_widget.addTopLevelItem(item) self.remaining_items -= 1 self.cmp_queue.task_done() count += 1 self.progressBar.setValue(self.progressBar.value() + 1) if count >= self.item_chunk_size: break def add_error_report(self, error_report, error_num): # Reset error tab name widget_idx = self.widgetTabs.indexOf(self.errorsTab) self.widgetTabs.setTabText(widget_idx, _('Error')) if error_num: # Switch to error tab and report number of errors in tab title self.widgetTabs.setCurrentIndex(widget_idx) self.widgetTabs.setTabText(widget_idx, f'Error ({error_num})') self.errorTextWidget.clear() self.errorTextWidget.append(error_report) @staticmethod def color_items(parent_item): for c in range(0, parent_item.childCount()): item = parent_item.child(c) value = item.text(1) old_value = item.text(2) # Skip actor's without values if not value and not old_value: continue if not value: # No new value, actor removed for c in range(0, 4): item.setForeground(c, QBrush(QColor(190, 90, 90))) elif not old_value and value: # New actor added for c in range(0, 4): item.setForeground(c, QBrush(QColor(90, 140, 90))) def clear_item_queue(self): if self.cmp_queue.qsize(): LOGGER.debug('Clearing %s items from the queue.', self.cmp_queue.qsize()) while not self.cmp_queue.empty(): try: _, _ = self.cmp_queue.get() except Exception as e: LOGGER.error('Error clearing queue %s', e) self.cmp_queue.task_done() LOGGER.debug('Queue cleared!')
class Ui_FE14MapEditor(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.toolbar = QToolBar() self.toggle_coordinate_type_action = QAction("Toggle Coordinate Type") self.refresh_action = QAction("Refresh") self.refresh_action.setShortcut(QKeySequence("Ctrl+R")) self.copy_spawn_action = QAction("Copy Spawn") self.copy_spawn_action.setShortcut(QKeySequence("Ctrl+C")) self.paste_spawn_action = QAction("Paste Spawn") self.paste_spawn_action.setShortcut(QKeySequence("Ctrl+V")) self.add_spawn_action = QAction("Add Spawn") self.delete_spawn_action = QAction("Delete Spawn") self.add_group_action = QAction("Add Group") self.delete_group_action = QAction("Delete Group") self.add_tile_action = QAction("Add Tile") self.toggle_mode_action = QAction("Toggle Mode") self.undo_action = QAction("Undo") self.undo_action.setShortcut(QKeySequence("Ctrl+Z")) self.redo_action = QAction("Redo") self.redo_action.setShortcut(QKeySequence("Ctrl+Shift+Z")) self.toolbar.addActions( [self.toggle_coordinate_type_action, self.refresh_action]) self.toolbar.addSeparator() self.toolbar.addActions([ self.copy_spawn_action, self.paste_spawn_action, self.add_spawn_action, self.delete_spawn_action, self.add_group_action, self.delete_group_action ]) self.toolbar.addSeparator() self.toolbar.addAction(self.add_tile_action) self.toolbar.addSeparator() self.toolbar.addAction(self.toggle_mode_action) self.toolbar.addSeparator() self.toolbar.addActions([self.undo_action, self.redo_action]) self.addToolBar(self.toolbar) self.model_view = QTreeView() self.model_view.setHeaderHidden(True) self.grid = FE14MapGrid() self.grid_scroll = QScrollArea() self.grid_scroll.setWidgetResizable(True) self.grid_scroll.setWidget(self.grid) self.tile_list = QListView() self.terrain_pane = FE14TerrainEditorPane() self.spawn_pane = FE14SpawnEditorPane() self.model_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.model_view_context_menu = QMenu() self.model_view_context_menu.addActions( [self.toggle_coordinate_type_action, self.refresh_action]) self.model_view_context_menu.addSeparator() self.model_view_context_menu.addActions([ self.copy_spawn_action, self.paste_spawn_action, self.add_spawn_action, self.delete_spawn_action, self.add_group_action, self.delete_group_action ]) self.model_view_context_menu.addSeparator() self.model_view_context_menu.addAction(self.add_tile_action) self.model_view_context_menu.addSeparator() self.model_view_context_menu.addAction(self.toggle_mode_action) self.model_view_context_menu.addSeparator() self.model_view_context_menu.addActions( [self.undo_action, self.redo_action]) self.status_bar = QStatusBar() self.coordinate_type_label = QLabel() self.status_bar.addPermanentWidget(self.coordinate_type_label) self.setStatusBar(self.status_bar) self.main_widget = QSplitter() self.main_widget.setOrientation(QtCore.Qt.Horizontal) self.main_widget.addWidget(self.model_view) self.main_widget.addWidget(self.grid_scroll) self.main_widget.addWidget(self.spawn_pane) self.main_widget.addWidget(self.terrain_pane) self.setCentralWidget(self.main_widget)
def __init__( self, document: Optional[vp.Document] = None, view_mode: ViewMode = ViewMode.PREVIEW, show_pen_up: bool = False, show_points: bool = False, parent=None, ): super().__init__(parent) self._settings = QSettings() self._settings.beginGroup("viewer") self.setWindowTitle("vpype viewer") self.setStyleSheet(""" QToolButton:pressed { background-color: rgba(0, 0, 0, 0.2); } """) self._viewer_widget = QtViewerWidget(parent=self) # setup toolbar self._toolbar = QToolBar() self._icon_size = QSize(32, 32) self._toolbar.setIconSize(self._icon_size) view_mode_grp = QActionGroup(self._toolbar) if _DEBUG_ENABLED: act = view_mode_grp.addAction("None") act.setCheckable(True) act.setChecked(view_mode == ViewMode.NONE) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.NONE)) act = view_mode_grp.addAction("Outline Mode") act.setCheckable(True) act.setChecked(view_mode == ViewMode.OUTLINE) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.OUTLINE)) act = view_mode_grp.addAction("Outline Mode (Colorful)") act.setCheckable(True) act.setChecked(view_mode == ViewMode.OUTLINE_COLORFUL) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.OUTLINE_COLORFUL)) act = view_mode_grp.addAction("Preview Mode") act.setCheckable(True) act.setChecked(view_mode == ViewMode.PREVIEW) act.triggered.connect( functools.partial(self.set_view_mode, ViewMode.PREVIEW)) self.set_view_mode(view_mode) # VIEW MODE # view modes view_mode_btn = QToolButton() view_mode_menu = QMenu(view_mode_btn) act = view_mode_menu.addAction("View Mode:") act.setEnabled(False) view_mode_menu.addActions(view_mode_grp.actions()) view_mode_menu.addSeparator() # show pen up act = view_mode_menu.addAction("Show Pen-Up Trajectories") act.setCheckable(True) act.setChecked(show_pen_up) act.toggled.connect(self.set_show_pen_up) self._viewer_widget.engine.show_pen_up = show_pen_up # show points act = view_mode_menu.addAction("Show Points") act.setCheckable(True) act.setChecked(show_points) act.toggled.connect(self.set_show_points) self._viewer_widget.engine.show_points = show_points # preview mode options view_mode_menu.addSeparator() act = view_mode_menu.addAction("Preview Mode Options:") act.setEnabled(False) # pen width pen_width_menu = view_mode_menu.addMenu("Pen Width") act_grp = PenWidthActionGroup(0.3, parent=pen_width_menu) act_grp.triggered.connect(self.set_pen_width_mm) pen_width_menu.addActions(act_grp.actions()) self.set_pen_width_mm(0.3) # pen opacity pen_opacity_menu = view_mode_menu.addMenu("Pen Opacity") act_grp = PenOpacityActionGroup(0.8, parent=pen_opacity_menu) act_grp.triggered.connect(self.set_pen_opacity) pen_opacity_menu.addActions(act_grp.actions()) self.set_pen_opacity(0.8) # debug view if _DEBUG_ENABLED: act = view_mode_menu.addAction("Debug View") act.setCheckable(True) act.toggled.connect(self.set_debug) # rulers view_mode_menu.addSeparator() act = view_mode_menu.addAction("Show Rulers") act.setCheckable(True) val = bool(self._settings.value("show_rulers", True)) act.setChecked(val) act.toggled.connect(self.set_show_rulers) self._viewer_widget.engine.show_rulers = val # units units_menu = view_mode_menu.addMenu("Units") unit_action_grp = QActionGroup(units_menu) unit_type = UnitType(self._settings.value("unit_type", UnitType.METRIC)) act = unit_action_grp.addAction("Metric") act.setCheckable(True) act.setChecked(unit_type == UnitType.METRIC) act.setData(UnitType.METRIC) act = unit_action_grp.addAction("Imperial") act.setCheckable(True) act.setChecked(unit_type == UnitType.IMPERIAL) act.setData(UnitType.IMPERIAL) act = unit_action_grp.addAction("Pixel") act.setCheckable(True) act.setChecked(unit_type == UnitType.PIXELS) act.setData(UnitType.PIXELS) unit_action_grp.triggered.connect(self.set_unit_type) units_menu.addActions(unit_action_grp.actions()) self._viewer_widget.engine.unit_type = unit_type view_mode_btn.setMenu(view_mode_menu) view_mode_btn.setIcon(load_icon("eye-outline.svg")) view_mode_btn.setText("View") view_mode_btn.setPopupMode(QToolButton.InstantPopup) view_mode_btn.setStyleSheet( "QToolButton::menu-indicator { image: none; }") self._toolbar.addWidget(view_mode_btn) # LAYER VISIBILITY self._layer_visibility_btn = QToolButton() self._layer_visibility_btn.setIcon( load_icon("layers-triple-outline.svg")) self._layer_visibility_btn.setText("Layer") self._layer_visibility_btn.setMenu(QMenu(self._layer_visibility_btn)) self._layer_visibility_btn.setPopupMode(QToolButton.InstantPopup) self._layer_visibility_btn.setStyleSheet( "QToolButton::menu-indicator { image: none; }") self._toolbar.addWidget(self._layer_visibility_btn) # FIT TO PAGE fit_act = self._toolbar.addAction(load_icon("fit-to-page-outline.svg"), "Fit") fit_act.triggered.connect(self._viewer_widget.engine.fit_to_viewport) # RULER # TODO: not implemented yet # self._toolbar.addAction(load_icon("ruler-square.svg"), "Units") # MOUSE COORDINATES> self._mouse_coord_lbl = QLabel("") self._mouse_coord_lbl.setMargin(6) self._mouse_coord_lbl.setAlignment(Qt.AlignVCenter | Qt.AlignRight) self._mouse_coord_lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self._toolbar.addWidget(self._mouse_coord_lbl) # noinspection PyUnresolvedReferences self._viewer_widget.mouse_coords.connect( self.set_mouse_coords) # type: ignore # setup horizontal layout for optional side widgets self._hlayout = QHBoxLayout() self._hlayout.setSpacing(0) self._hlayout.setMargin(0) self._hlayout.addWidget(self._viewer_widget) widget = QWidget() widget.setLayout(self._hlayout) # setup global vertical layout layout = QVBoxLayout() layout.setSpacing(0) layout.setMargin(0) layout.addWidget(self._toolbar) layout.addWidget(widget) self.setLayout(layout) if document is not None: self.set_document(document)
class SimpleEditor(QWidget, Ui_simple_editor): def __init__(self, module: TableModule): super().__init__() self.setupUi(self) self.module = module self.selection = None self.model = self.module.entries_model self.proxy_model = QtCore.QSortFilterProxyModel() self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.proxy_model.setSourceModel(self.model) self.list_view.setModel(self.proxy_model) self.clear_selection_shortcut = QShortcut(QKeySequence.Cancel, self) self.list_view.selectionModel().currentRowChanged.connect( self._update_selection) self.clear_selection_shortcut.activated.connect( lambda: self._update_selection(QModelIndex())) self.search_field.textChanged.connect(self._update_filter) self.add_button.clicked.connect(self._on_add_pressed) self.remove_button.clicked.connect(self._on_remove_pressed) self.copy_to_button.clicked.connect(self._on_copy_to_pressed) self.property_form = PropertyForm(module.element_template) self.form_layout.setLayout(self.property_form) self.setWindowTitle(self.module.name) self.setWindowIcon(QIcon("paragon.ico")) self.copy_to_button.setEnabled(False) self.remove_button.setEnabled(False) self.splitter.setSizes([300, 680]) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.list_context_menu = QMenu(self) self.add_action = QAction("Add Element") self.add_action.triggered.connect(self._on_add_pressed) self.remove_action = QAction("Remove Element") self.remove_action.triggered.connect(self._on_remove_pressed) self.copy_to_action = QAction("Copy To") self.copy_to_action.triggered.connect(self._on_copy_to_pressed) self.list_context_menu.addActions( [self.add_action, self.remove_action, self.copy_to_action]) self.list_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.list_view.customContextMenuRequested.connect( self._on_list_context_menu_requested) if self.module.disable_add_remove: self.add_action.setEnabled(False) self.remove_action.setEnabled(False) self.add_button.setVisible(False) self.remove_button.setVisible(False) self._update_selection(QModelIndex()) logging.info("Generated SimpleEditor for " + self.module.name) def _on_list_context_menu_requested(self, point: QPoint): self.list_context_menu.exec_(self.list_view.mapToGlobal(point)) def _update_selection(self, index: QtCore.QModelIndex): logging.info("Updating " + self.module.name + " to selected index " + str(index.row())) self.selection = self.proxy_model.data(index, QtCore.Qt.UserRole) self.property_form.update_target(self.selection) self.scrollArea.setEnabled(self.selection is not None) self.remove_action.setEnabled(self.selection is not None) self.copy_to_action.setEnabled(self.selection is not None) self.remove_button.setEnabled(self.selection is not None) self.copy_to_button.setEnabled(self.selection is not None) if not self.selection: self.list_view.clearSelection() def _update_filter(self): self.proxy_model.setFilterRegExp(self.search_field.text()) def _on_add_pressed(self): # Add the new entry. self.model.insertRow(self.model.rowCount()) # Copy the first entry's properties into the new entry. source = self.module.entries[0] dest = self.module.entries[len(self.module.entries) - 1] source.copy_to(dest) def _on_remove_pressed(self): for i in range(0, len(self.module.entries)): if self.module.entries[i] == self.selection: self.model.removeRow(i) self.model.beginResetModel() self.model.endResetModel() return def _on_copy_to_pressed(self): logging.info("Beginning copy to for " + self.module.name) choices = [] for i in range(0, len(self.module.entries)): choices.append( str(i + 1) + ". " + self.model.data(self.model.index(i, 0), QtCore.Qt.DisplayRole)) choice = QInputDialog.getItem(self, "Select Destination", "Destination", choices) if choice[1]: for i in range(0, len(choices)): if choice[0] == choices[i]: self.selection.copy_to(self.module.entries[i]) else: logging.info("No choice selected for " + self.module.name + " copy to. Aborting.")
class PlaylistView(QTableView): current_index_changed = Signal(QModelIndex) playlist_double_clicked = Signal() filtering = Signal(str) unfiltered = Signal(str) next = Slot(int) previous = Slot(int) @property def mime_Index(self): return 'application/x-original_index' @property def mime_URLS(self): return 'application/x-file-urls' @property def mime_url_count(self): return 'application/x-urls-count' @property def url_delimiter(self): return '\n' @property def open_file_filter(self): return '*.mp4 *.m4v *.mov *.mpg *.mpeg *. mp3 *.m4a *.wmv *.aiff *.wav' def __init__(self, parent=None): super(PlaylistView, self).__init__(parent) self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setDragEnabled(True) self.setAcceptDrops(True) self.setDragDropMode(QAbstractItemView.DragDrop) self.setDropIndicatorShown(True) self.proxy_model = QSortFilterProxyModel(self) self.proxy_model.setSourceModel(PlaylistModel(self)) self.setModel(self.proxy_model) # self.setModel(PlaylistModel()) self.setShowGrid(False) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.verticalHeader().setDefaultSectionSize(16) self.verticalHeader().hide() self.horizontalHeader().setMinimumSectionSize(30) self.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) self.current_index = QModelIndex() self.previousIndex = QModelIndex() self.rubberBand: QRubberBand = QRubberBand(QRubberBand.Rectangle, self) self.isDragging = False self.wasSelected = False self.context_menu = QMenu(self) self.create_context_menu() # self.current_index_changed.connect(self.proxy_model.sourceModel().set_current_index) self.current_index_changed.connect( self.proxy_model.sourceModel().set_current_index) def create_context_menu(self): add_file = createAction(self, 'Add File(s)', self.open) delete_selected = createAction(self, 'Delete selected', self.delete_items) sort_action = createAction(self, 'Sort', self.proxy_model.sourceModel().sort) pickup_dup_action = createAction(self, 'Filter by same title', self.filter_same_title) stop_filtering_action = createAction(self, 'Stop Filtering', self.unfilter) self.context_menu.addActions([ add_file, delete_selected, sort_action, pickup_dup_action, stop_filtering_action ]) def contextMenuEvent(self, event): self.context_menu.exec_(event.globalPos()) def count(self): return self.proxy_model.sourceModel().rowCount() def open(self): list, _ = QFileDialog.getOpenFileNames(self, 'Open File', QDir.homePath()) for path in list: if path[-3:] == 'm3u': self.load(path) else: self.add_item(path) def open_directory(self): directory_url = QFileDialog.getExistingDirectory( self, '0Open directory', QDir.homePath()) dir = QDir(directory_url) filters = [ '*.mp4', '*.m4v', '*.mov', '*.mpg', '*.mpeg', '*.mp3', '*.m4a', '*.wmv', '*.wav', '*.aiff' ] dir.setNameFilters(filters) file_list = dir.entryList() path = dir.absolutePath() + '/' for file in file_list: self.add_item(path + file) def save(self, path=None): """プレイリストを保存する。 :param file :QFile 出力するようのファイル fileが指定されていれば、fileに内容を書き込み、 指定がなければ、ダイアログで名前を指定してそこにファイルを保存。 """ if path is None: return with open(path, 'wt') as fout: for i in range(self.proxy_model.sourceModel().rowCount()): index = self.proxy_model.sourceModel().index(i, 0) print(self.url(index).toLocalFile(), file=fout) return True def load(self, path=None): """プレイリストを読み込む pathが与えられた場合は、そこから読み込み、 ない場合は、何も読み込まない。""" with open(path, 'rt') as fin: for line in fin: self.add_item(line[:-1]) # 最後の改行文字を取り除く def add_item(self, path): if is_media(path): self.proxy_model.sourceModel().add(QUrl.fromLocalFile(path)) return True return False def current_url(self): return self.url(self.current_index) def current_title(self): return self.title(self.current_index) def next(self, step=1): if self.current_row() + step < self.count(): self.set_current_index_from_row(self.current_row() + step) return self.url(self.current_index) else: return None def previous(self, step=1): if self.current_row() - step >= 0: self.set_current_index_from_row(self.current_row() - step) return self.url(self.current_index) else: return None def selected(self): selected_indexes = self.selectedIndexes() if len(selected_indexes) > 0: return selected_indexes[0] else: return None def current_row(self): return self.current_index.row() def url(self, index): if isinstance(index, int): row = index if 0 <= row < self.count(): index = self.proxy_model.sourceModel().index(row, 0) if isinstance(index, QModelIndex): return self.proxy_model.sourceModel().data(index) else: return None def title(self, index): if isinstance(index, int): row = index if 0 <= row < self.count(): index = self.proxy_model.sourceModel().index(row, 0) if isinstance(index, QModelIndex): return self.proxy_model.sourceModel().data(index, Qt.DisplayRole) else: return None def set_current_index_from_row(self, row): new_index = self.proxy_model.sourceModel().index(row, 0) return self.set_current_index(new_index) def set_current_index(self, new_index: QModelIndex): self.current_index = new_index self.current_index_changed.emit(new_index) def deactivate(self): self.set_current_index(QModelIndex()) def auto_resize_header(self): """auto resize Header width on table view. """ width = self.viewport().width() duration_width = 120 self.horizontalHeader().resizeSection(0, width - duration_width) self.horizontalHeader().resizeSection(1, duration_width) def mousePressEvent(self, event): """左クリックされたらカーソル下にある要素を選択し、ドラッグを認識するために現在の位置を保存する。 :param event: QMousePressEvent :return: nothing """ self.isDragging = False if Qt.LeftButton == event.button(): self.dragStartPosition = event.pos() index = self.indexAt(self.dragStartPosition) if index in self.selectedIndexes(): self.isDragging = True self.wasSelected = True return self.rubberBand.setGeometry(QRect(self.dragStartPosition, QSize())) self.rubberBand.show() super(PlaylistView, self).mousePressEvent(event) def mouseMoveEvent(self, event): """start Drag and prepare for Drop. :type event: QMoveEvent マウスを動かした嶺がQApplication.startDragDistance()を超えると、Drag開始されたと認識し、 そのための準備を行う。QMimeDataを使って、データをやりとりする。 """ if not (event.buttons() & Qt.LeftButton): return if (event.pos() - self.dragStartPosition).manhattanLength() \ < QApplication.startDragDistance(): return if self.isDragging: indexes = self.selectedIndexes() urls = self.url_list(indexes) mimeData = QMimeData() # mimeData.setData(self.mime_URLS, convert_to_bytearray(urls)) mimeData.setUrls(urls) file_icon = self.style().standardIcon(QStyle.SP_FileIcon) pixmap = file_icon.pixmap(32, 32) drag = QDrag(self) drag.setMimeData(mimeData) drag.setPixmap(pixmap) drag.setHotSpot(QPoint(0, 0)) dropAction = drag.exec_(Qt.CopyAction | Qt.MoveAction, Qt.CopyAction) if dropAction == Qt.MoveAction: pass else: self.rubberBand.setGeometry( QRect(self.dragStartPosition, event.pos()).normalized()) super(PlaylistView, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): '''マウスを離したときにQRubberBandを隠す。 左クリックをpress と release がだいたい同じ位置であれば、その要素を1つだけ選択する。 :param event: ''' self.rubberBand.hide() if Qt.LeftButton == event.button( ) and Qt.NoModifier == event.modifiers(): if self.indexAt(event.pos()).row() == -1 and \ self.indexAt(self.dragStartPosition).row() == -1: self.clearSelection() elif len(self.selectedIndexes() ) / 2 == 1 and self.wasSelected == True: self.clearSelection() elif (event.pos() - self.dragStartPosition).manhattanLength() \ < QApplication.startDragDistance(): self.setCurrentIndex(self.indexAt(event.pos())) self.wasSelected = False super(PlaylistView, self).mouseReleaseEvent(event) def dragEnterEvent(self, event): """ドラッグした状態でWidgetに入った縁で呼ばれる関数。 :param event: QDragEvent :return: nothing イベントが発生元と発生しているWidgetが同一の場合はMoveActionにする。それ以外はCopyAction。 その二つの場合は受け入れられるように、accept()もしくはacceptProposedAction()を呼ぶ。 """ if event.mimeData().hasUrls() or event.mimeData().hasFormat( self.mime_URLS): if event.source() is self: event.setDropAction(Qt.MoveAction) event.accept() else: event.acceptProposedAction() else: event.ignore() def dragMoveEvent(self, event): """ドラッグした状態でWidget内を移動したときに呼ばれる。 :param event: QDragMoveEvent :return: nothing ドラッグしている要素の背景の色を変えて、どこにファイルがDropされるかをグラデーションした背景で 表現する。 """ if event.mimeData().hasUrls() or event.mimeData().hasFormat( self.mime_URLS): self.rubberBand.setGeometry( self.rectForDropIndicator( self.index_for_dropping_pos(event.pos()))) self.rubberBand.show() self.previousIndex = self.indexAt(event.pos()) if event.source() is self: event.setDropAction(Qt.MoveAction) event.accept() else: event.acceptProposedAction() else: event.ignore() def dragLeaveEvent(self, event): """ドラッグしたままWidget内を出たときにドラッグ下にあった要素の背景色の色を元に戻す。 :param event: QDragLeaveEvent :return: nothing """ self.rubberBand.hide() def dropEvent(self, event): """Dropされたらデータを取り出して、新たに登録する。 :param event: QDropEvent :return: nothing ファイルへのパスと移動前に登録してあった要素のindexを取り出す。 """ self.rubberBand.hide() if event.mimeData().hasUrls() or event.mimeData().hasFormat( self.mime_URLS): if event.mimeData().hasUrls(): urls = event.mimeData().urls() else: urls = convert_from_bytearray(event.mimeData().data( self.mime_URLS)) index = self.index_for_dropping_pos(event.pos()) if event.source() is self: self.move_items(self.selectedIndexes(), index) event.setDropAction(Qt.MoveAction) event.accept() else: self.add_items(urls) event.acceptProposedAction() else: event.ignore() def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: new_index = self.indexAt(event.pos()) if not new_index.isValid(): return self.selectRow(new_index.row()) self.playlist_double_clicked.emit() def add_items(self, items: [QUrl], start: int = -1): """渡された要素をmodelに追加する。 :param items: 追加する項目 :param start: 追加するindexを表す。初期値は-1 start に −1を渡すと一番後ろに追加する。 """ if isinstance(items, QUrl): self.proxy_model.sourceModel().add(items) elif start == -1: for item in items: self.proxy_model.sourceModel().add(item) else: for item, i in items, range(start, len(items)): self.proxy_model.sourceModel().add(item, i) def delete_items(self): """渡されたインデックスを順番に消していく。 :param indexes: 消すためのインデックス """ indexes = self.selectedIndexes() if indexes: self.proxy_model.sourceModel().remove_items(indexes) else: return def move_items(self, indexes: [QModelIndex], dest: QModelIndex): self.proxy_model.sourceModel().move(indexes, dest.row()) def filter_same_title(self): dup = self.proxy_model.sourceModel().pickup_same_title() re = QRegularExpression('|'.join(dup)) self.proxy_model.setFilterRegularExpression(re) self.filtering.emit(' - filtered') def unfilter(self): self.proxy_model.setFilterWildcard('*') self.unfiltered.emit('') def index_for_dropping_pos(self, pos: QPoint) -> QModelIndex: """dropした場所のindexを返す。ただし、要素の高さ半分より下にある場合は、下の要素を返す。 :param pos: :return: posから導き出されたindex 挿入や移動のために、要素の間を意識している。 """ index = self.indexAt(pos) if index.row() < 0: new_index = self.proxy_model.sourceModel().index( self.proxy_model.sourceModel().rowCount(), 0) return new_index item_rect = self.visualRect(index) pos_in_rect = pos.y() - item_rect.top() if pos_in_rect < (item_rect.height() / 2): return index else: return self.proxy_model.sourceModel().index(index.row() + 1, 0) def rectForDropIndicator(self, index: QModelIndex) -> QRect: """QRubberBand を DropIndicatorとして表示するためのQRectを返す。 Geometryに渡されるので、表示位置となるようにQRectを作成する。 幅が表示領域、縦1pixelの棒で表示する。 """ item_rect = self.visualRect(index) top_left = item_rect.topLeft() size = QSize(item_rect.width(), 3) return QRect(top_left, size) def url_list(self, indexes): urls = [] for index in indexes: urls.append(self.proxy_model.sourceModel().data(index)) return sorted(set(urls), key=urls.index)
def __init__(self): QMainWindow.__init__(self) signal.signal(signal.SIGINT, self.exit_app) self.setWindowTitle("Steam Account Switcher") self.setMinimumSize(300, 200) self.resize(300, 300) # Logo self.switcher_logo = QIcon("logo.png") self.setWindowIcon(self.switcher_logo) if platform.system() == "Windows": # windows taskbar app icon fix import ctypes win_appid = 'github.tommis.steam_account_switcher' ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( win_appid) from steamswitcher import SteamSwitcher self.switcher = SteamSwitcher() self.args = self.switcher.args self.main_widget = QWidget() if self.args.gui or self.switcher.settings.get( "show_on_startup", True) and not self.args.no_gui: self.show() elif self.args.no_gui and self.args.no_tray: self.exit_app() self.menu_bar = self.menuBar() self.file_menu = self.menu_bar.addMenu(_("File")) self.settings_menu = self.menu_bar.addMenu(_("Settings")) self.size_menu = self.menu_bar.addMenu(_("Size")) refresh_action = QAction(_("Refresh"), self) import_action = QAction(_("Import accounts"), self) open_skinsdir_action = QAction(_("Skins dir"), self) about_action = QAction(_("About"), self) exit_action = QAction(_("Exit"), self) refresh_action.triggered.connect(Accounts.steamapi_refresh) import_action.triggered.connect( lambda: DialogImportAccount.import_accounts_dialog(self)) open_skinsdir_action.triggered.connect(self.open_skinsdir) about_action.triggered.connect(lambda: DialogAbout.about_dialog(self)) exit_action.triggered.connect(self.exit_app) refresh_action.setShortcut("F5") exit_action.setShortcut("Ctrl+Q") self.file_menu.addActions([ refresh_action, import_action, open_skinsdir_action, about_action ]) self.file_menu.addSeparator() self.file_menu.addAction(exit_action) set_steamapi_key = QAction(_("Set steamapi key"), self) show_avatars = QAction(_("Show avatars"), self, checkable=True) use_systemtray = QAction(_("Use systemtray"), self, checkable=True) after_login_menu = QMenu(_("After login")) after_login_behaviour_group = QActionGroup(after_login_menu) nothing_behaviour = QAction(_('Nothing'), after_login_behaviour_group, checkable=True, data="nothing") close_behaviour = QAction(_('Close'), after_login_behaviour_group, checkable=True, data="close") minimize_behaviour = QAction(_('Minimize to taskbar'), after_login_behaviour_group, checkable=True, data="minimize") minimize_tray_behaviour = QAction(_('Minimize to tray'), after_login_behaviour_group, checkable=True, data="minimize_tray") after_login_menu.addActions([ nothing_behaviour, close_behaviour, minimize_behaviour, minimize_tray_behaviour ]) behaviour_switcher = { "close": lambda: close_behaviour.setChecked(True), "minimize": lambda: minimize_behaviour.setChecked(True), "minimize_tray": lambda: minimize_tray_behaviour.setChecked(True) } behaviour_switcher.get(self.switcher.settings["behavior_after_login"], lambda: nothing_behaviour.setChecked(True))() after_login_menu.triggered.connect(self.set_after_login_action) self.systemtray(self.main_widget) set_steamapi_key.triggered.connect(lambda: self.steamapi_key_dialog()) show_avatars.triggered.connect(lambda: self.set_show_avatars()) use_systemtray.triggered.connect(lambda: self.set_use_systemtray()) self.settings_menu.addAction(set_steamapi_key) self.settings_menu.addSeparator() self.settings_menu.addActions([show_avatars, use_systemtray]) self.settings_menu.addMenu(after_login_menu) show_avatars.setChecked(self.switcher.settings.get("show_avatars")) use_systemtray.setChecked(self.switcher.settings.get("use_systemtray")) set_size_small = QAction(_("Small"), self) set_size_medium = QAction(_("Medium"), self) set_size_large = QAction(_("Large"), self) set_size_small.triggered.connect(lambda: self.set_size("small")) set_size_medium.triggered.connect(lambda: self.set_size("medium")) set_size_large.triggered.connect(lambda: self.set_size("large")) self.size_menu.addActions( [set_size_small, set_size_medium, set_size_large]) set_size_small.setShortcut("Ctrl+1") set_size_medium.setShortcut("Ctrl+2") set_size_large.setShortcut("Ctrl+3") self.add_button = QPushButton(_("Add account")) self.edit_button = QPushButton(_("Edit account")) self.edit_button.setDisabled(True) self.buttons = QHBoxLayout() self.buttons.addWidget(self.add_button) self.buttons.addWidget(self.edit_button) self.layout = QVBoxLayout() self.main_widget.setLayout(self.layout) self.accounts_list = QListWidget() self.accounts_list.setDragDropMode(QAbstractItemView.InternalMove) self.layout.addWidget(self.accounts_list) self.layout.addLayout(self.buttons) self.layout.setSpacing(10) self.accounts_list.setSpacing(1) self.import_accounts_window = QDialog() self.load_accounts() def edit_button_enabled(): if self.accounts_list.selectedItems(): self.edit_button.setEnabled(True) else: self.edit_button.setEnabled(False) # Signals and Slots self.add_button.clicked.connect(lambda: self.account_dialog(True)) self.edit_button.clicked.connect(lambda: self.account_dialog(False)) self.accounts_list.itemSelectionChanged.connect(edit_button_enabled) self.accounts_list.doubleClicked.connect(lambda: self.steam_login( self.accounts_list.currentIndex().data(5))) self.accounts_list.setContextMenuPolicy(Qt.CustomContextMenu) self.accounts_list.customContextMenuRequested.connect( lambda: RightClickMenu.show_rightclick_menu(self)) #self.accounts_list.layoutChanged.connect(lambda: self.account_reordered) #self.accounts_list.dropEvent(self.dropEvent(QDropEvent)) self.setCentralWidget(self.main_widget) if self.args.no_tray: print("test") elif self.switcher.settings.get("use_systemtray") or self.args.tray: self.tray_icon.show() if self.switcher.first_run or self.args.first_run: self.steamapi_key_dialog() elif not self.switcher.first_run and \ not self.is_valid_steampi_key(self.switcher.settings["steam_api_key"]): self.tray_icon.showMessage("No api key", "Set the steam web api key.", self.switcher_logo)
class FE14SoundEditor(QWidget, Ui_sound_editor): def __init__(self): super().__init__() self.setupUi(self) self.setWindowTitle("Voice Set Editor") self.setWindowIcon(QIcon("paragon.ico")) self.service = locator.get_scoped("SoundService") self.voice_set_model = self.service.get_voice_set_model() self.error_dialog = None self.form = PropertyForm(self.service.template) self.form_widget = QWidget() self.form_widget.setLayout(self.form) self.horizontalLayout_3.addWidget(self.form_widget) self.proxy_model = QtCore.QSortFilterProxyModel() self.proxy_model.setSourceModel(self.voice_set_model) self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.sets_menu = QMenu(self) self.add_set_action = QAction("Add Voice Set") self.remove_set_action = QAction("Remove Voice Set") self.remove_set_action.setDisabled(True) self.sets_menu.addActions( [self.add_set_action, self.remove_set_action]) self.sets_list_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.sets_list_view.customContextMenuRequested.connect( self._on_sets_menu_requested) self.entries_menu = QMenu(self) self.entries_menu.setDisabled(True) self.add_entry_action = QAction("Add Entry") self.remove_entry_action = QAction("Remove Entry") self.copy_tag_action = QAction("Copy Tag to All Entries") self.entries_menu.addActions([ self.add_entry_action, self.remove_entry_action, self.copy_tag_action ]) self.sounds_list_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.sounds_list_view.customContextMenuRequested.connect( self._on_entries_menu_requested) self.sets_list_view.setModel(self.proxy_model) self.sets_list_view.selectionModel().currentRowChanged.connect( self._update_set_selection) self.search_bar.textChanged.connect(self._update_filter) self.add_set_action.triggered.connect(self._on_add_set_triggered) self.remove_set_action.triggered.connect(self._on_remove_set_triggered) self.add_entry_action.triggered.connect(self._on_add_entry_triggered) self.remove_entry_action.triggered.connect( self._on_remove_entry_triggered) self.copy_tag_action.triggered.connect(self._on_copy_tags_triggered) self.form.editors["Name"].editingFinished.connect( self._write_back_entry) self.form.editors["Tag"].editingFinished.connect( self._write_back_entry) self.selected_voice_set: Optional[VoiceSetEntriesModel] = None self.selected_voice_set_entry: Optional[PropertyContainer] = None self.service.set_in_use() self._update_set_selection(QModelIndex()) def show(self): super().show() self.setDisabled(not self.service.load_succeeded) if not self.service.load_succeeded: self.error_dialog = ErrorDialog( "Unable to load required data. See the log for details.") self.error_dialog.show() def _update_filter(self): self.proxy_model.setFilterRegExp(self.search_bar.text()) def _on_entries_menu_requested(self, point: QPoint): self.entries_menu.exec_(self.sounds_list_view.mapToGlobal(point)) def _on_sets_menu_requested(self, point: QPoint): self.sets_menu.exec_(self.sets_list_view.mapToGlobal(point)) def _update_set_selection(self, index: QtCore.QModelIndex): if self.selected_voice_set: self.sounds_list_view.selectionModel( ).currentRowChanged.disconnect() self.selected_voice_set = self.proxy_model.data( index, QtCore.Qt.UserRole) self.sounds_list_view.setModel(self.selected_voice_set) if self.selected_voice_set: self.sounds_list_view.selectionModel().currentRowChanged.connect( self._update_sound_selection) self.entries_menu.setDisabled(False) self.remove_set_action.setDisabled(False) else: self.entries_menu.setDisabled(True) self.remove_set_action.setDisabled(True) self.form_widget.setDisabled(True) self.remove_entry_action.setDisabled(True) self.copy_tag_action.setDisabled(True) self.selected_voice_set_entry = None self.form.update_target(self.selected_voice_set_entry) def _update_sound_selection(self, index: QtCore.QModelIndex): self.selected_voice_set_entry = self.selected_voice_set.data( index, QtCore.Qt.UserRole) self.form_widget.setDisabled(self.selected_voice_set_entry is None) self.remove_entry_action.setDisabled( self.selected_voice_set_entry is None) self.copy_tag_action.setDisabled(self.selected_voice_set_entry is None) self.form.update_target(self.selected_voice_set_entry) def _write_back_entry(self): if self.selected_voice_set and self.selected_voice_set_entry: self.selected_voice_set.save_entry(self.selected_voice_set_entry) def _on_add_entry_triggered(self): if self.selected_voice_set: self.selected_voice_set.insertRow( self.selected_voice_set.rowCount()) def _on_remove_entry_triggered(self): if self.selected_voice_set and self.selected_voice_set_entry: self.selected_voice_set.remove_entry(self.selected_voice_set_entry) self.sounds_list_view.selectionModel().clearCurrentIndex() def _on_copy_tags_triggered(self): if self.selected_voice_set and self.selected_voice_set_entry: self.selected_voice_set.synchronize_tags( self.selected_voice_set_entry) def _on_add_set_triggered(self): (desired_name, ok) = QInputDialog.getText(self, "Enter a unique name for the voice set.", "Name") if not ok: return try: self.voice_set_model.create_voice_set(desired_name) except NameError: self.error_dialog = ErrorDialog( "Voice set name %s is already in use." % desired_name) self.error_dialog.show() except: logging.exception( "Unknown error when adding voice set to IndirectSound.") self.error_dialog = ErrorDialog( "An unknown error occurred. See the log for details.") self.error_dialog.show() def _on_remove_set_triggered(self): if self.selected_voice_set: self.voice_set_model.remove_voice_set( self.selected_voice_set.voice_set_label) self.sets_list_view.clearSelection()
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()