Search code examples
pythonpython-3.xpyqtpyqt5qlistwidget

How to trigger an event when a QListWidget has contents added or removed


When adding elements to a QListWidget I want to enable or disable an action depending if there are elements in the QListWidget. Is there something list listWidget.itemChanged.connect(self.checkListLength) except for change in number of elements in the QlistWidget I could use? Normally after Each change to the Widget I would just do the check but many different sources will be changing it and it would make more sense to just listen for the change.

I've tried every connect I could find to see if it would return the desired result and it doesn't seem to.

Here is an example that compiles but doesn't enable the action Rename Selection. We're importing filenames into the QListWidget. In this example we could just enable it after using the open folder but for my code it won't be that easy.

import sys
from os import listdir
from os.path import isfile, join
from PyQt5.QtWidgets import (
    QMainWindow, QAction, QHBoxLayout, QWidget, QListWidget, 
    QListWidgetItem, QAbstractItemView, QApplication, QDialog, qApp, QToolBar, QFileDialog
)
from PyQt5.QtGui import QIcon
from PyQt5 import QtCore

class MainGui(QMainWindow):

    def __init__(self):
        super().__init__()
        self.mainWidget = QWidget()
        self.setCentralWidget(self.mainWidget)
        self.initUI()

    def initUI(self):  
        #TopIcon
        self.exitAct = QAction(QIcon('img/x-square.svg'), 'Exit', self)
        self.exitAct.setShortcut('Ctrl+Q')
        self.exitAct.triggered.connect(qApp.quit)

        #Import Folder
        self.importAct = QAction(QIcon('img/folder.svg'), 'Open Folder', self)
        self.importAct.setShortcut('Ctrl+N')
        self.importAct.triggered.connect(self.getFolder)

        #Rename Button
        self.renameAct = QAction(QIcon('img/edit.svg'), 'Rename Selection', self)
        self.renameAct.setShortcut('Ctrl+R')
        self.renameAct.setEnabled(False)

        self.toolbar = QToolBar('ToolBar')
        self.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolbar)
        self.toolbar.addAction(self.exitAct)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.importAct)
        self.toolbar.addAction(self.renameAct)
        self.hbox = QHBoxLayout()
        self.listWidget = QListWidget()
        self.listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.listWidget.setGeometry(QtCore.QRect(10, 10, 211, 291))
        self.hbox.addWidget(self.listWidget)
        self.mainWidget.setLayout(self.hbox)
        self.show()

    def getFolder(self):
        self.ImportFolder = QFileDialog.getExistingDirectory(None, "Select Directory") 
        self.getFilesInDir(self.ImportFolder)

    def getFilesInDir(self, mypath):
        f = []
        files = [f for f in listdir(mypath) if isfile(join(mypath, f))]
        if files:
            self.listWidget.addItems(files)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    gui = MainGui()
    sys.exit(app.exec_())

Solution

  • If you want to know if an item was added or removed to the QListWidget, you must do it through the internal model that has the rowsInserted and rowsRemoved signals, respectively.

    import sys
    from os import listdir
    from os.path import isfile, join
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class MainGui(QtWidgets.QMainWindow):
        def __init__(self):
            super().__init__()
            self.mainWidget = QtWidgets.QWidget()
            self.setCentralWidget(self.mainWidget)
            self.initUI()
    
        def initUI(self):
            # TopIcon
            self.exitAct = QtWidgets.QAction(
                QtGui.QIcon("img/x-square.svg"), "Exit", self
            )
            self.exitAct.setShortcut("Ctrl+Q")
            self.exitAct.triggered.connect(QtWidgets.qApp.quit)
    
            # Import Folder
            self.importAct = QtWidgets.QAction(
                QtGui.QIcon("img/folder.svg"), "Open Folder", self
            )
            self.importAct.setShortcut("Ctrl+N")
            self.importAct.triggered.connect(self.getFolder)
    
            # Rename Button
            self.renameAct = QtWidgets.QAction(
                QtGui.QIcon("img/edit.svg"), "Rename Selection", self
            )
            self.renameAct.setShortcut("Ctrl+R")
            self.renameAct.setEnabled(False)
    
            self.toolbar = QtWidgets.QToolBar("ToolBar")
            self.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolbar)
            self.toolbar.addAction(self.exitAct)
            self.toolbar.addSeparator()
            self.toolbar.addAction(self.importAct)
            self.toolbar.addAction(self.renameAct)
            hbox = QtWidgets.QHBoxLayout(self.mainWidget)
            self.listWidget = QtWidgets.QListWidget()
            self.listWidget.setSelectionMode(
                QtWidgets.QAbstractItemView.ExtendedSelection
            )
            self.listWidget.model().rowsInserted.connect(self.on_rowsInserted)
            self.listWidget.model().rowsRemoved.connect(self.on_rowsRemoved)
            hbox.addWidget(self.listWidget)
            self.show()
    
        @QtCore.pyqtSlot(QtCore.QModelIndex, int, int)
        def on_rowsInserted(self, parent, first, last):
            print("Insert:", parent, first, last)
    
        @QtCore.pyqtSlot(QtCore.QModelIndex, int, int)
        def on_rowsRemoved(self, parent, first, last):
            print("Remove:", parent, first, last)
    
        @QtCore.pyqtSlot()
        def getFolder(self):
            importFolder = QtWidgets.QFileDialog.getExistingDirectory(
                None, "Select Directory"
            )
            self.getFilesInDir(importFolder)
    
        def getFilesInDir(self, mypath):
            files = [f for f in listdir(mypath) if isfile(join(mypath, f))]
            if files:
                self.listWidget.addItems(files)
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        gui = MainGui()
        sys.exit(app.exec_())