예제 #1
0
 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
예제 #2
0
    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())
예제 #3
0
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)
예제 #4
0
    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()"))
예제 #5
0
    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
예제 #6
0
파일: window.py 프로젝트: alek9z/dataMole
 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())
예제 #7
0
    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
예제 #8
0
    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())
예제 #9
0
    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
예제 #10
0
파일: browser.py 프로젝트: libreblog/cells
 def contextMenuEvent(self, event):
     menu = QMenu()
     menu.setStyleSheet(Theme.contextMenu.style)
     menu.setFont(Theme.contextMenu.font)
     menu.addActions(self.actions())
     menu.exec_(event.globalPos())
예제 #11
0
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.")
예제 #12
0
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!')
예제 #13
0
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)
예제 #14
0
    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)
예제 #15
0
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.")
예제 #16
0
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)
예제 #17
0
    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)
예제 #18
0
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()
예제 #19
0
class MainWindow(QMainWindow):

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

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

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

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

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

        self._loadSettings()

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

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


    def closeEvent(self, event):

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

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


    def _loadSettings(self):

        settings = QSettings()

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

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

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


    def _saveSettings(self):

        settings = QSettings()

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

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

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


    def _createActions(self):

        #
        # Actions: Application

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

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

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

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

        #
        # Actions: Document

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        #
        # Actions: View

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

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

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

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

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

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

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

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

        #
        # Actions: Help

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


    def _createMenus(self):

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

        #
        # Menu: Document

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

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

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

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

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

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

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


    def _createToolBars(self):

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

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

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

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

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

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


    def _updateActions(self, subWindowCount=0):

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

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


    def _updateActionFullScreen(self):

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


    def _updateActionRecentDocuments(self):

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

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

            self._actionRecentDocuments.append(actionRecentDocument)

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

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

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

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


    def _updateMenuOpenRecent(self):

        self._menuOpenRecent.clear()

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

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

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


    def _updateTitleBar(self):

        title = None

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

        self.setWindowTitle(title)


    def _onActionAboutTriggered(self):

        dialog = AboutDialog(self)
        dialog.exec_()


    def _onActionColophonTriggered(self):

        dialog = ColophonDialog(self)
        dialog.exec_()


    def _onActionPreferencesTriggered(self):

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

        self._preferences = dialog.preferences()

        self._updateRecentDocuments(None)
        self._updateMenuOpenRecent()


    def _onActionNewTriggered(self):

        self._loadDocument("")


    def _onActionOpenTriggered(self):

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

        for fileName in fileNames:
            self._openDocument(fileName)


    def _onActionOpenRecentDocumentTriggered(self, canonicalName):
        pass

#        self.openDocument(canonicalName)


    def _onActionOpenRecentClearTriggered(self):

        self._recentDocuments.clear()

        self._updateRecentDocuments(None)
        self._updateMenuOpenRecent()


    def _onActionSaveTriggered(self):
        pass


    def _onActionSaveAsTriggered(self):
        pass


    def _onActionSaveAsDelimiterTriggered(self, delimiter):
        pass


    def _onActionSaveCopyAsTriggered(self):
        pass


    def _onActionSaveAllTriggered(self):
        pass


    def _onActionCloseTriggered(self):

        self._documentArea.closeActiveSubWindow()


    def _onActionCloseOtherTriggered(self):

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


    def _onActionCloseAllTriggered(self):

        self._documentArea.closeAllSubWindows()


    def _onActionFullScreenTriggered(self):

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

        self._updateActionFullScreen()


    def _onActionTitlebarFullPathTriggered(self):

        self._updateTitleBar()


    def _onActionKeyboardShortcutsTriggered(self):

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

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


    def _onDocumentWindowActivated(self, subWindow):

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

        if not subWindow:
            return


    def _onDocumentAboutToClose(self, canonicalName):

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

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


    def _createDocument(self):

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

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

        return document


    def _createDocumentIndex(self, canonicalName):

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

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

        return canonicalIndex + 1


    def _findDocumentWindow(self, canonicalName):

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

        return None


    def _activeDocument(self):

        subWindow = self._documentArea.activeSubWindow()

        return subWindow.widget() if subWindow else None


    def _openDocument(self, fileName):

        canonicalName = QFileInfo(fileName).canonicalFilePath()

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

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

        return self._loadDocument(canonicalName);


    def _loadDocument(self, canonicalName):

        document = self._createDocument()

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

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

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

        return succeeded


    def _updateRecentDocuments(self, canonicalName):

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

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

        self._updateActionRecentDocuments()