如何使用QListWidget响应内部拖放操作?

时间:2009-08-03 20:26:20

标签: qt qt4 pyqt pyqt4

我有一个Qt4应用程序(使用PyQt绑定),其中包含一个QListWidget,初始化如下:

class MyList(QtGui.QListWidget):
    def __init__(self):
        QtGui.QListWidget.__init__(self)
        self.setDragDropMode(self.InternalMove)

我可以添加项目,这允许我拖放以重新排序列表。但是如何在用户重新排序列表时收到通知?我尝试在类中添加dropMimeData(self, index, data, action)方法,但它永远不会被调用。

6 个答案:

答案 0 :(得分:12)

我有一个更简单的方法。 :)

您实际上可以使用myList->model()访问listwidget的内部模型 - 从那里可以获得许多信号。

如果您只关心拖放,请连接到layoutChanged。 如果您有移动按钮(通常使用移除+添加实现),也可以连接到rowsInserted

如果您想知道移动的内容,rowsMoved可能比layoutChanged更好。

答案 1 :(得分:7)

我只是不得不处理这个问题,这是一个痛苦的屁股,但这是做什么的:

您必须在eventFilter子类上安装ListWidget,然后注意ChildRemoved事件。此事件包括移动和删除,因此它应该可以在列表中拖放重新排列项目。

我用C ++编写Qt,但这是一个pythonification版本:

class MyList(QtGui.QListWidget):
    def __init__(self):
        QtGui.QListWidget.__init__(self)
        self.setDragDropMode(self.InternalMove)
        self.installEventFilter(self)

    def eventFilter(self, sender, event):
        if (event.type() == QEvent.ChildRemoved):
            self.on_order_changed()
        return False # don't actually interrupt anything

    def on_order_changed(self):
        # do magic things with our new-found knowledge

如果您有其他类包含此列表,您可能希望在那里移动事件过滤器方法。希望这会有所帮助,我知道在解决这个问题之前,我必须与之斗争一天。

答案 2 :(得分:5)

我发现Trey Stout's answer确实有效,但是当列表顺序没有实际改变时,我显然得到了事件。我转向Chani's answer,它可以按要求工作但没有代码,我花了一些时间在python中实现。

我以为我会分享代码段来帮助以后的访问者:

class MyList(QListWidget):
    def __init__(self):
        QListWidget.__init__(self)
        self.setDragDropMode(self.InternalMove)
        list_model = self.model()
        list_model.layoutChanged.connect(self.on_layout_changed)

    def on_layout_changed(self):
        print "Layout Changed"

这是在PySide中测试的,但是没有理由在PyQt中不起作用。

答案 3 :(得分:1)

不是解决方案,而是一些想法:

您应该检查supportedDropActions方法返回的内容。可能需要覆盖该方法,包括Qt :: MoveAction或Qt :: CopyAction。

你有QListView :: indexesMoved信号,但我不确定如果你正在使用QListWidget它是否会被发出。值得一试。

答案 4 :(得分:1)

我知道这是旧的,但我能够使用Trey的答案让我的代码工作,并希望分享我的python解决方案。这是QListWidget内的QDialog,而不是属于子类的class NotesDialog(QtGui.QDialog): def __init__(self, notes_list, notes_dir): QtGui.QDialog.__init__(self) self.ui=Ui_NotesDialog() # the notesList QListWidget is created here (from Qt Designer) self.ui.setupUi(self) # install an event filter to catch internal QListWidget drop events self.ui.notesList.installEventFilter(self) def eventFilter(self, sender, event): # this is the function that processes internal drop in notesList if event.type() == QtCore.QEvent.ChildRemoved: self.update_views() # do something return False # don't actually interrupt anything

{{1}}

答案 5 :(得分:1)

QListWidget.model()方法似乎是最优雅的解决方案,但在PyQt5中对我不起作用。我不知道为什么,但在转向Qt5时可能会有所改变。 eventFilter方法确实有效,但还有另一种方法值得考虑:覆盖QDropEvent并检查event.source是否为self。请参阅下面的代码,其中包含用于检查PyQt5的所有建议解决方案的MVCE:

import sys
from PyQt5 import QtGui, QtWidgets, QtCore


class MyList(QtWidgets.QListWidget):
    itemMoved = QtCore.pyqtSignal()

    def __init__(self):
        super(MyList, self).__init__()
        self.setDragDropMode(self.InternalMove)
        list_model = self.model()
        # list_model.layoutChanged.connect(self.onLayoutChanged)  # doesn't work
        # self.installEventFilter(self)  # works
        self.itemMoved.connect(self.onLayoutChanged)  # works

    def onLayoutChanged(self):
        print("Layout Changed")

    def eventFilter(self, sender, event):
        """
        Parameters
        ----------
        sender : object
        event : QtCore.QEvent
        """
        if event.type() == QtCore.QEvent.ChildRemoved:
            self.onLayoutChanged()
        return False

    def dropEvent(self, QDropEvent):
        """
        Parameters
        ----------
        QDropEvent : QtGui.QDropEvent
        """

        mime = QDropEvent.mimeData()  # type: QtCore.QMimeData
        source = QDropEvent.source()

        if source is self:
            super(MyList, self).dropEvent(QDropEvent)
            self.itemMoved.emit()


app = QtWidgets.QApplication([])
form = MyList()
for text in ("one", "two", "three"):
    item = QtWidgets.QListWidgetItem(text)
    item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
    item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
    item.setCheckState(QtCore.Qt.Checked)
    form.addItem(item)

form.show()
sys.exit(app.exec_())