Search code examples
pythonpyqtpysideqfilesystemwatcher

How to only emit one directoryChanged signal with QFileSystemWatcher


Here's the basic premise of my application:

I've established a QFileSystemWatcher to watch a directory.

Path = [r'C:\Users\user\Documents\Images']
DirectoryWatcher = QtCore.QFileSystemWatcher(Path)
DirectoryWatcher.directoryChanged.connect(showImages.UpdateImages)

I've used QFileSystemWatcher in the past, and it's always worked perfectly (for both directory and file changes).

The application will display a slideshow of the images in the \Images folder. When a new image is placed in the \Images folder, the slideshow is reset to include the new image. If an image is removed from the \Images folder, the slideshow is again reset.

The issue I'm having is this: if I drag multiple images into the \Images folder, the directoryChanged signal is fired multiple times. The signal is fired, and the corresponding UpdateImages() routine is run for each image that's been added to the folder, even when they're added at the same time (i.e. select multiple images, drag and drop them into \Images).

This is playing havoc with my routine. Is there any way to fire a directoryChanged signal once for a batch of directory changes? I.e. can I disable the signal until the final image has been added to the directory?

Many thanks!


Solution

  • The solution is to simply avoid connecting the directoryChanged signal to the slot that updates the images. Instead, just set a flag whenever any changes occur, and then periodically check the flag to see if any updates are needed (this can be done with a simple timer mechanism).

    Here's a basic script that demonstrates the idea:

    import sys, os
    from PyQt4 import QtCore, QtGui
    
    class Window(QtGui.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            self.list = QtGui.QListWidget(self)
            self.button = QtGui.QPushButton('Choose Directory', self)
            self.button.clicked.connect(self.handleSetDirectory)
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.list)
            layout.addWidget(self.button)
            self.watcher = QtCore.QFileSystemWatcher(self)
            self.watcher.directoryChanged.connect(self.handleDirectoryChanged)
            self.timer = QtCore.QTimer(self)
            self.timer.setInterval(500)
            self.timer.timeout.connect(self.handleTimer)
            self.handleSetDirectory(QtCore.QDir.tempPath())
    
        def handleDirectoryChanged(self):
            self.timer.stop()
            print('Directory Changed')
            self._changed = True
            self.timer.start()
    
        def handleSetDirectory(self, directory=None):
            if not directory:
                directory = QtGui.QFileDialog.getExistingDirectory(self)
            if directory:
                self.timer.stop()
                self.watcher.removePaths(self.watcher.directories())
                self._changed = False
                self.watcher.addPath(directory)
                self.updateList()
                self.timer.start()
    
        def handleTimer(self):
            if self._changed:
                self._changed = False
                self.updateList()
    
        def updateList(self):
            print('Update List')
            self.list.clear()
            for directory in self.watcher.directories():
                self.list.addItems(os.listdir(directory))
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.resize(250, 600)
        window.show()
        sys.exit(app.exec_())