Exemple #1
0
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)
Exemple #4
0
    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
Exemple #6
0
    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))
Exemple #7
0
  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
Exemple #10
0
 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
Exemple #11
0
    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)
Exemple #12
0
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)
Exemple #14
0
 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 + '*')
Exemple #15
0
    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)
Exemple #16
0
    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))
Exemple #17
0
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')
Exemple #18
0
 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))
Exemple #19
0
 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))
Exemple #20
0
 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()
Exemple #24
0
 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)
Exemple #25
0
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."))
Exemple #27
0
 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()
Exemple #28
0
 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"))