def insertItem(self, childItem, position=None, parentIndex=None): """ Inserts a childItem before row 'position' under the parent index. If position is None the child will be appended as the last child of the parent. Returns the index of the new inserted child. """ if parentIndex is None: parentIndex = QtCore.QModelIndex() parentItem = self.getItem(parentIndex, altItem=self.invisibleRootItem) nChildren = parentItem.nChildren() if position is None: position = nChildren assert 0 <= position <= nChildren, \ "position should be 0 < {} <= {}".format(position, nChildren) self.beginInsertRows(parentIndex, position, position) try: parentItem.insertChild(childItem, position) finally: self.endInsertRows() childIndex = self.index(position, 0, parentIndex) assert childIndex.isValid( ), "Sanity check failed: childIndex not valid" return childIndex
def indexTupleFromItem(self, treeItem): # TODO: move to BaseTreeItem? """ Return (first column model index, last column model index) tuple for a configTreeItem """ if not treeItem: return (QtCore.QModelIndex(), QtCore.QModelIndex()) if not treeItem.parentItem: # TODO: only necessary because of childNumber? return (QtCore.QModelIndex(), QtCore.QModelIndex()) # Is there a bug in Qt in QStandardItemModel::indexFromItem? # It passes the parent in createIndex. TODO: investigate row = treeItem.childNumber() return (self.createIndex(row, 0, treeItem), self.createIndex(row, self.columnCount() - 1, treeItem))
def hasChildren(self, parentIndex=QtCore.QModelIndex()): """ Returns true if parent has any children; otherwise returns false. Use rowCount() on the parent to find out the number of children. """ parentItem = self.getItem(parentIndex, altItem=self.invisibleRootTreeItem) return parentItem.hasChildren()
def findItemAndIndexPath(self, path, startIndex=None): """ Searches all the model recursively (starting at startIndex) for an item where item.nodePath == path. Returns list of (item, itemIndex) tuples from the start index to that node. Raises IndexError if the item cannot be found. If startIndex is None, or path starts with a slash, searching begins at the (invisible) root item. """ def _getIndexAndItemByName(nodeName, parentItem, parentIndex): """ Searches the parent for a direct child having the nodeName. Returns (item, itemIndex) tuple. Raises IndexError if the item cannot be found. """ if self.canFetchMore(parentIndex): self.fetchMore(parentIndex) for rowNr, childItem in enumerate(parentItem.childItems): if childItem.nodeName == nodeName: childIndex = self.index(rowNr, 0, parentIndex=parentIndex) return (childItem, childIndex) raise IndexError("Item not found: {!r}".format(path)) def _auxGetByPath(parts, item, index): "Aux function that does the actual recursive search" #logger.debug("_auxGetByPath item={}, parts={}".format(item, parts)) if len(parts) == 0: return [(item, index)] head, tail = parts[0], parts[1:] if head == '': # Two consecutive slashes. Just go one level deeper. return _auxGetByPath(tail, item, index) else: childItem, childIndex = _getIndexAndItemByName( head, item, index) return [(item, index)] + _auxGetByPath(tail, childItem, childIndex) # The actual body of findItemAndIndexPath starts here check_is_a_string(path) if not path: raise IndexError("Item not found: {!r}".format(path)) if startIndex is None or path.startswith('/'): startIndex = QtCore.QModelIndex() startItem = self.invisibleRootItem else: startItem = self.getItem(startIndex, None) if not startItem: raise IndexError( "Item not found: {!r}. No start item!".format(path)) return _auxGetByPath(path.split('/'), startItem, startIndex)
def rowCount(self, parentIndex=QtCore.QModelIndex()): """ Returns the number of rows under the given parent. When the parent is valid it means that rowCount is returning the number of children of parent. Note: When implementing a table based model, rowCount() should return 0 when the parent is valid. """ parentItem = self.getItem(parentIndex, altItem=self.invisibleRootItem) return parentItem.nChildren()
def insertItem(self, item, row): """ Insert an item in the store at a certain row. """ check_class(item, self.store.ITEM_CLASS) logger.info("Inserting {!r} at row {}".format(item, row, self)) self.beginInsertRows(QtCore.QModelIndex(), row, row) try: self.store.items.insert(row, item) finally: self.endInsertRows()
def popItemAtRow(self, row): """ Removes a store item from the store. Returns the item """ self.beginRemoveRows(QtCore.QModelIndex(), row, row) try: item = self.store.items[row] del self.store.items[row] return item finally: self.endRemoveRows()
def indexFromItem(self, regItem, col=0): """ Gets the index (with column=0) for the row that contains the regItem If col is negative, it is counted from the end """ if col < 0: col = len(self.attrNames) - col try: row = self.registry.items.index(regItem) except ValueError: return QtCore.QModelIndex() else: return self.index(row, col)
def parent(self, index): """ Returns the parent of the model item with the given index. If the item has no parent, an invalid QModelIndex is returned. A common convention used in models that expose tree data structures is that only items in the first column have children. For that case, when reimplementing this function in a subclass the column of the returned QModelIndex would be 0. (This is done here.) When reimplementing this function in a subclass, be careful to avoid calling QModelIndex member functions, such as QModelIndex.parent(), since indexes belonging to your model will simply call your implementation, leading to infinite recursion. """ if not index.isValid(): return QtCore.QModelIndex() childItem = self.getItem(index, altItem=self.invisibleRootItem) parentItem = childItem.parentItem if parentItem == self.invisibleRootItem: return QtCore.QModelIndex() return self.createIndex(parentItem.childNumber(), 0, parentItem)
def index(self, row, column, parentIndex=QtCore.QModelIndex()): """ Returns the index of the item in the model specified by the given row, column and parent index. Since each item contains information for an entire row of data, we create a model index to uniquely identify it by calling createIndex() it with the row and column numbers and a pointer to the item. (In the data() function, we will use the item pointer and column number to access the data associated with the model index; in this model, the row number is not needed to identify data.) When reimplementing this function in a subclass, call createIndex() to generate model indexes that other components can use to refer to items in your model. """ # logger.debug(" called index({}, {}, {}) {}" # .format(parentIndex.row(), parentIndex.column(), parentIndex.isValid(), # parentIndex.isValid() and parentIndex.column() != 0)) parentItem = self.getItem(parentIndex, altItem=self.invisibleRootTreeItem) #logger.debug(" Getting row {} from parentItem: {}".format(row, parentItem)) if not (0 <= row < parentItem.nChildren()): # Can happen when deleting the last child. #logger.warning("Index row {} invalid for parent item: {}".format(row, parentItem)) return QtCore.QModelIndex() if not (0 <= column < self.columnCount()): #logger.warning("Index column {} invalid for parent item: {}".format(column, parentItem)) return QtCore.QModelIndex() childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: logger.warning( "No child item found at row {} for parent item: {}".format( row, parentItem)) return QtCore.QModelIndex()
def expandBranch(self, index=None, expanded=True): """ Expands or collapses the node at the index and all it's descendants. If expanded is True the nodes will be expanded, if False they will be collapsed. If parentIndex is None, the invisible root will be used (i.e. the complete forest will be expanded). """ treeModel = self.model() if index is None: index = QtCore.QModelIndex() if index.isValid(): self.setExpanded(index, expanded) for rowNr in range(treeModel.rowCount(index)): childIndex = treeModel.index(rowNr, 0, parentIndex=index) self.expandBranch(index=childIndex, expanded=expanded)
def loadFile(self, fileName, rtiClass=None, position=None, parentIndex=QtCore.QModelIndex()): """ Loads a file in the repository as a repo tree item of class rtiClass. Autodetects the RTI type if rtiClass is None. If position is None the child will be appended as the last child of the parent. Returns the index of the newly inserted RTI """ logger.info("Loading data from: {!r}".format(fileName)) if rtiClass is None: repoTreeItem = createRtiFromFileName(fileName) else: repoTreeItem = rtiClass.createFromFileName(fileName) assert repoTreeItem.parentItem is None, "repoTreeItem {!r}".format( repoTreeItem) return self.insertItem(repoTreeItem, position=position, parentIndex=parentIndex)
def expandBranch(self, index=None, expanded=None): """ Expands or collapses the node at the index and all it's descendants. If expanded is True the nodes will be expanded, if False they will be collapsed, and if expanded is None the expanded attribute of each item is used. If parentIndex is None, the invisible root will be used (i.e. the complete forest will be expanded). """ configModel = self.model() if index is None: #index = configTreeModel.createIndex() index = QtCore.QModelIndex() if index.isValid(): if expanded is None: item = configModel.getItem(index) self.setExpanded(index, item.expanded) else: self.setExpanded(index, expanded) for rowNr in range(configModel.rowCount(index)): childIndex = configModel.index(rowNr, configModel.COL_NODE_NAME, parentIndex=index) self.expandBranch(index=childIndex, expanded=expanded)
def columnCount(self, _parentIndex=QtCore.QModelIndex()): """ Returns the number of columns for the children of the given parent. In most subclasses, the number of columns is independent of the parent. """ return len(self.horizontalHeaders)
def rootIndex(self): """ Returns an invalid index, which therefore will point to the root Item""" return QtCore.QModelIndex()