Comment répondre à une opération interne de glisser-déposer à l'aide d'un QListWidget?

StackOverflow https://stackoverflow.com/questions/1224432

  •  11-07-2019
  •  | 
  •  

Question

J'ai une application Qt4 (utilisant les liaisons PyQt) qui contient un QListWidget , initialisé comme suit:

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

Je peux ajouter des éléments, ce qui me permet de glisser-déposer pour réorganiser la liste. Mais comment puis-je recevoir une notification lorsque la liste est réorganisée par l'utilisateur? J'ai essayé d'ajouter une méthode dropMimeData (self, index, data, action) à la classe, mais cette méthode n'est jamais appelée.

Était-ce utile?

La solution

Je viens d'avoir à faire face à cela et c'est une douleur dans le cul, mais voici ce qu'il faut faire:

Vous devez installer un eventFilter sur votre sous-classe ListWidget , puis rechercher l'événement ChildRemoved . Cet événement couvre à la fois les déplacements et les suppressions. Il devrait donc fonctionner pour réorganiser les éléments par glisser-déposer dans une liste.

J'écris mon Qt en C ++, mais voici une version de 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

Si vous avez une autre classe contenant cette liste, vous souhaiterez peut-être y déplacer la méthode de filtrage des événements. J'espère que cela aide, je sais que je devais me battre avec cela pendant une journée avant de comprendre cela.

Autres conseils

J'ai un moyen plus facile. :)

Vous pouvez réellement accéder au modèle interne du listwidget avec maListe - > modèle () - et à partir de là de nombreux signaux sont disponibles.

Si vous ne vous souciez que du glisser-déposer, connectez-vous à layoutChanged . Si vous avez des boutons de déplacement (qui sont généralement implémentés avec remove + add), connectez-vous également à rowsInserted .

Si vous voulez savoir ce qui a été déplacé, rowsMoved pourrait être meilleur que layoutChanged .

J'ai trouvé la réponse de Trey Stout qui fonctionnait bien, mais je recevais évidemment des événements lorsque l'ordre de la liste n'avait pas changé. Je me suis tourné vers la réponse de Chani qui fonctionne comme prévu, mais sans code, mais il m'a fallu un peu de travail pour l'implémenter en python.

Je pensais partager l'extrait de code pour aider les futurs visiteurs:

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"

Ceci est testé dans PySide mais ne voit aucune raison pour laquelle cela ne fonctionnerait pas dans PyQt.

Pas une solution, mais quelques idées:

Vous devriez probablement vérifier ce qui est renvoyé par supportedDropActions. Vous devrez peut-être écraser cette méthode pour inclure Qt :: MoveAction ou Qt :: CopyAction.

Vous avez le signal QListView :: indexesMoved, mais je ne sais pas s'il sera émis si vous utilisez QListWidget. Cela vaut la peine d'être vérifié.

Je sais que c'est vieux, mais j'ai réussi à faire fonctionner mon code en utilisant la réponse de Trey et je voulais partager ma solution python. Ceci est pour un QListWidget dans un QDialog , pas un sous-classé.

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

L’approche QListWidget.model () semblait la plus élégante des solutions proposées, mais ne fonctionnait pas pour moi dans PyQt5. Je ne sais pas pourquoi, mais peut-être que quelque chose a changé dans le passage à Qt5. L'approche eventFilter a fonctionné, mais il existe une autre alternative à considérer: surcharger le QDropEvent et vérifier si event.source est self. Voir le code ci-dessous qui est un MVCE avec toutes les solutions proposées codées pour vérification dans 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_())
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top