如何使用一个QListWidget内部拖动和拖放操作应对?
题
我有含有QListWidget
一个的Qt4应用(使用PyQt的绑定),像这样初始化:
class MyList(QtGui.QListWidget):
def __init__(self):
QtGui.QListWidget.__init__(self)
self.setDragDropMode(self.InternalMove)
我可以添加项目,这让我可以拖放重新排序列表。但是,我怎么通知时,列表获取用户重新排序?我尝试添加一个dropMimeData(self, index, data, action)
方法的类,但它从未被调用。
解决方案
我不得不处理这和它在一个痛苦的屁股,但这里是做什么:
您必须对您的eventFilter
子安装ListWidget
,然后查看该ChildRemoved
事件。本次活动涵盖了移动和拆卸,所以应该重新安排项目工作,拖放名单内下降。
我写我的Qt在C ++,但这里有一个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
如果您有一个包含此列表中的一些其他类,你可能希望有移动的事件过滤器的方法。希望这可以帮助,我知道我已经搞清楚了这一点之前,这个打了一天。
其他提示
我有一个更简单的方法。 :)
您可以实际上myList->model()
访问listwidget的内部模型 - 并从那里有很多的信号,可用
如果你只关心拖放,连接到layoutChanged
。
如果有移动按钮(其通常与移除实现+添加)连接到rowsInserted
太
如果你想知道什么是感动,rowsMoved
可能比layoutChanged
更好。
我发现三分球粗壮的回答没有工作,但是我明显越来越事件当列表顺序实际上并没有改变。我转身 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的工作。
不是一个解决方案,但一些想法:
您也许应该检查什么是supportedDropActions方法返回。这可能是你需要覆盖该方法,包括Qt的:: MoveAction或Qt的:: CopyAction。
您有而QListView :: indexesMoved信号,但我不知道它是否会如果你使用QListWidget发出。它实在值得校验。
我知道这是旧的,但我可以把我的代码使用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
在QListWidget.model()
方法似乎是最优雅的提出的解决方案,但对我来说在PyQt5没有工作。我不知道为什么,但也许东西搬到QT5改变。该eventFilter
方法没有工作,但还有另一种替代方案,是值得考虑的:压倒一切的QDropEvent和检查,如果event.source是自我。见下面的代码这是一个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_())