开发者

qt: QTreeView - limit drag and drop to only happen within a particlar grandparent (ancestor)

I have a QTreeView in which I have implemented drag and drop to allow the re-ordering of items.

GIven the following example of a tree:

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...

Anything marked as draggable may be dragged. Anything marked as parentable may have a draggable item as a child. Anything marked fixed is, well, fixed.

My question is, how would I go about limiting the dropping of an item to stay within a particular parent? For example, I could drag 'bloodOrange' and make it a child of 'apple' or 'orange' (or even just change its ordinal position inside of 'Fruit'), but I should not be able to make it a child of carrots or lettuce etc.

I've managed to correctly code the flags for everything except the part that limits a drop operation to stay within a particular parent. I just don't know how to capture the currently dragged QModelIndex (from which I can determine parent, grandparent, etc.)

Thanks!

Here is my code for the flags method in case it helps: Note: I refer to the top level children as Nodes (i.e. 'Food'), the next level as Groups (i.e. 'Fruit'), and the final two levels (i.e. lettuce and icebergLe开发者_开发问答ttuce) are both 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 


If you want the drag to display the "not allowed" icon when hovering over certain rows, I believe you can't do it from the model. You'd have to intercept the dragEnter/Move events on the View wiedget.

However, dropMimeData() can return False to indicate that the drop is rejected.

Note that (in my Qt version) there is a bug in qdnd_win regarding drops that are rejected by the model. Here is my workaround based on some source diving; this is a method defined on my QTreeView subclass:

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)

(note that by "not-insignificant work" I really mean "I don't want to bother intercepting the events" :-)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