Come si risponde a un'operazione di trascinamento della selezione interna utilizzando un QListWidget?

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

  •  11-07-2019
  •  | 
  •  

Domanda

Ho un'applicazione Qt4 (usando i collegamenti PyQt) che contiene un QListWidget , inizializzato in questo modo:

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

Posso aggiungere elementi e questo mi permette di trascinare e rilasciare per riordinare l'elenco. Ma come posso ricevere una notifica quando l'elenco viene riordinato dall'utente? Ho provato ad aggiungere un metodo dropMimeData (self, index, data, action) alla classe, ma non viene mai chiamato.

È stato utile?

Soluzione

Ho dovuto affrontare questo ed è un dolore nel culo, ma ecco cosa fare:

Devi installare un eventFilter nella tua sottoclasse ListWidget e quindi guardare l'evento ChildRemoved . Questo evento copre sia le mosse che la rimozione, quindi dovrebbe funzionare per riordinare gli oggetti trascinandoli all'interno di un elenco.

Scrivo il mio Qt in C ++, ma ecco una versione di pitonificazione:

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

Se hai un'altra classe che contiene questo elenco, potresti voler spostare lì il metodo di filtro degli eventi. Spero che questo aiuti, so che ho dovuto combattere con questo per un giorno prima di capirlo.

Altri suggerimenti

Ho un modo più semplice. :)

Puoi effettivamente accedere al modello interno di listwidget con myList- > model () - e da lì ci sono molti segnali disponibili.

Se ti interessa solo trascinare e rilasciare, connettiti a layoutChanged . Se hai pulsanti di spostamento (che di solito sono implementati con remove + add), connettiti anche a righeInserted .

Se vuoi sapere cosa è stato spostato, rowsMoved potrebbe essere migliore di layoutChanged .

Ho trovato la risposta di Trey Stout ha funzionato, ma ovviamente stavo ricevendo eventi quando l'ordine della lista non era realmente cambiato. Mi sono rivolto a la risposta di Chani che funziona come richiesto ma senza codice mi ci è voluto un po 'di lavoro per implementarlo in Python.

Ho pensato di condividere lo snippet di codice per aiutare i futuri visitatori:

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"

Questo è testato in PySide ma non vedo alcun motivo per cui non funzionerebbe in PyQt.

Non una soluzione, ma alcune idee:

Probabilmente dovresti controllare cosa viene restituito dal metodo supportDropActions. Potrebbe essere necessario sovrascrivere quel metodo per includere Qt :: MoveAction o Qt :: CopyAction.

Hai il segnale QListView :: indexesMoved, ma non sono sicuro che verrà emesso se stai usando QListWidget. Vale la pena controllare.

So che questo è vecchio, ma sono stato in grado di far funzionare il mio codice usando la risposta di Trey e volevo condividere la mia soluzione Python. Questo è per un QListWidget all'interno di un QDialog , non uno che è sottoclassato.

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'approccio QListWidget.model () sembrava la più elegante delle soluzioni proposte ma non ha funzionato per me in PyQt5. Non so perché, ma forse qualcosa è cambiato nel passaggio a Qt5. L'approccio eventFilter ha funzionato, ma c'è un'altra alternativa che vale la pena prendere in considerazione: sovrascrivere QDropEvent e verificare se event.source è autonomo. Vedi il codice sotto che è un MVCE con tutte le soluzioni proposte codificate per il controllo in 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_())
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top