QT QItemSelectionModel игнорировать столбцы?

Я пытаюсь ограничить выбор дерева определенным столбцом.

Я активно использую делегатов для создания настраиваемых поведений для каждого элемента в столбце, редакторов и т. д. Я надеялся, что смогу каким-то образом сделать это из делегата, заблокировав событие или что-то подобное. Проблема в том, что я думаю, мне придется создать полностью индивидуальное решение, которое имитирует расширенный выбор.

Однако, после долгих поисков и очень небольшого количества примеров, похоже, что мне нужна пользовательская модель QItemSelectionModel в моем древовидном представлении. Верно ли это предположение?

Как создать пользовательскую модель QItemSelectionModel, которая будет использовать режим расширенного выбора, но позволит мне игнорировать или отменить выбор, если он не находится в определенном столбце. Другими словами, щелчок по другому столбцу не должен изменять выбор (не следует выбирать или отменять выбор).

Я знаю, как добавить модель выбора, когда она у меня есть. Прошу помощи в реализации производного класса (если это нельзя сделать с подключенным сигналом).

Я использую Python, но буду признателен за любую помощь.

Спасибо,

[EDIT:] Я нашел эти похожие вопросы: 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)

person Rafe    schedule 06.06.2012    source источник
comment
Используете ли вы пользовательскую модель для своих данных? Независимо от того, доступны ли элементы для выбора, контролируется моделью. См. 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).   -  person leemes    schedule 06.06.2012
comment
Спасибо за комментарий. Я использую QTreeWidgetItems. Я вижу, что flags может быть прочитано по индексу, но единственный способ установить флаги, который я обнаружил, - это на уровне элемента в QTreeWidgetItem.setFlags(). Я пытался переопределить функцию, но она так и не была вызвана: def flags(self, index): print index.column() return super(DDOutlinerBaseItem, self).flags(index)   -  person Rafe    schedule 06.06.2012
comment
Хорошо, это странно, но модели Qt в любом случае :) Я только что посмотрел исходный код C++ модели выбора. Всякий раз, когда вызывается select(), он даже не смотрит на связанную модель данных. Попробуйте поиграть с некоторыми значениями для QAbstractItemView.selectionBehaviour (qt-project. org/doc/qt-4.8/). Возможно, это представление (а не модель выбора), которое смотрит на модель, когда пользователь хочет что-то выбрать. Если это не поможет, вам нужно дождаться ответа кого-то еще, так как я недостаточно знаю обработку выбора представлений Qt. :(   -  person leemes    schedule 06.06.2012
comment
Спасибо за попытку. Я думаю, что проблема, с которой я столкнулся, заключается в том, что мне нужно установить эти флаги на уровне индекса, а не на уровне элемента, и это, похоже, недоступно. Вот почему я начал искать способы заблокировать или перехватить поведение. В идеале мой делегат должен был бы принять событие мыши, поэтому модель выбора никогда его не увидит, но я не смог найти и этого, так что теперь я смотрю на саму модель. Я добавил еще кое-что к своему вопросу, и я продолжаю взламывать свой путь. Мое королевство для нескольких примеров!   -  person Rafe    schedule 06.06.2012
comment
Я думаю, что уровень элемента == уровень индекса. Индекс определяется его родительским элементом, строкой и столбцом. Под пунктом, я думаю, вы имеете в виду именно эту точность. Должно быть достаточным возвратом невыбираемого флага для любых запросов к методу flag(), когда столбец данного индекса является одним из ваших столбцов, который не должен принимать выбор. Но я думаю, вы это уже поняли - должна быть еще одна ошибка.   -  person leemes    schedule 06.06.2012
comment
Как это будет выглядеть при заполнении дерева? Я довольно новичок в Qt и формирую свою собственную ментальную карту того, как его использовать. Мне кажется, что QTreeWidgetItem - это строка с родителем. Вы можете получить элемент из индекса или индекс из элемента, но индекс содержит столбец, в котором элементу, похоже, все равно. Эти вещи заставляют меня поверить, что индекс - это вещь в определенном столбце для элемента (родительский элемент и строка, столбец). Может ли QTreeWidgetItem также быть столбцом другого QTreeWidgetItem? Это не влияет на решение, которое я выбрал, так как дает больше гибкости, но интересно.   -  person Rafe    schedule 06.06.2012
comment
Элемент — это что-то вроде ячейки в таблице (например, в листе Excel). Насколько мне известно, у каждой ячейки может быть целая таблица в качестве дочернего элемента (подумайте о дереве, где каждый узел представляет собой лист Excel, каждая ячейка имеет свою собственную дочернюю таблицу). Это самая общая (абстрактная) модель элементов, о которой вы только можете подумать, но в большинстве случаев у вас есть либо только один столбец (= простое дерево элементов), ни дочерние элементы (= одна таблица), либо, по крайней мере, несколько столбцов в качестве дочерних ( = таблица с дочерними строками). Я думаю, что в последнем случае column==0 для детей. См. qt-project.org/doc/qabstractitemmodel.html#details.   -  person leemes    schedule 07.06.2012


Ответы (2)


Теоретически следующая настройка должна работать.

В приведенном выше решении можно использовать два отдельных метода и декораторы @pyqtSlot для устранения неоднозначности имен перегруженных методов:

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

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

Это позволяет избежать необходимости проверять экземпляры определенных классов в реализациях метода.

person David Boddie    schedule 30.11.2012

Первая интересная вещь заключается в том, что, поскольку 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)

В моей окончательной реализации я планирую делегировать решение своей системе делегирования, сделав ее универсальной и, теоретически, способной динамически игнорировать любой индекс, который я захочу.

person Rafe    schedule 06.06.2012