QT QItemSelectionModel 忽略列?

Posted

技术标签:

【中文标题】QT QItemSelectionModel 忽略列?【英文标题】:QT QItemSelectionModel to ignore columns? 【发布时间】:2012-06-06 00:51:00 【问题描述】:

我正在尝试将树的选择限制为特定列。

我大量使用委托来创建自定义的每项每列行为、编辑器等。我希望我可以通过阻止事件或类似的方式从委托中以某种方式做到这一点。问题是,我认为我必须创建一个完全自定义的解决方案来模仿扩展选择。

但是,经过大量搜索和很少的示例后,听起来我想要在我的树视图上自定义 QItemSelectionModel。这个假设正确吗?

如何创建自定义 QItemSelectionModel 将使用扩展选择模式,但如果不在特定列中,则允许我忽略或还原选择。换句话说,点击另一列不应该改变选择(不应该选择或取消选择)

我知道如何添加选择模型。我正在寻求实现派生类的帮助(除非这可以通过连接的信号来完成)。

我正在使用 Python,但希望得到任何帮助。

谢谢,

[编辑:]我发现了这些类似的问题: http://lists.qt.nokia.com/pipermail/qt-interest/2010-September/027647.html

"子类 QItemSelectionModel 并重新实现两个 select 方法以使 您想要的行为。 只需忽略列 > 0 的范围部分。 ... 或者也许只是重新实现 flags() 以使项目不可选择。我不 知道这会不会有任何副作用。”

我尝试在我的 QTreeWidgetItem 上重新实现标志,但它从未被调用:

def flags(self, index):
    print index.column()
    return super(DDOutlinerBaseItem, self).flags(index)

【问题讨论】:

