qt: QTreeView - ограничить перетаскивание только внутри определенного прародителя (предка)

У меня есть QTreeView, в котором я реализовал перетаскивание, чтобы разрешить изменение порядка элементов.

Дан следующий пример дерева:

Food                        <--fixed
|--Vegetables               <--fixed
|  |--carrots            <-- draggable/parentable
|  |--lettuce            <-- draggable/parentable
|  |  |--icebergLettuce  <-- draggable but NOT parentable
|--Fruit                    <-- fixed
|  |--apple              <-- draggable/parentable
|  |--orange             <-- draggable/parentable
|  |--bloodOrange        <-- draggable/parentable
etc...

Все, что помечено как перетаскиваемое, можно перетаскивать. Все, что помечено как родительское, может иметь перетаскиваемый элемент в качестве дочернего. Все, что отмечено как исправленное, исправлено.

Мой вопрос в том, как я могу ограничить удаление элемента, чтобы он оставался внутри определенного родителя? Например, я мог бы перетащить «bloodOrange» и сделать его потомком «apple» или «апельсина» (или даже просто изменить его порядковое положение внутри «Fruit»), но я не смог бы сделать его потомком моркови. или салат и т.д.

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

Спасибо!

Вот мой код для метода флагов на случай, если это поможет: Примечание. Я называю дочерние элементы верхнего уровня узлами (т. е. «Еда»), следующий уровень — группами (т. е. «Фрукты») и последние два уровня (т. е. «Фрукты»). lettuce и icebergLettuce) являются Params.

#---------------------------------------------------------------------------
def flags(self, index):
    """
    Returns whether or not the current item is editable/selectable/etc. 
    """

    if not index.isValid():
        return QtCore.Qt.ItemIsEnabled

    #by default, you can't do anything
    enabled = QtCore.Qt.NoItemFlags
    selectable = QtCore.Qt.NoItemFlags
    editable = QtCore.Qt.NoItemFlags
    draggable = QtCore.Qt.NoItemFlags
    droppable = QtCore.Qt.NoItemFlags

    #get a pointer to the referenced object
    item = index.internalPointer()

    #only 'valid' cells may be manipulated ('valid' is defined by the obj)
    if item.column_is_valid(index.column()):

        #all valid cells are selectable and enabled
        selectable = QtCore.Qt.ItemIsSelectable
        enabled = QtCore.Qt.ItemIsEnabled

        #column 0 cells may occasionally be dragged and dropped
        if index.column() == 0:

            #drag/drop is only possible if it is a param...
            if item.get_obj_type() == 'param':

                #...and then only child-less params may be dragged...
                if item.get_child_count() == 0:
                    draggable = QtCore.Qt.ItemIsDragEnabled

                #...and only params with a group as parent may be dropped on
                if item.get_parent().get_obj_type() == "group":
                    droppable = QtCore.Qt.ItemIsDropEnabled

        #all other valid columns > 0 may be edited (no drag or drop)
        else:                
            editable = QtCore.Qt.ItemIsEditable

    #return our flags.
    return enabled | selectable| editable| draggable| droppable 

person bvz    schedule 18.11.2010    source источник


Ответы (1)


Если вы хотите, чтобы при перетаскивании отображался значок «не разрешено» при наведении курсора на определенные строки, я считаю, что вы не можете сделать это из модели. Вам придется перехватывать события dragEnter/Move в виджете View.

Однако dropMimeData() может возвращать значение False, чтобы указать, что удаление отклонено.

Обратите внимание, что (в моей версии Qt) в qdnd_win есть ошибка, связанная с отбрасыванием отбрасываемых моделью. Вот мой обходной путь, основанный на некоторых источниках; это метод, определенный в моем подклассе QTreeView:

def dropEvent(self, evt):
    QTreeView.dropEvent(self, evt)
    if not evt.isAccepted():
        # qdnd_win.cpp has weird behavior -- even if the event isn't accepted
        # by target widget, it sets accept() to true, which causes the executed
        # action to be reported as "move", which causes the view to remove the
        # source rows even though the target widget didn't like the drop.
        # Maybe it's better for the model to check drop-okay-ness during the
        # drag rather than only on drop; but the check involves not-insignificant work.
        evt.setDropAction(Qt.IgnoreAction)

(обратите внимание, что под «незначительной работой» я действительно подразумеваю «я не хочу перехватывать события» :-)

person Paul Du Bois    schedule 15.02.2011
comment
Это было очень полезно, спасибо, я столкнулся с этой проблемой. - person TrevorB; 12.03.2014