在 PyQt5 中将主行计数器作为第一列/文本添加到 QTreeView 中?

问题描述 投票:0回答:1

下面的示例代码生成如下 GUI:

code GUI collapsed vs expanded

我想要的是仅在主要条目(图像中的 a、b 和 c)之前添加行计数,如手动编辑的图像所示:

edited GUI collapsed vs expanded

如何在不更改数据/数据模型的情况下做到这一点? (我想我更喜欢插入一个全新的“虚拟列”,但我想一些只在展开/折叠图标/手柄上添加标签的黑客也可能会起作用......)

# https://gist.github.com/nbassler/342fc56c42df27239fa5276b79fca8e6
"""
Reworked code based on
http://trevorius.com/scrapbook/uncategorized/pyqt-custom-abstractitemmodel/
Adapted to Qt5 and fixed column/row bug.
TODO: handle changing data.
"""

import sys
from PyQt5 import QtCore, QtWidgets


class CustomNode(object):
    def __init__(self, data):
        self._data = data
        if type(data) == tuple:
            self._data = list(data)
        if type(data) is str or not hasattr(data, '__getitem__'):
            self._data = [data]

        self._columncount = len(self._data)
        self._children = []
        self._parent = None
        self._row = 0

    def data(self, column):
        if column >= 0 and column < len(self._data):
            return self._data[column]

    def columnCount(self):
        return self._columncount

    def childCount(self):
        return len(self._children)

    def child(self, row):
        if row >= 0 and row < self.childCount():
            return self._children[row]

    def parent(self):
        return self._parent

    def row(self):
        return self._row

    def addChild(self, child):
        child._parent = self
        child._row = len(self._children)
        self._children.append(child)
        self._columncount = max(child.columnCount(), self._columncount)


class CustomModel(QtCore.QAbstractItemModel):
    def __init__(self, nodes):
        QtCore.QAbstractItemModel.__init__(self)
        self._root = CustomNode(None)
        for node in nodes:
            self._root.addChild(node)

    def rowCount(self, index):
        if index.isValid():
            return index.internalPointer().childCount()
        return self._root.childCount()

    def addChild(self, node, _parent):
        if not _parent or not _parent.isValid():
            parent = self._root
        else:
            parent = _parent.internalPointer()
        parent.addChild(node)

    def index(self, row, column, _parent=None):
        if not _parent or not _parent.isValid():
            parent = self._root
        else:
            parent = _parent.internalPointer()

        if not QtCore.QAbstractItemModel.hasIndex(self, row, column, _parent):
            return QtCore.QModelIndex()

        child = parent.child(row)
        if child:
            return QtCore.QAbstractItemModel.createIndex(self, row, column, child)
        else:
            return QtCore.QModelIndex()

    def parent(self, index):
        if index.isValid():
            p = index.internalPointer().parent()
            if p:
                return QtCore.QAbstractItemModel.createIndex(self, p.row(), 0, p)
        return QtCore.QModelIndex()

    def columnCount(self, index):
        if index.isValid():
            return index.internalPointer().columnCount()
        return self._root.columnCount()

    def data(self, index, role):
        if not index.isValid():
            return None
        node = index.internalPointer()
        if role == QtCore.Qt.DisplayRole:
            return node.data(index.column())
        return None


class MyTree():
    """
    """
    def __init__(self):
        self.items = []

        # Set some random data:
        for i in 'abc':
            self.items.append(CustomNode(i))
            self.items[-1].addChild(CustomNode(['d', 'e', 'f']))
            self.items[-1].addChild(CustomNode(['g', 'h', 'i']))

        self.tw = QtWidgets.QTreeView()
        self.tw.setModel(CustomModel(self.items))

    def add_data(self, data):
        """
        TODO: how to insert data, and update tree.
        """
        # self.items[-1].addChild(CustomNode(['1', '2', '3']))
        # self.tw.setModel(CustomModel(self.items))


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mytree = MyTree()
    mytree.tw.show()
    sys.exit(app.exec_())
pyqt5 qtreeview
1个回答
0
投票

好的,我想我已经解决了,基于向 QTreeView/QFileSystemModel 中的项目添加附加信息中的解决方案:

  • CustomModel
    返回一个“被修改的”columnCount() 值,该值在数据的实际列数基础上增加 1
  • CustomModel.data()
    中,检查我们是否在最后一个“被黑”/添加的列中,如果是,则确定该节点是否是“第一级”(除了将引用传递给持有者之外,找不到其他方法数据的,这里
    MyTree
    ,到
    CustomModel
    - 这样我们就可以检索其中的数据
    MyTree.items
    ),如果是,则输出行号(否则不输出任何内容)
  • CustomModel.data()
    中,还可以通过
    section
    检查我们是否在最后一个“被黑”/添加的列中,如果是,则输出
    #
  • 无论
    .setModel()
    在表视图上运行,之后,添加
    .moveSection
    以直观地将“黑客”/添加的列移动到其他位置 - 在本例中,在开头(最左边的位置),即列索引 0。

