def encode_seq_component_tree_node_path(project_tree_path, seq_component_tree_path): """Encodes a path-based reference to a sequence component tree node. Suitable for use with the SEQUENCE_COMPONENT_TREE_NODE_PATH_MEDIA_TYPE media type. Returns the data as a bytes-like object. Raises ValueError if either specified path is relative. project_tree_path -- Absolute TreeNamePath giving the path of the project tree node that owns the relevant sequence component tree. seq_component_tree_path -- Absolute TreeNamePath giving the path of the relevant node in the sequence component tree. """ if not project_tree_path.is_absolute: raise ValueError( QCoreApplication.translate( 'MIMEData', 'Cannot encode relative path as project tree node path data.')) if not seq_component_tree_path.is_absolute: raise ValueError( QCoreApplication.translate( 'MIMEData', 'Cannot encode relative path as sequence component tree node path data.' )) project_tree_path_bytes = str(project_tree_path).encode(MEDIA_STR_ENCODING) seq_component_tree_path_bytes = str(seq_component_tree_path).encode( MEDIA_STR_ENCODING) return pickle.dumps( (project_tree_path_bytes, seq_component_tree_path_bytes))
def __init__(self, project_tree_controller, node, new_parent, parent=None): """Constructor. Raises ValueError if it is determined that the reparent operation would fail. project_tree_controller -- Reference to the ProjectTreeController in charge of making high-level changes to the project tree. node -- ProjectTreeNode to reparent. new_parent -- New parent node for the ProjectTreeNode. parent -- Parent QUndoCommand. """ super().__init__(project_tree_controller, parent) # Check that the operation is valid. if node.parent is None: raise ValueError( QCoreApplication.translate('ReparentProjectTreeNodeCommand', 'Cannot reparent root project tree node.')) if new_parent is None: raise ValueError( QCoreApplication.translate('ReparentProjectTreeNodeCommand', 'Cannot make a new root project tree node.')) new_parent.verify_can_add_as_child(node) self._node = node self._old_parent = node.parent self._new_parent = new_parent self.setText( QCoreApplication.translate('ReparentProjectTreeNodeCommand', "Move '{}' to Parent '{}'").format(self._node.name, self._new_parent.name)) self.setObsolete(self._old_parent is self._new_parent)
def add_node(self, node, parent): """Adds a node to a sequence component tree. Raises ValueError if the operation cannot be performed. node -- New sequence component tree node to add. parent -- Parent to which the new node will be added. """ if node.parent is not None or node is node.tree_owner.root_seq_component_node: raise ValueError( QCoreApplication.translate( 'SequenceComponentTreeController', 'Cannot add a node that already exists in the sequence component tree.' )) if node.tree_owner is not parent.tree_owner: raise ValueError( QCoreApplication.translate( 'SequenceComponentTreeController', "Cannot mix sequence component nodes from different project tree nodes." )) # Notify the SeqComponentTreeQtModel before and after making the change. with self.seq_component_tree_qt_model.begin_add_node(node, parent): node.parent = parent # Send out appropriate signals to notify other GUI components. self.node_added.emit(node)
def reparent_node(self, node, new_parent): """Performs a reparent operation on a node in the project tree. Raises ValueError if the operation cannot be performed. node -- Project tree node to reparent. new_parent -- New parent for the project tree node. """ # Do nothing if the node already has the specified parent. if node.parent is new_parent: return # Check that the operation is valid before notifying the ProjectTreeQtModel. if node.parent is None: raise ValueError( QCoreApplication.translate( 'ProjectTreeController', 'Cannot reparent root project tree node.')) if new_parent is None: raise ValueError( QCoreApplication.translate( 'ProjectTreeController', 'Cannot make a new root project tree node.')) new_parent.verify_can_add_as_child(node) old_parent = node.parent # Notify the ProjectTreeQtModel before and after making the change. with self.project_tree_qt_model.begin_reparent_node(node, new_parent): node.parent = new_parent # Send out appropriate signals to notify other GUI components. self.node_reparented.emit(node, new_parent, old_parent)
def __ask_for_continue_if_unsaved_changes(self): if not self.__command_handler.can_undo.get(): # Continue if there are no unsaved changes return True else: # Ask if there are unsaved changes box = QMessageBox() box.addButton(QCoreApplication.translate("main_window", "Yes"), box.YesRole) no_button = box.addButton(QCoreApplication.translate("main_window", "No"), box.NoRole) box.setDefaultButton(no_button) box.setWindowTitle(QCoreApplication.translate("main_window", "Unsaved changes")) box.setText(QCoreApplication.translate( "main_window", "Do you really want to continue " "without saving changes?")) box.setIcon(box.Icon.Question) response = box.exec() if (response == 1): # Do not continue because user wants to stop return False else: return True
def __init__(self, seq_component_tree_controller, node, parent_node, parent=None): """Constructor. Raises ValueError if it is determined that the addition operation would fail. seq_component_tree_controller -- Reference to the SequenceComponentTreeController in charge of making high-level changes to the sequence component tree. node -- New BaseSequenceComponentNode to add. parent_node -- Parent BaseSequenceComponentNode to which the new node will be added. parent -- Parent QUndoCommand. """ super().__init__(seq_component_tree_controller, parent) if node.parent is not None: raise ValueError( QCoreApplication.translate( 'AddSequenceComponentTreeNodeCommand', 'Cannot add a node that already exists in the sequence component tree.' )) parent_node.verify_can_add_as_child(node) self._new_node = node self._parent_node = parent_node self.setText( QCoreApplication.translate('AddSequenceComponentTreeNodeCommand', "Create '{}'").format(node.name))
def make_context_menu(self, undo_stack, seq_component_tree_controller, seq_component_node_controller, parent=None): menu = super().make_context_menu(undo_stack, seq_component_tree_controller, seq_component_node_controller, parent) # Add menu items for changing the component type, if that is allowed. change_type_menu = menu.addMenu(QCoreApplication.translate('NonInstancedSequenceComponentNode', 'Change &Type')) def change_type_func_maker(new_component_type): def change_type_func(): undo_stack.push(SetComponentTypeCommand(seq_component_node_controller, self, new_component_type)) return change_type_func for component_type in SUPPORTED_COMPONENT_TYPES: change_type_action = change_type_menu.addAction(component_type.get_icon(), component_type.menu_text) change_type_action.setEnabled(component_type != self.component_type) change_type_action.triggered.connect(change_type_func_maker(component_type)) # Add menu items for creating child nodes, if creating children is allowed. if self.can_have_children: add_menu = menu.addMenu(QCoreApplication.translate('NonInstancedSequenceComponentNode', '&Create Child')) def add_func_maker(component_type): def add_func(): new_node = NonInstancedSequenceComponentNode(self.suggest_child_name(component_type.node_default_name), component_type=component_type, tree_owner=self.tree_owner) undo_stack.push(AddSequenceComponentTreeNodeCommand(seq_component_tree_controller, new_node, self)) return add_func for component_type in SUPPORTED_COMPONENT_TYPES: add_action = add_menu.addAction(component_type.get_icon(), component_type.menu_text) add_action.triggered.connect(add_func_maker(component_type)) # Add a menu item for deleting the node, if it can be deleted. if self.parent is not None: def delete_func(): undo_stack.push(DeleteSequenceComponentTreeNodeCommand(seq_component_tree_controller, self)) delete_action = menu.addAction(QCoreApplication.translate('NonInstancedSequenceComponentNode', '&Delete')) delete_action.triggered.connect(delete_func) return menu
def scale_path(sel, *args): dialog = QInputDialog() text, ok = dialog.getText( self, QCoreApplication.translate("side_widget", "Scale factor"), QCoreApplication.translate("side_widget", "Scale factor")) if (ok): self.__operation_controller.scale_path(sel, text)
def make_context_menu(self, undo_stack, project_tree_controller, parent=None): """Creates a context menu for this node. undo_stack -- QUndoStack that should receive undoable editing commands generated by the menu. project_tree_controller -- ProjectTreeController in charge of high-level changes to the project tree. parent -- Parent QObject for the context menu. """ menu = QMenu(parent) # Add menu items for creating child nodes, if the node is allowed to have children. if self.can_have_children: def add_dir_func(): new_node = DirProjectTreeNode( self.suggest_child_name(_DIR_NODE_NAME_PREFIX)) undo_stack.push( AddProjectTreeNodeCommand(project_tree_controller, new_node, self)) def add_sequence_func(): new_node = SequenceProjectTreeNode( self.suggest_child_name(_SEQUENCE_NODE_NAME_PREFIX)) undo_stack.push( AddProjectTreeNodeCommand(project_tree_controller, new_node, self)) add_menu = menu.addMenu( QCoreApplication.translate('BaseProjectTreeNode', '&Create Child')) add_dir_action = add_menu.addAction( DirProjectTreeNode.get_icon(), QCoreApplication.translate('BaseProjectTreeNode', '&Directory')) add_sequence_action = add_menu.addAction( SequenceProjectTreeNode.get_icon(), QCoreApplication.translate('BaseProjectTreeNode', '&Sequence')) add_dir_action.triggered.connect(add_dir_func) add_sequence_action.triggered.connect(add_sequence_func) # Add a menu item for deleting the node, if it is not the root node. if self.parent is not None: def delete_func(): undo_stack.push( DeleteProjectTreeNodeCommand(project_tree_controller, self)) delete_action = menu.addAction( QCoreApplication.translate('BaseProjectTreeNode', '&Delete')) delete_action.triggered.connect(delete_func) return menu
def test_install_translators(self): install_translators("ru_RU") txt = QCoreApplication.translate("@default", "Question") self.assertEqual(txt, "Вопрос") # i18n install_translators("en_EN") txt = QCoreApplication.translate("@default", "Question") self.assertEqual(txt, "Question") install_translators() if QLocale.system().name() == "ru_RU": txt = QCoreApplication.translate("@default", "Question") self.assertEqual(txt, "Вопрос") # i18n
def suggest_child_name(self, prefix=DEFAULT_NAME_PREFIX): """Suggests an available child name starting with the specified prefix. Raises ValueError if this node is not allowed to have children, or if the prefix is not a valid node name. prefix -- String that the child name must start with. """ # Check that the prefix is a valid name. NamedTreeNode.verify_name_valid(prefix) # Check that this node is allowed to have children. if not self.can_have_children: raise ValueError( QCoreApplication.translate( 'NamedTreeNode', "Node '{}' is not allowed to have children.").format( self.name)) # Use the prefix as the full name if it is available. if prefix not in self._children: return prefix # Otherwise, append a number to the prefix. suffix_num = 0 while prefix + _SUFFIX_FORMAT_STRING.format( suffix_num) in self._children: suffix_num += 1 return prefix + _SUFFIX_FORMAT_STRING.format(suffix_num)
def read_qt_resource(path, encoding=None): """Reads a resource from the file system or a packaged Qt resource file. Returns either binary or text data, depending on whether encoding is None. path -- Path to the resource. Should start with ":" if referencing a resource inside a packaged Qt resource file. encoding -- Text encoding to use for decoding the data, or None to return binary data. """ qfile = QFile(path) try: # Open the QFile. if not qfile.open(QFile.ReadOnly): raise IOError( QCoreApplication.translate('QtUtil', "Failed to open '{}'").format(path)) # Read the data. # TODO: Handle reading in a way that can report errors. data = bytes(qfile.readAll()) if encoding is None: return data else: return data.decode(encoding) # Close the QFile. finally: if qfile.isOpen(): qfile.close()
def delete_node(self, node): """Deletes a node from a sequence component tree. Raises ValueError if the operation cannot be performed. node -- Sequence component tree node to delete. """ if node.parent is None: raise ValueError( QCoreApplication.translate( 'SequenceComponentTreeController', 'Cannot delete root node from the sequence component tree.' )) # Reset the active node first, if it or one of its ancestors is being deleted. if self.active_node is not None and (node is self.active_node or node in self.active_node.ancestors): self.active_node = None parent_node = node.parent # Notify the SeqComponentTreeQtModel before and after making the change. with self.seq_component_tree_qt_model.begin_delete_node(node): node.parent = None # Send out appropriate signals to notify other GUI components. self.node_deleted.emit(node, parent_node)
def _set_undo_stack_clean(self, is_clean): """Called when the undo stack changes clean status. is_clean -- Boolean indicating whether the undo stack is currently in a clean state. """ self.actionSaveProject.setEnabled(not is_clean) window_title = QCoreApplication.translate('MainWindow', 'ScriptASeq') self.setWindowTitle(window_title if is_clean else window_title + '*')
def __init__(self, seq_component_tree_controller, node, new_name, parent=None): """Constructor. Raises ValueError if it is determined that the rename operation would fail. seq_component_tree_controller -- Reference to the SequenceComponentTreeController in charge of making high-level changes to the sequence component tree. node -- BaseSequenceComponentNode to rename. new_name -- New name for the BaseSequenceComponentNode. parent -- Parent QUndoCommand. """ super().__init__(seq_component_tree_controller, parent) # Check that the operation is valid. if node.parent is not None: node.parent.verify_child_name_available(new_name) else: NamedTreeNode.verify_name_valid(new_name) self._node = node self._old_name = node.name self._new_name = new_name self.setText( QCoreApplication.translate( 'RenameSequenceComponentTreeNodeCommand', "Rename '{}' to '{}'").format(self._old_name, new_name)) self.setObsolete(self._old_name == self._new_name)
def verify_name_valid(name): """Checks if a prospective node name is valid, and raises ValueError if not. name -- Name to check. """ # Check if the name is empty. if name == '': raise ValueError( QCoreApplication.translate('NamedTreeNode', 'Node name must not be empty.')) # Check if the name contains a disallowed substring. for substring in NAME_DISALLOWED_SUBSTRINGS: if substring in name: raise ValueError( QCoreApplication.translate( 'NamedTreeNode', "Node name must not contain '{}'.").format(substring))
class NoteSequenceComponentType(BaseSequenceComponentType): """Sequence component type for note nodes.""" display_name = QCoreApplication.translate('NoteSequenceComponentType', 'Note') menu_text = QCoreApplication.translate('NoteSequenceComponentType', '&Note') node_default_name = QCoreApplication.translate( 'NoteSequenceComponentType', 'Note', 'Sequence component node name') internal_name = 'Note' @classmethod def make_icon(cls): return make_multires_icon(':/icons/sequence_component_tree/note')
def del_custom_prop(self, name): """Deletes a custom property mapping. Raises KeyError if no custom property with the specified name is present. Default implementation always raises KeyError. Subclasses should override this. name -- Name of the custom property to delete. """ raise KeyError( QCoreApplication.translate('BaseSequenceComponentNode', "Custom property '{}' not found.").format(name))
def get_custom_prop(self, name): """Gets the value of a custom property, as a CustomSequenceComponentPropValue object. Raises KeyError if no custom property with the specified name is present. Default implementation always raises KeyError. Subclasses should override this. name -- Name of the custom property to get. """ raise KeyError( QCoreApplication.translate('BaseSequenceComponentNode', "Custom property '{}' not found.").format(name))
def _set_can_undo(self, can_undo): """Called when the status of whether we have an action to undo changes. can_undo -- Boolean indicating whether we can currently undo. """ self.actionUndo.setEnabled(can_undo) undo_action_text = QCoreApplication.translate('MainWindow', '&Undo') self.actionUndo.setText(undo_action_text + ' {}'.format(self._undo_stack.undoText()) if can_undo else undo_action_text)
def __set_title(self): # Set window title if (self.__file_path): self.setWindowTitle(ntpath.basename(self.__file_path) + " - SignalFlowGrapher") else: self.setWindowTitle( QCoreApplication.translate("main_window", "*Unknown - SignalFlowGrapher"))
def __init__(self, project_tree_controller, node, parent=None): """Constructor. Raises ValueError if it is determined that the deletion operation would fail. project_tree_controller -- Reference to the ProjectTreeController in charge of making high-level changes to the project tree. node -- ProjectTreeNode to delete. parent -- Parent QUndoCommand. """ super().__init__(project_tree_controller, parent) if node.parent is None: raise ValueError( QCoreApplication.translate('DeleteProjectTreeNodeCommand', 'Cannot delete root node from project tree.')) self._node = node self._parent_node = node.parent self.setText(QCoreApplication.translate('DeleteProjectTreeNodeCommand', "Delete '{}'").format(node.name))
def __init__(self, graph_field, side_widget, main_controller: MainController, io_controller: IOController, command_handler, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setupUi(self) self.__command_handler = command_handler self.__graph_field = graph_field self.__main_controller = main_controller self.__io_controller = io_controller self.__file_path = "" self.setCentralWidget(graph_field) self.__conditional_actions = [] dock = QDockWidget(QCoreApplication.translate("main_window", "Operations"), self) dock.setWidget(side_widget) dock.setFeatures(dock.DockWidgetMovable | dock.DockWidgetFloatable) self.addDockWidget(Qt.RightDockWidgetArea, dock) # Observe can_undo and can_redo on command handler # to enable/disable und and redo buttons self.__command_handler.can_undo.observe( lambda old, new: self.action_undo.setDisabled(not new)) self.__command_handler.can_redo.observe( lambda old, new: self.action_redo.setDisabled(not new)) # Observe selection changes self.__graph_field.selection.observe( self.__handle_selection_change) # Connect triggers to methods self.action_redo.triggered.connect(self.__command_handler.redo) self.action_undo.triggered.connect(self.__command_handler.undo) self.action_save.triggered.connect(self.__save) self.action_save_as.triggered.connect(self.__save_as) self.action_select_all.triggered.connect(self.__select_all) self.action_open.triggered.connect(self.__open) self.action_new.triggered.connect(self.__new) self.action_exit.triggered.connect(lambda: self.close()) self.action_about.triggered.connect(self.__about) self.__conditional_actions.append(ConditionalAction( [MinNumNodesOrBranchesSelected(1)], self.action_remove_branch_or_node, self.action_remove_branch_or_node.triggered, lambda sel, *args: self.__main_controller.remove_nodes_and_branches(sel) )) # Creat inital graph self.__new()
def key_switcher_params(widget): paste_txt = QCoreApplication.translate("@default", "Paste") paste_act = ([ a for a in widget.actions() if a.text().split("\t")[0].replace("&", "") == paste_txt ])[0] test = QTestHelper() test.sleep() widget.setActiveAction(paste_act) test.sleep() test.key_click(widget, Qt.Key_Enter)
def decode_project_tree_node_path(data): """Decodes a path-based reference to a project tree node. Suitable for use with the PROJECT_TREE_NODE_PATH_MEDIA_TYPE media type. Returns the decoded path as a TreeNamePath object. """ path = TreeNamePath.from_str(bytes(data).decode(MEDIA_STR_ENCODING)) if not path.is_absolute: raise ValueError( QCoreApplication.translate( 'MIMEData', 'Expected an absolute path, got a relative path.')) return path
def save_tikz(sel, *args): dialog = QFileDialog() dialog.setDefaultSuffix(".tex") result = dialog.getSaveFileName(self, QCoreApplication.translate( "side_widget", "Save TikZ Output"), filter="LaTex (*.tex)") if result[0]: try: self.__io_controller.generate_tikz(result[0]) except Exception: logger.exception("Exception while saving tikz") box = QMessageBox() box.critical( self, QCoreApplication.translate("side_widget", "Error"), QCoreApplication.translate( "side_widget", "Error while writing " "tikz file. " "See log for details."))
def image_param(widget): image_txt = QCoreApplication.translate("@default", "Change size of image") image_act = ([ a for a in widget.actions() if a.text().split("\t")[0].replace("&", "") == image_txt ]) test = QTestHelper() test.sleep() # check item in menu self.assertEqual(len(image_act), 1) widget.hide()
def spell_no(widget): image_txt = QCoreApplication.translate("@default", "Add to the dictionary") image_act = ([ a for a in widget.actions() if a.text().split("\t")[0].replace("&", "") == image_txt ]) test = QTestHelper() test.sleep() # check item in menu self.assertEqual(len(image_act), 1) widget.hide()
def __save_as(self): dialog = QFileDialog() dialog.setDefaultSuffix(".json") result = dialog.getSaveFileName( self, QCoreApplication.translate("main_window", "Save Signal-flow Graph"), filter="JSON (*.json)") if result[0]: try: self.__io_controller.save_graph(result[0]) self.__file_path = result[0] self.__set_title() except Exception: logger.exception("Exception while saving to path: %s", self.__file_path) self.__show_error( QCoreApplication.translate("main_window", "Error while saving file." " See log for details"))
def __save(self): if (not self.__file_path): self.__save_as() else: try: self.__io_controller.save_graph(self.__file_path) except Exception: logger.exception("Exception while saving to path: %s", self.__file_path) self.__show_error( QCoreApplication.translate("main_window", "Error while saving file." " See log for details"))