您对数据使用自定义模型吗?项目是否可选择由模型控制。请参阅QAbstractItemModel::flags() (qt-project.org/doc/qt-4.8/qabstractitemmodel.html#flags) 和 Qt::ItemFlags (qt-project.org/doc/qt-4.8/qt.html#ItemFlag-enum)。 感谢您的评论。我正在使用 QTreeWidgetItems。我看到每个索引都可以读取flags,但我发现设置标志的唯一方法是在 QTreeWidgetItem.setFlags() 中的项目级别。我试图覆盖该函数,但它从未被调用:def flags(self, index): print index.column() return super(DDOutlinerBaseItem, self).flags(index) 好吧,这很奇怪,但无论如何Qt的模型都是这样的:) 我只是查看了选择模型的C++源代码。每当调用select() 时,它甚至都不查看关联的数据模型。尝试使用 QAbstractItemView.selectionBehaviour (qt-project.org/doc/qt-4.8/…) 的一些值。当用户想要选择某些东西时,可能是视图(而不是选择模型)查看模型。如果这没有帮助,您需要等待其他人的回答,因为我对 Qt 视图的选择处理知之甚少。 :( 感谢您的努力。我认为我面临的问题是我需要在索引级别而不是项目级别设置这些标志,而且这似乎不可用。这就是为什么我开始寻找阻止或拦截行为的方法。理想情况下,我的代表会接受鼠标事件,因此选择模型甚至都看不到它,但我也找不到,所以现在我正在查看模型本身。我在我的问题中添加了更多内容,并继续破解我的方式。我的王国举几个例子! 我认为项目级别 == 索引级别。索引由其父项、行和列定义。对于“项目”,我认为您的意思正是这种精度。当给定索引的列是不应该接受选择的列之一时,它应该为对 flag() 方法发出的任何请求返回一个不可选择的标志就足够了。但我想你已经明白了——肯定还有另一个错误。 【参考方案1】:

理论上,以下调整应该有效。

上述解决方案可以使用两个单独的方法和@pyqtSlot 装饰器来消除重载方法名称的歧义:

@pyqtSlot(QModelIndex, QItemSelectionModel.SelectionFlags)
  def select(self, index, command):
    # ...

@pyqtSlot(QItemSelection, QItemSelectionModel.SelectionFlags)
  def select(self, selection, command):
    #...

这避免了在方法实现中检查某些类的实例的需要。

【讨论】:

【参考方案2】:

第一个有趣的事情是,由于 Python 不能重载方法,所以我的 select 方法似乎只是被调用了两次,参数 0 中的每种类型调用一次。下面是一个示例来说明这一点以及基本设置。我的 QTreeWidget 的树叫做“树”(self.tree)

    # in __init__ of my QTreeWidget:
    sel_model = ColumnSelectionModel(self.tree.model())
    self.tree.setSelectionModel(sel_model)

class ColumnSelectionModel(QtGui.QItemSelectionModel):
    def select(self, selection, selectionFlags):
    """
    Runs both QItemSelectionModel.select methods::

        1. select(QtCore.QModelIndex, QItemSelectionModel.SelectionFlags)
        2. select(QtGui.QItemSelection, QItemSelectionModel.SelectionFlags)

    The first seems to run on mouse down and mouse up.
    The second seems to run on mouse down, up and drag
    """
    print("select(%s,  %s)" % (type(selection), type(selectionFlags)))

    if isinstance(selection, QtGui.QItemSelection):
        infos = []
        for index in selection.indexes():
            infos.append(("index=%s row=%s column=%s" 
                                    % (index, index.row(), index.column())))

        print ", ".join(infos)
    elif isinstance(selection, QtCore.QModelIndex):
        index = selection
        print("index=%s row=%s column=%s" % (index, index.row(), index.column()))
    else:
        raise Exception("Unexpected type for arg 0: '%s'" % type(selection))

    super(ColumnSelectionModel, self).select(selection, selectionFlags)

这似乎解决了我的问题:

class ColumnSelectionModel(QtGui.QItemSelectionModel):
    def __init__(self, model):
        super(ColumnSelectionModel, self).__init__(model)

        self.selectable_columns = [0]
        """ Set the columns that are allowed to be selected """


    def select(self, selection, selectionFlags):
        """
        Ignores any selection changes if an item is not in one of the columns
        in the self.selectable_columns list.

        Is run by both QItemSelectionModel.select methods::

            1. select(QtCore.QModelIndex, QItemSelectionModel.SelectionFlags)
            2. select(QtGui.QItemSelection, QItemSelectionModel.SelectionFlags)

        The first seems to run on mouse down and mouse up.
        The second seems to run on mouse down, up and drag
        """
        if isinstance(selection, QtGui.QItemSelection):
            # This is the overload with the QItemSelection passed to arg 0
            # Loop over all the items and if any are not in selectable_columns
            #   ignore this event. This works because it is run for every change
            #   so the offending selection index will always be the newest
            indexes = selection.indexes()
            for i in xrange(len(indexes)):
                index = indexes[i]
                if not index.column() in self.selectable_columns:
                    return
        elif isinstance(selection, QtCore.QModelIndex):
            # This is the overload with the QModelIndex passed to arg 0
            # If this index isn't in selectable_columns, just ignore this event
            index = selection
            if not index.column() in self.selectable_columns:
                return
        else:  # Just in case
            raise Exception("Unexpected type for arg 0: '%s'" % type(selection))

        # Fall through. Select as normal
        super(ColumnSelectionModel, self).select(selection, selectionFlags)

在我的最终实现中,我计划将决定委托给我的委托系统,使其具有通用性,并且理论上能够动态忽略我想要的任何索引。

【讨论】:

以上是关于QT QItemSelectionModel 忽略列?的主要内容,如果未能解决你的问题,请参考以下文章

Qt QItemSelection::indexes() 返回错误

在 Qt 中找不到 Qlistview Selectionchanged 事件?

QTreeView

如何在 Qt 中获取选定的列表项索引

调用 QItemSelectionModel::select 后如何重绘 QTreeView?

QItemSelectionModel currentChange 信号在应用程序启动时发出?