我有一个Qt4应用程序(使用PyQt绑定),其中包含一个QListWidget
,初始化如下:
class MyList(QtGui.QListWidget):
def __init__(self):
QtGui.QListWidget.__init__(self)
self.setDragDropMode(self.InternalMove)
我可以添加项目,这允许我拖放以重新排序列表。但是如何在用户重新排序列表时收到通知?我尝试在类中添加dropMimeData(self, index, data, action)
方法,但它永远不会被调用。
答案 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_())