这样,我就得到了生成此 GUI 的代码:

corrected code GUI

...这正是我想要的(新的第一列现在有点太宽,但我想这并不难修复)。

应用引用答案中的原始解决方案很棘手,因为它们似乎只调用

columnCount()
,为此我收到错误“CustomModel.columnCount() 缺少 1 个必需的位置参数:'index'”。

# https://gist.github.com/nbassler/342fc56c42df27239fa5276b79fca8e6
'''
Reworked code based on
http://trevorius.com/scrapbook/uncategorized/pyqt-custom-abstractitemmodel/

Adapted to Qt5 and fixed column/row bug.

TODO: handle changing data.
'''

import sys
from PyQt5 import QtCore, QtWidgets


class CustomNode(object):
  def __init__(self, data):
    self._data = data
    if type(data) == tuple:
      self._data = list(data)
    if type(data) is str or not hasattr(data, '__getitem__'):
      self._data = [data]

    self._columncount = len(self._data)
    self._children = []
    self._parent = None
    self._row = 0

  def data(self, column):
    if column >= 0 and column < len(self._data):
      return self._data[column]

  def columnCount(self):
    return self._columncount

  def childCount(self):
    return len(self._children)

  def child(self, row):
    if row >= 0 and row < self.childCount():
      return self._children[row]

  def parent(self):
    return self._parent

  def row(self):
    return self._row

  def addChild(self, child):
    child._parent = self
    child._row = len(self._children)
    self._children.append(child)
    self._columncount = max(child.columnCount(), self._columncount)


class CustomModel(QtCore.QAbstractItemModel):
  def __init__(self, dataparent, nodes):
    QtCore.QAbstractItemModel.__init__(self)
    self._root = CustomNode(None)
    self._dataparent = dataparent
    for node in nodes:
      self._root.addChild(node)

  def rowCount(self, index):
    if index.isValid():
      return index.internalPointer().childCount()
    return self._root.childCount()

  def addChild(self, node, _parent):
    if not _parent or not _parent.isValid():
      parent = self._root
    else:
      parent = _parent.internalPointer()
    parent.addChild(node)

  def index(self, row, column, _parent=None):
    if not _parent or not _parent.isValid():
      parent = self._root
    else:
      parent = _parent.internalPointer()

    if not QtCore.QAbstractItemModel.hasIndex(self, row, column, _parent):
      return QtCore.QModelIndex()

    child = parent.child(row)
    if child:
      return QtCore.QAbstractItemModel.createIndex(self, row, column, child)
    else:
      return QtCore.QModelIndex()

  def parent(self, index):
    if index.isValid():
      p = index.internalPointer().parent()
      if p:
        return QtCore.QAbstractItemModel.createIndex(self, p.row(), 0, p)
    return QtCore.QModelIndex()

  def columnCount(self, index):
    hack = 1 # https://stackoverflow.com/q/46835109
    if index.isValid():
      return index.internalPointer().columnCount() +hack
    return self._root.columnCount() +hack

  def data(self, index, role):
    extra = False
    if not index.isValid():
      return None
    extra = index.column() == self.columnCount(index.parent()) - 1 # https://stackoverflow.com/q/46835109 ; no +hack here, else no match!
    node = index.internalPointer()
    is_first_level = (node in self._dataparent.items)
    if role == QtCore.Qt.DisplayRole:
      if extra and is_first_level:
        return "🚣{}".format(index.row()+1) # 1-based count
      else:
        return node.data(index.column())
    return None

  def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole): # https://stackoverflow.com/q/64287713
    if (orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole):
      if section == self.columnCount(QtCore.QModelIndex()) - 1:
        return '#'
    return super().headerData(section, orientation, role)

class MyTree():
  '''
  '''
  def __init__(self):
    self.items = []

    # Set some random data:
    for i in 'abc':
      self.items.append(CustomNode(i))
      self.items[-1].addChild(CustomNode(['d', 'e', 'f']))
      self.items[-1].addChild(CustomNode(['g', 'h', 'i']))

    self.tw = QtWidgets.QTreeView()
    self.tw.setModel(CustomModel(self, self.items))
    last_column_idx = self.tw.model().columnCount(QtCore.QModelIndex()) - 1
    self.tw.header().moveSection(last_column_idx, 0) # https://stackoverflow.com/q/46835109 "Moves the section at visual index `from` to occupy visual index `to`"

  def add_data(self, data):
    '''
    TODO: how to insert data, and update tree.
    '''
    # self.items[-1].addChild(CustomNode(['1', '2', '3']))
    # self.tw.setModel(CustomModel(self.items))


if __name__ == "__main__":
  app = QtWidgets.QApplication(sys.argv)
  mytree = MyTree()
  mytree.tw.show()
  sys.exit(app.exec_())
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.