Как реагировать на внутреннюю операцию перетаскивания с помощью QListWidget?
у меня есть приложение Qt4 (используя привязки PyQt), которое содержит QListWidget
инициализируется вот так:
class MyList(QtGui.QListWidget):
def __init__(self):
QtGui.QListWidget.__init__(self)
self.setDragDropMode(self.InternalMove)
Я могу добавлять элементы, и это позволяет мне перетаскивать, чтобы изменить порядок списка. Но как получить уведомление, когда список будет переупорядочен пользователем? Я попытался добавить dropMimeData(self, index, data, action)
метод для класса, но он никогда не вызывается.
6 ответов
Я просто имел дело с этим и это боль в заднице, но вот что нужно делать:
вы должны установить 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
если у вас есть другой класс, который содержит этот список, вы, возможно, захотите переместите туда метод фильтра событий. Надеюсь, это поможет, я знаю, что мне пришлось бороться с этим в течение дня, прежде чем понять это.
у меня есть более простой способ. :)
вы можете получить доступ к внутренней модели listwidget с помощью myList->model()
- и оттуда есть много сигналов.
если вы заботитесь только о drag & drop, подключитесь к layoutChanged
.
Если у вас есть кнопки перемещения (которые обычно реализуются с помощью remove+add), подключитесь к rowsInserted
тоже.
если вы хотите знать, что переехала, rowsMoved
может быть лучше, чем layoutChanged
.
нашел ответ Трея Стаута работал, однако я, очевидно, получал события, когда порядок списка фактически не изменился. Я повернулся к Чани!--3--> который работает по мере необходимости, но без кода мне потребовалось немного работы для реализации в 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. Это проверка.
Я знаю, что это старый, но я смог заставить свой код работать с помощью ответа Трея и хотел поделиться своим решением 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. The eventFilter
подход действительно работал, но есть еще одна альтернатива, которую стоит рассмотреть: превышение qdropevent и проверка события if.источник-это Я. См. код ниже, который является 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_())