def insertRow(self, row, parent_index, node, reinserting=False): ''' Override the default insertRow to catch updates to models. ''' # Catch the events where we alter the list of available models and # notify interested objects XmlModel.insertRow(self, row, parent_index, node, reinserting) if node.tag == 'model': update_models_to_run_lists()
def setUp(self): self.app = QApplication([], True) self.testdatapath = os.path.split(__file__)[0] self.testdatapath = os.path.join(self.testdatapath, 'testdata') manager_xml_file = os.path.join(self.testdatapath, 'model_manager.xml') self.xml = ElementTree(file=manager_xml_file).getroot() self.instance = XmlModel(self.xml)
def data(self, index, role): ''' PyQt API Method -- see PyQt for documentation ''' if not index.isValid(): return QVariant() node = index.internalPointer().node if node is None: return QVariant() # only override displaying of left column if index.column() != 0: return XmlModel.data(self, index, role) # give some nodes special icons if role == Qt.DecorationRole: if node.tag in ['structure', 'specification']: if node.getparent().tag == 'model': return QVariant(IconLibrary.icon('folder_development')) elif node.getparent().tag == 'structure': return QVariant(IconLibrary.icon('method')) elif node.getparent().tag == 'specification' and node.tag != 'submodel': # assume it's a submodel group return QVariant(IconLibrary.icon('folder_development')) # fall back on default return XmlModel.data(self, index, role)
def data(self, index, role): ''' PyQt API Method -- see PyQt for documentation ''' if not index.isValid(): return QVariant() node = index.internalPointer().node if node is None: return QVariant() # only override displaying of left column if index.column() != 0: return XmlModel.data(self, index, role) # give some nodes special icons if role == Qt.DecorationRole: if node.tag in ['structure', 'specification']: if node.getparent().tag == 'model': return QVariant(IconLibrary.icon('folder_development')) elif node.getparent().tag == 'structure': return QVariant(IconLibrary.icon('method')) elif node.getparent( ).tag == 'specification' and node.tag != 'submodel': # assume it's a submodel group return QVariant(IconLibrary.icon('folder_development')) # fall back on default return XmlModel.data(self, index, role)
def insertRow(self, row, parent_index, node, reinserting = False): ''' Override the default insertRow to catch updates to models. ''' # Catch the events where we alter the list of available models and # notify interested objects XmlModel.insertRow(self, row, parent_index, node, reinserting) if node.tag == 'model': update_models_to_run_lists()
def add_model_view_delegate(self): ''' Initialize and bind the Model, View and Delegate for this controller. This method is called before any initialization of the widgets. Subclasses that wish to use their own model, view and/or delegate should override this method and initialize their own widgets. ''' self.model = XmlModel(self.xml_root, self.project) self.view = XmlView(self.manager.base_widget) self.delegate = XmlItemDelegate(self.view)
def removeRow(self, row, parent_index): ''' Override the default removeRow to catch updates to models. ''' # Catch the events where we alter the list of available models and # notify interested objects node = self.index(row, 0, parent_index).internalPointer().node notify = False if node is not None and node.tag == 'model': notify = True XmlModel.removeRow(self, row, parent_index) if notify: update_models_to_run_lists()
def __init__(self, parent_widget): QDialog.__init__(self, parent_widget) self.setupUi(self) settings_directory = os.path.join(os.environ['OPUS_HOME'], 'settings') self._config_filename = os.path.join(settings_directory, 'database_server_configurations.xml') try: root = ElementTree(file=self._config_filename).getroot() view = XmlView(self) model = XmlModel(root) delegate = XmlItemDelegate(view) view.setModel(model) # Turns out that Qt Garbage collects the model (and delegate) if we don't explicitly # bind it to a Python object in addition to using the PyQt .setModel() method. view._model = model view._delegate = delegate view.setItemDelegate(delegate) view.openDefaultItems() self.gridlayout.addWidget(view) self.tree_view = view self.xml_root = root return except IOError, ex: MessageBox.error(mainwindow = self, text = 'Could not initialize Database Settings', detailed_text = str(ex)) self.xml_root = None self._config_filename = '' self.configFile = None
def data(self, index, role): """ PyQt API Method -- See the PyQt documentation for a description """ # Handle special drawing of missing models node = index.internalPointer().node if node.tag != "model" or node.get("name") not in self.missing_models: # Not a missing model -- use default data handler return XmlModel.data(self, index, role) # Missing models get a colored description label and a special icon if index.column() == 1: if role == Qt.ForegroundRole: return QVariant(QColor(Qt.red)) elif role == Qt.DisplayRole: return QVariant("(no such model)") # Give it a special icon elif role == Qt.DecorationRole and index.column() == 0: return QVariant(IconLibrary.icon("missing_model")) # Other data properties are handled by the default data() method return XmlModel.data(self, index, role)
def data(self, index, role): ''' PyQt API Method -- See the PyQt documentation for a description ''' # Handle special drawing of missing models node = index.internalPointer().node if node.tag != 'model' or node.get('name') not in self.missing_models: # Not a missing model -- use default data handler return XmlModel.data(self, index, role) # Missing models get a colored description label and a special icon if index.column() == 1: if role == Qt.ForegroundRole: return QVariant(QColor(Qt.red)) elif role == Qt.DisplayRole: return QVariant("(no such model)") # Give it a special icon elif role == Qt.DecorationRole and index.column() == 0: return QVariant(IconLibrary.icon('missing_model')) # Other data properties are handled by the default data() method return XmlModel.data(self, index, role)
def __init__(self, model_root_node, project=None, parent_widget=None): ''' See XmlModel.__init__ for documentation ''' XmlModel.__init__(self, model_root_node, project, parent_widget) self.missing_models = set()
def __init__(self, model_root_node, project=None, parent_widget=None): XmlModel.__init__(self, model_root_node, project, parent_widget)
def __init__(self, model_root_node, project=None, parent_widget=None): """ See XmlModel.__init__ for documentation """ XmlModel.__init__(self, model_root_node, project, parent_widget) self.missing_models = set()
def __init__(self, model_root_node, project = None, parent_widget = None): XmlModel.__init__(self, model_root_node, project, parent_widget)
class TestXmlModel(opus_unittest.TestCase): def setUp(self): self.app = QApplication([], True) self.testdatapath = os.path.split(__file__)[0] self.testdatapath = os.path.join(self.testdatapath, 'testdata') manager_xml_file = os.path.join(self.testdatapath, 'model_manager.xml') self.xml = ElementTree(file=manager_xml_file).getroot() self.instance = XmlModel(self.xml) def test_iconFromType(self): # just test some of the icons expected_results = { 'dir_path': self.instance.folderIcon, 'path': self.instance.folderDatabaseIcon, '-- NOT IN DICT --.': QVariant(), 'defValue': QVariant() } for key, value in expected_results.items(): self.assertEqual(self.instance.iconFromType(key), value) def test_columnCount(self): self.assertEqual(self.instance.columnCount(None), len(self.instance._headers)) def test_rebuild_tree(self): pass def test_index_and_parent(self): idx_child1 = self.instance.index(0, 0, QModelIndex()) idx_child2 = self.instance.index(1, 0, QModelIndex()) idx_child21 = self.instance.index(0, 0, idx_child2) node_child1 = self.instance.root_node().find('child_1') node_child2 = self.instance.root_node().find('child_2') node_child21 = self.instance.root_node().find('child_2/child_21') # Make sure the nodes are actually there self.assertFalse(node_child1 is None) self.assertFalse(node_child2 is None) self.assertFalse(node_child21 is None) # Check to see if the indexes contain the actual nodes self.assertTrue(idx_child1.internalPointer().node is node_child1) self.assertTrue(idx_child21.internalPointer().node is node_child21) # Check parent lookups self.assertEquals(self.instance.parent(idx_child21), idx_child2) self.assertEquals(self.instance.parent(idx_child2), QModelIndex()) self.assertNotEqual(self.instance.parent(idx_child21), self.instance.parent(idx_child2)) def test_data(self): # test that the tag name is returned as display # test that the correct icon is returned for the types # test that QVariant() is returned for bogus values idx_child1 = self.instance.index(0, 1, QModelIndex()) idx_child2 = self.instance.index(1, 0, QModelIndex()) idx_child21 = self.instance.index(0, 0, idx_child2) idx_child21_value = self.instance.index(0, 1, idx_child2) node_child2 = self.instance.root_node().find('child_2') node_child21 = self.instance.root_node().find('child_2/child_21') self.assertEqual(str(self.instance.data(idx_child1, Qt.DisplayRole).toString()), '*********') # password values should be secret self.assertEqual(str(self.instance.data(idx_child2, Qt.DisplayRole).toString()), node_child2.tag) self.assertTrue(node_child21.text is not None) self.assertEqual(str(self.instance.data(idx_child21_value, Qt.DisplayRole).toString()), node_child21.text.strip()) self.assertTrue(self.instance.data(idx_child21, Qt.DecorationRole) is not None) self.assertEqual(self.instance.data(idx_child21, Qt.DecorationRole), QVariant(self.instance.iconFromType(node_child21.get('type')))) def test_rowCount(self): idx_child1 = self.instance.index(0, 1, QModelIndex()) idx_child2 = self.instance.index(1, 0, QModelIndex()) self.assertEquals(self.instance.rowCount(QModelIndex()), 2) self.assertEquals(self.instance.rowCount(idx_child2), 1) self.assertEquals(self.instance.rowCount(idx_child1), 0) def test_remove_node(self): pass def test_removeRow(self): pass def test_index_for_item(self): pass def test_update_node(self): pass def test_item_for_node(self): pass def test_index_for_node(self): pass def test_add_node(self): pass def test_insert_node(self): pass def test_index(self): pass def test_make_item_local(self): pass def test_insertRow(self): pass def test_insert_sibling(self): pass def test_move_up(self): pass def test_move_down(self): pass def test_root_node(self): self.assertTrue(self.instance.root_node() is self.instance._root_node and self.instance._root_node is not None) def test_root_item(self): self.assertTrue(self.instance.root_item() is self.instance._root_item and self.instance._root_item is not None)
class XmlController(object): ''' Controller class for XML Trees. ''' def __init__(self, manager): ''' @param manager (AbstractManager) The parent manager for this XmlController The XmlController will attach itself to manger.base_widget on initialization. ''' self.manager = manager self.project = self.manager.project self.xml_root = manager.xml_root self.model = None self.view = None self.delegate = None self.add_model_view_delegate() self.view.setItemDelegate(self.delegate) self.view.setModel(self.model) self.view.openDefaultItems() self.manager.base_widget.layout().addWidget(self.view) self.view.setContextMenuPolicy(Qt.CustomContextMenu) QObject.connect(self.view, SIGNAL("customContextMenuRequested(const QPoint &)"), self.process_custom_menu) # Actions for common menu choices # Note that revert and delete are the same action, but we want to present them differently # to show that deleting an inherited node (reverting) will keep the node around self.act_remove_selected = self.create_action('delete', 'Delete', self.remove_selected_node) self.act_revert = self.create_action('revert', 'Revert to inherited value', self.remove_selected_node) self.act_make_editable = self.create_action('make_editable', 'Make node local', self.make_selected_editable) self.act_clone_node = self.create_action('clone', 'Duplicate', self.clone_selected_node) self.act_rename_node = self.create_action('rename', 'Rename', self.rename_selected_node) def add_model_view_delegate(self): ''' Initialize and bind the Model, View and Delegate for this controller. This method is called before any initialization of the widgets. Subclasses that wish to use their own model, view and/or delegate should override this method and initialize their own widgets. ''' self.model = XmlModel(self.xml_root, self.project) self.view = XmlView(self.manager.base_widget) self.delegate = XmlItemDelegate(self.view) def create_action(self, icon_name, text, callback): return create_qt_action(icon_name, text, callback, self.view) def rebuild_tree(self): ''' Rebuild the model tree ''' if self.model: self.model.rebuild_tree() # CK: TODO This is a method left from before the refactoring in december, don't know # if it's needed/used def close(self): ''' Closes the controller and removes it from the parent if it is not empty. @return: True. Previous behavior was to return False if self.model was dirty. This is no longer done as dirty checks have been moved. ''' self.view.hide() self.manager.base_widget.layout().removeWidget(self.view) return True def selected_item(self): ''' Get the currently selected item in the controller's view. @return: the selected item (XmlItem) or None if no item is selected. ''' index = self.selected_index() return index.internalPointer() if index else None def selected_index(self): ''' Get the index for the currently selected item in the controller's view. @return: the index (QModelIndex) for the selected item or None ''' index = self.view.currentIndex() if index.isValid(): return index return None def has_selected_item(self): ''' Tests if the controller's view has a selected item or not. @return: True if there is a selected item, otherwise False. ''' return self.selected_index() is not None def process_custom_menu(self, position): ''' Abstract method for creating a context sensitive popup menu. @param position (QPoint) point of request for the popupmenu. ''' item = self.select_item_at(position) if not item: return node = item.node menu = QMenu(self.view) self.add_default_menu_items_for_node(node, menu) if not menu.isEmpty(): menu.exec_(QCursor.pos()) def remove_selected_node(self): ''' Removes the selected item from the model. ''' if not self.has_selected_item(): return index = self.selected_index() self.model.removeRow(index.row(), self.model.parent(index)) self.view.clearSelection() # CK: this is a helper function for the clone_node method, but maybe its general enough to be # promoted to a higher abstraction layer? def _get_unique_name_like_node(self, node): ''' Search sibling items with the same tag to find a name that is guaranteed to be unique. First it tries to insert the item with it's current name. If that fails it appends _copy to the name and tries again. Subsequent tries have _copyX appended to them where X is a number from 1 -> @param node (Element) the item holding the node to find a new name for @return (String) a unique name for the new node. ''' base_name = node.get('name') # Get all the names that currently exist in the same level if node.getparent() is None: # no parent = no siblings return base_name sibling_nodes = node.getparent().getchildren() taken_names = [n.get('name') for n in sibling_nodes] if base_name not in taken_names: return base_name try_name = 'Copy of %s' % base_name copy_number = 0 while try_name in taken_names: copy_number += 1 try_name = 'Copy %d of %s' % (copy_number, base_name) return try_name def clone_selected_node(self): ''' Clone the selected node and insert it as a sibling with a unique name ''' if not self.has_selected_item(): return index = self.selected_index() item = self.selected_item() cloned_node = deepcopy(item.node) cloned_node.set('name', self._get_unique_name_like_node(item.node)) # Insert the cloned node into the tree if self.model.insert_sibling(cloned_node, index) is not None: index_of_clone = self.model.last_inserted_index # Select the new clone if it was inserted if index_of_clone is not None: self.view.setCurrentIndex(index_of_clone) def rename_selected_node(self): ''' Opens a dialog box for changing the node name. ''' if not self.has_selected_item(): return item = self.selected_item() node = item.node taken_names = [n.get('name') for n in node.getparent().getchildren() if not n is node] dialog = RenameDialog(node.get('name'), taken_names, self.view) if dialog.exec_() == dialog.Accepted: node.set('name', dialog.accepted_name) def make_selected_editable(self): ''' Copies the selected node to this project and strips the inhertied flag from all it's immidiate parents and all it's child nodes. ''' if not self.has_selected_item(): return self.model.make_item_local(self.selected_item()) def select_item_at(self, point): ''' Select the item at "point" to visualize which item we are working on and making the item accessible through self.selected_item(). @param point (QPoint): coordinates for where to get the item. @return: The selected item if the point was valid, None otherwise ''' index = self.view.indexAt(point) if not index.isValid or index.column() != 0: # only allow right-clicking on left side nodes return None self.view.setCurrentIndex(index) return index.internalPointer() def add_default_menu_items_for_node(self, node, menu): ''' Append a list of menu items that is common for all nodes regardless of which manager they are in. @param node (Element): node to inspect @param menu (QMenu): menu to append actions to ''' added_actions = [] # Inherited nodes can be made local if node.get('inherited'): added_actions.append(self.act_make_editable) # nodes that do not have a 'name' attribute are special nodes that should not be copyable # for example; <expression_library>, <model_manager> etc.. if node.get('name') is not None: # or node.get('copyable') == 'True': added_actions.append(self.act_clone_node) # named nodes that are not inherited can be renamed if node.get('name') is not None and not node.get('inherited'): added_actions.append(self.act_rename_node) if self.project.is_shadowing(node): added_actions.append(self.act_revert) elif node.tag in _REMOVABLE_NODE_TYPES and not node.get('inherited'): added_actions.append(self.act_remove_selected) # Separate from other items if added_actions and not menu.isEmpty(): menu.addSeparator() map(lambda x: menu.addAction(x), added_actions)
class XmlController(object): ''' Controller class for XML Trees. ''' def __init__(self, manager): ''' @param manager (AbstractManager) The parent manager for this XmlController The XmlController will attach itself to manger.base_widget on initialization. ''' self.manager = manager self.project = self.manager.project self.xml_root = manager.xml_root self.model = None self.view = None self.delegate = None self.add_model_view_delegate() self.view.setItemDelegate(self.delegate) self.view.setModel(self.model) self.view.openDefaultItems() self.manager.base_widget.layout().addWidget(self.view) self.view.setContextMenuPolicy(Qt.CustomContextMenu) QObject.connect(self.view, SIGNAL("customContextMenuRequested(const QPoint &)"), self.process_custom_menu) QObject.connect(self.view, SIGNAL('activated(const QModelIndex&)'), self.on_activated) # Actions for common menu choices # Note that revert and delete are the same action, but we want to present them differently # to show that deleting an inherited node (reverting) will keep the node around self.act_remove_selected = self.create_action( 'delete', 'Delete', self.remove_selected_node) self.act_revert = self.create_action('revert', 'Revert to inherited value', self.remove_selected_node) self.act_make_editable = self.create_action( 'make_editable', 'Make node local', self.make_selected_editable) self.act_clone_node = self.create_action('clone', 'Duplicate', self.clone_selected_node) self.act_rename_node = self.create_action('rename', 'Rename', self.rename_selected_node) self.actExportXMLToFile = self.create_action( 'export', "Export XML Node With Inherited Values To File", self.exportXMLToFile) self.actImportXMLFromFile = self.create_action( 'import', "Import XML Node From File", self.importXMLFromFile) self.actExportXMLToFile_all = self.create_action( 'export_all', "Export all XML Nodes With Inherited Values To File", self.exportXMLToFile) self.actImportXMLFromFile_all = self.create_action( 'import_all', "Import all XML Nodes From File", self.importXMLFromFile) self.actExportXMLToFile_without_inherited = self.create_action( 'export_without_inherited', 'Export XML Node To File', self.export_without_inherited) self.actExportXMLToFile_all_without_inherited = self.create_action( 'export_all_without_inherited', 'Export all XML Nodes To File', self.export_without_inherited) self.act_edit = self.create_action('inspect', 'Edit as XML', self.edit) self.act_edit_all = self.create_action('inspect_all', 'Edit all as XML', self.edit) self.act_copy_to_parent = self.create_action( 'copy_to_parent', 'Copy to parent', self.copy_selected_to_parent) self.act_move_to_parent = self.create_action( 'move_to_parent', 'Move to parent', self.move_selected_to_parent) def add_model_view_delegate(self): ''' Initialize and bind the Model, View and Delegate for this controller. This method is called before any initialization of the widgets. Subclasses that wish to use their own model, view and/or delegate should override this method and initialize their own widgets. ''' self.model = XmlModel(self.xml_root, self.project) self.view = XmlView(self.manager.base_widget) self.delegate = XmlItemDelegate(self.view) def create_action(self, icon_name, text, callback): return create_qt_action(icon_name, text, callback, self.view) def rebuild_tree(self): ''' Rebuild the model tree ''' if self.model: self.model.rebuild_tree() # CK: TODO This is a method left from before the refactoring in december, don't know # if it's needed/used def close(self): ''' Closes the controller and removes it from the parent if it is not empty. @return: True. Previous behavior was to return False if self.model was dirty. This is no longer done as dirty checks have been moved. ''' self.view.hide() self.manager.base_widget.layout().removeWidget(self.view) return True def selected_item(self): ''' Get the currently selected item in the controller's view. @return: the selected item (XmlItem) or None if no item is selected. ''' index = self.selected_index() return index.internalPointer() if index else None def selected_index(self): ''' Get the index for the currently selected item in the controller's view. @return: the index (QModelIndex) for the selected item or None ''' index = self.view.currentIndex() if index.isValid(): return index return None def has_selected_item(self): ''' Tests if the controller's view has a selected item or not. @return: True if there is a selected item, otherwise False. ''' return self.selected_index() is not None def process_custom_menu(self, position): ''' Default method for creating a context sensitive popup menu. Calls self.add_custom_menu_items_for_node() to populate the menu with context-specific menu items. @param position (QPoint) point of request for the popupmenu. ''' item = self.select_item_at(position) assert item is not None node = item.node menu = self.create_custom_menu_for_node(node) if not menu.isEmpty(): menu.exec_(self.view.mapToGlobal(position)) def on_activated(self, index): if not self.execute_default_action(index): self.view.setExpanded(index, not self.view.isExpanded(index)) def execute_default_action(self, index): if self.model.hasChildren(index) and not self.view.isExpanded(index): return node = index.internalPointer().node if node is None: return menu = self.create_custom_menu_for_node(node) act = menu.defaultAction() if act is None: return act.activate(QAction.Trigger) return True def create_custom_menu_for_node(self, node): menu = QMenu(self.view) if node is self.model.root_node(): self.add_default_menu_items_for_widget(menu) elif not self.add_custom_menu_items_for_node(node, menu): if not menu.isEmpty(): menu.addSeparator() self.add_default_menu_items_for_node(node, menu) return menu def set_project_dirty(self): if self.project: self.project.dirty = True def remove_selected_node(self): ''' Removes the selected item from the model. ''' if not self.has_selected_item(): return index = self.selected_index() self.model.removeRow(index.row(), self.model.parent(index)) self.view.clearSelection() self.set_project_dirty() # CK: this is a helper function for the clone_node method, but maybe its general enough to be # promoted to a higher abstraction layer? def _get_unique_name_like_node(self, node): ''' Search sibling items with the same tag to find a name that is guaranteed to be unique. First it tries to insert the item with it's current name. If that fails it appends _copy to the name and tries again. Subsequent tries have _copyX appended to them where X is a number from 1 -> @param node (Element) the item holding the node to find a new name for @return (String) a unique name for the new node. ''' base_name = node.get('name') # Get all the names that currently exist in the same level if node.getparent() is None: # no parent = no siblings return base_name sibling_nodes = node.getparent().getchildren() taken_names = [n.get('name') for n in sibling_nodes] if base_name not in taken_names: return base_name try_name = 'Copy of %s' % base_name copy_number = 0 while try_name in taken_names: copy_number += 1 try_name = 'Copy %d of %s' % (copy_number, base_name) return try_name def clone_selected_node(self): ''' Clone the selected node and insert it as a sibling with a unique name ''' if not self.has_selected_item(): return index = self.selected_index() item = self.selected_item() cloned_node = deepcopy(item.node) cloned_node.set('name', self._get_unique_name_like_node(item.node)) # Insert the cloned node into the tree if self.model.insert_sibling(cloned_node, index) is not None: index_of_clone = self.model.last_inserted_index # Select the new clone if it was inserted if index_of_clone is not None: self.view.setCurrentIndex(index_of_clone) self.set_project_dirty() def rename_selected_node(self): ''' Opens a dialog box for changing the node name. ''' if not self.has_selected_item(): return item = self.selected_item() node = item.node taken_names = [ n.get('name') for n in node.getparent().getchildren() if not n is node ] dialog = RenameDialog(node.get('name'), taken_names, self.view) if dialog.exec_() == dialog.Accepted: node.set('name', dialog.accepted_name) self.set_project_dirty() def make_selected_editable(self): ''' Copies the selected node to this project and strips the inhertied flag from all it's immidiate parents and all it's child nodes. ''' if not self.has_selected_item(): return self.model.make_item_local(self.selected_item()) def rebuild(self): '''rebuild...''' self.model.rebuild_tree() def copy_selected_to_parent(self): ''' Copy the selected item to the first parent configuration ''' if not self.has_selected_item(): return if not get_mainwindow_instance().okToCloseProject( 'copying the node to the parent configuration (reload required)' ): return self.model.copy_to_parent(self.selected_index()) get_mainwindow_instance().reloadProject() def move_selected_to_parent(self): ''' Move the selected item to the first parent configuration ''' if not self.has_selected_item(): return if not get_mainwindow_instance().okToCloseProject( 'moving the node to the parent configuration (reload required)' ): return self.model.move_to_parent(self.selected_index()) self.project.save() get_mainwindow_instance().reloadProject() def select_item_at(self, point): ''' Select the item at "point" to visualize which item we are working on and making the item accessible through self.selected_item(). If the point is invalid, the currently selected item, if any, is deselected, and the root item is returned. @param point (QPoint): coordinates for where to get the item. @return: The selected item if the point was valid, None otherwise ''' index = self.view.indexAt(point) if not index.isValid or index.column( ) != 0: # only allow right-clicking on left side nodes index = self.view.rootIndex() item = self.model.root_item() else: item = index.internalPointer() self.view.setCurrentIndex(index) assert item is not None return item def get_selected_or_root_node(self): if not self.has_selected_item(): return self.model.root_node() else: return self.selected_item().node def get_selected_or_root_node_and_index(self): node = self.get_selected_or_root_node() if node is self.model.root_node(): index = self.model.index_for_item(self.model.root_item()) else: index = self.model.index_for_node(node) assert index is not None return node, index def get_clean_copy_of_selected_node(self, inherited=True): root_node = self.get_selected_or_root_node() root_node = deepcopy(root_node) if not inherited: self.project.xml_config._clean_tree(root_node) # Write out the file self.project.xml_config._indent(root_node) return root_node def export_without_inherited(self): self.exportXMLToFile(inherited=False) def exportXMLToFile(self, inherited=True): ''' NO DOCUMENTATION ''' # Ask the users where they want to save the file start_dir = paths.get_project_configs_path() configDialog = QFileDialog() filter_str = QString("*.xml") fd = configDialog.getSaveFileName(self.manager.base_widget, QString("Save As..."), QString(start_dir), filter_str) # Check for cancel if len(fd) == 0: return fileNameInfo = QFileInfo(QString(fd)) fileName = fileNameInfo.fileName().trimmed() fileNamePath = fileNameInfo.absolutePath().trimmed() saveName = os.path.join(str(fileNamePath), str(fileName)) root_node = self.get_clean_copy_of_selected_node(inherited) ElementTree(root_node).write(saveName) def check_import_node(self, clicked_node, xml_node): if (clicked_node.tag == xml_node.tag) and (clicked_node.get('name') == xml_node.get('name')): return root_dummy = clicked_node.tag if 'name' in clicked_node: root_dummy += ' name="%s"' % clicked_node['name'] root_dummy = '<%s/>' % root_dummy raise ValueError('Expected an element like %s as root element' % root_dummy) def importXMLFromFile(self): ''' NO DOCUMENTATION ''' # print "importXMLFromFile" # First, prompt the user for the filename to read in start_dir = paths.get_project_configs_path() configDialog = QFileDialog() filter_str = QString("*.xml") fd = configDialog.getOpenFileName( self.manager.base_widget, "Please select an XML file to import...", start_dir, filter_str) # Check for cancel if len(fd) == 0: return fileName = QString(fd) # Pass that in to create a new XMLConfiguration try: options = '' xml_tree = load_xml_file(str(fileName)) xml_node = xml_tree.getroot() options = ' or another node in the tree view' self.import_from_node(xml_node) except Exception, e: MessageBox.error(mainwindow=self.view, text='Cannot insert XML file.', detailed_text='XML insert failed. ' '%s. ' 'Please select another XML file%s.' % (e, options)) return
class TestXmlModel(opus_unittest.TestCase): def setUp(self): self.app = QApplication([], True) self.testdatapath = os.path.split(__file__)[0] self.testdatapath = os.path.join(self.testdatapath, 'testdata') manager_xml_file = os.path.join(self.testdatapath, 'model_manager.xml') self.xml = ElementTree(file=manager_xml_file).getroot() self.instance = XmlModel(self.xml) def test_iconFromType(self): # just test some of the icons expected_results = { 'dir_path': self.instance.folderIcon, 'path': self.instance.folderDatabaseIcon, '-- NOT IN DICT --.': QVariant(), 'defValue': QVariant() } for key, value in expected_results.items(): self.assertEqual(self.instance.iconFromType(key), value) def test_columnCount(self): self.assertEqual(self.instance.columnCount(None), len(self.instance._headers)) def test_rebuild_tree(self): pass def test_index_and_parent(self): idx_child1 = self.instance.index(0, 0, QModelIndex()) idx_child2 = self.instance.index(1, 0, QModelIndex()) idx_child21 = self.instance.index(0, 0, idx_child2) node_child1 = self.instance.root_node().find('child_1') node_child2 = self.instance.root_node().find('child_2') node_child21 = self.instance.root_node().find('child_2/child_21') # Make sure the nodes are actually there self.assertFalse(node_child1 is None) self.assertFalse(node_child2 is None) self.assertFalse(node_child21 is None) # Check to see if the indexes contain the actual nodes self.assertTrue(idx_child1.internalPointer().node is node_child1) self.assertTrue(idx_child21.internalPointer().node is node_child21) # Check parent lookups self.assertEquals(self.instance.parent(idx_child21), idx_child2) self.assertEquals(self.instance.parent(idx_child2), QModelIndex()) self.assertNotEqual(self.instance.parent(idx_child21), self.instance.parent(idx_child2)) def test_data(self): # test that the tag name is returned as display # test that the correct icon is returned for the types # test that QVariant() is returned for bogus values idx_child1 = self.instance.index(0, 1, QModelIndex()) idx_child2 = self.instance.index(1, 0, QModelIndex()) idx_child21 = self.instance.index(0, 0, idx_child2) idx_child21_value = self.instance.index(0, 1, idx_child2) node_child2 = self.instance.root_node().find('child_2') node_child21 = self.instance.root_node().find('child_2/child_21') self.assertEqual( str(self.instance.data(idx_child1, Qt.DisplayRole).toString()), '*********') # password values should be secret self.assertEqual( str(self.instance.data(idx_child2, Qt.DisplayRole).toString()), node_child2.tag) self.assertTrue(node_child21.text is not None) self.assertEqual( str( self.instance.data(idx_child21_value, Qt.DisplayRole).toString()), node_child21.text.strip()) self.assertTrue( self.instance.data(idx_child21, Qt.DecorationRole) is not None) self.assertEqual( self.instance.data(idx_child21, Qt.DecorationRole), QVariant(self.instance.iconFromType(node_child21.get('type')))) def test_rowCount(self): idx_child1 = self.instance.index(0, 1, QModelIndex()) idx_child2 = self.instance.index(1, 0, QModelIndex()) self.assertEquals(self.instance.rowCount(QModelIndex()), 2) self.assertEquals(self.instance.rowCount(idx_child2), 1) self.assertEquals(self.instance.rowCount(idx_child1), 0) def test_remove_node(self): pass def test_removeRow(self): pass def test_index_for_item(self): pass def test_update_node(self): pass def test_item_for_node(self): pass def test_index_for_node(self): pass def test_add_node(self): pass def test_insert_node(self): pass def test_index(self): pass def test_make_item_local(self): pass def test_insertRow(self): pass def test_insert_sibling(self): pass def test_move_up(self): pass def test_move_down(self): pass def test_root_node(self): self.assertTrue(self.instance.root_node() is self.instance._root_node and self.instance._root_node is not None) def test_root_item(self): self.assertTrue(self.instance.root_item() is self.instance._root_item and self.instance._root_item is not None)