9

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

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

我可以添加项目,这允许我拖放以重新排序列表。但是当列表被用户重新排序时,我如何获得通知?我尝试向dropMimeData(self, index, data, action)该类添加一个方法,但它从未被调用。

4

6 回答 6

15

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

您实际上可以使用 - 访问列表小部件的内部模型,myList->model()并且从那里有很多可用的信号。

如果您只关心拖放,请连接到layoutChanged. 如果您有移动按钮(通常使用 remove+add 实现)rowsInserted也连接到。

如果你想知道什么移动了,rowsMoved可能比layoutChanged.

于 2012-11-13T20:20:53.403 回答
7

我只需要处理这个问题,这让我很头疼,但这是该怎么做:

你必须eventFilter在你的ListWidget子类上安装一个,然后观察ChildRemoved事件。此事件涵盖移动和删除,因此它应该适用于通过拖放在列表中重新排列项目。

我用 C++ 编写我的 Qt,但这里有一个 python 化版本:

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

如果您有其他包含此列表的类,您可能希望将事件过滤器方法移到那里。希望这会有所帮助,我知道在弄清楚这一点之前我必须为此奋斗一天。

于 2009-10-06T21:31:22.773 回答
5

我发现Trey Stout 的回答确实有效,但是当列表顺序实际上没有改变时,我显然得到了事件。我求助于Chani 的答案,它确实可以按要求工作,但没有代码,我花了一些时间在 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 中不起作用的原因。

于 2012-12-01T16:04:45.193 回答
1

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

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

您有 QListView::indexesMoved 信号,但我不确定如果您使用 QListWidget 是否会发出它。值得检查。

于 2009-08-05T08:20:15.617 回答
1

我知道这很旧,但我能够使用 Trey 的答案让我的代码工作,并想分享我的 python 解决方案。这是针对QListWidget内部的 a 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
于 2012-09-19T21:39:25.380 回答
1

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

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_())
于 2017-10-03T14:52:25.560 回答