Как выдать только один сигнал directoryChanged с помощью QFileSystemWatcher

Вот основная предпосылка моего приложения:

Я установил QFileSystemWatcher для просмотра каталога.

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

Я использовал QFileSystemWatcher в прошлом, и он всегда работал отлично (как для каталогов, так и для изменений файлов).

Приложение отобразит слайд-шоу из изображений в папке \Images. Когда новое изображение помещается в папку \Images, слайд-шоу сбрасывается, чтобы включить новое изображение. Если изображение удаляется из папки \Images, слайд-шоу снова сбрасывается.

Проблема, с которой я сталкиваюсь, заключается в следующем: если я перетаскиваю несколько изображений в папку \Images, сигнал directoryChanged срабатывает несколько раз. Сигнал срабатывает, и соответствующая процедура UpdateImages() запускается для каждого изображения, которое было добавлено в папку, даже если они добавляются одновременно (т. е. выберите несколько изображений, перетащите их в \Images).

Это портит мою рутину. Есть ли способ запустить сигнал directoryChanged один раз для пакета изменений каталога? т.е. Могу ли я отключить сигнал, пока окончательное изображение не будет добавлено в каталог?

Большое спасибо!


person jars121    schedule 17.02.2015    source источник
comment
Я не думаю, что изображения действительно являются одновременными копиями. Если вы не реализуете некоторую задержку во времени, это ожидаемое поведение.   -  person mdurant    schedule 18.02.2015
comment
Не похоже, что есть сигнал, который соответствует вашим потребностям, но вы можете подключить directoryChanged к более умной функции, которая устанавливает флаг или имеет таймер, а не сразу обновлять изображения. Таким образом, вы можете фильтровать события самостоятельно.   -  person all or None    schedule 18.02.2015
comment
Вы также можете исследовать eventFilter, который позволяет вам перехватывать и манипулировать событиями определенного типа.   -  person all or None    schedule 18.02.2015
comment
Спасибо за вклад, ребята, очень признателен. Я не слишком много играл с 'eventFilter', но я вижу, как это может быть полезно. Windows (7, если это имеет значение) «объявляет», сколько файлов будет добавлено в каталог? Если я знаю количество файлов, которые нужно переместить, до их перемещения, я мог бы настроить событие фильтрации, которое запускается при испускании сигнала directoryChanged.   -  person jars121    schedule 18.02.2015


Ответы (1)


Решение состоит в том, чтобы просто не подключать сигнал directoryChanged к слоту, который обновляет изображения. Вместо этого просто устанавливайте флаг всякий раз, когда происходят какие-либо изменения, а затем периодически проверяйте флаг, чтобы увидеть, нужны ли какие-либо обновления (это можно сделать с помощью простого механизма таймера).

Вот базовый сценарий, демонстрирующий идею:

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_())
person ekhumoro    schedule 18.02.2015
comment
Спасибо за ваш комментарий, это очень ценно (как всегда!). Я собираюсь пройтись по вашему коду построчно, но в то же время, является ли опрос единственным практическим способом сделать это? Я не против опроса в этом конкретном случае, я просто предположил, что будет более высокий уровень контроля над установленными сигнальными процессами. Еще раз спасибо! - person jars121; 18.02.2015
comment
@jars121. Проблема не имеет ничего общего с сигналами или даже с Qt в целом: она просто связана с тем, как работают файловые системы. Qt полностью зависит от ОС, когда речь идет об уведомлениях файловой системы. Некоторые операции являются строго атомарными (например, переименование файлов при определенных обстоятельствах), но большинство — нет. QFileSystemWatcher — это довольно простой класс-оболочка, который скрывает большую часть деталей между различными платформами. Это делает его удобным в использовании для простых задач, но ему явно не хватает детального контроля, который могла бы обеспечить сама ОС. - person ekhumoro; 18.02.2015
comment
Честная оценка. С тех пор я еще немного почитал, и кажется, что одно «простое» событие, такое как копирование файла в каталог, на самом деле может включать несколько низкоуровневых сигналов, испускаемых самой ОС. Учитывая, что Python просто читает излучаемые сигналы ОС (как вы сказали), в этом случае мало что можно сделать. В настоящее время я переписываю приложение, чтобы включить флаги и опрос, и похоже, что это будет более чем подходящий альтернативный метод. Еще раз спасибо! - person jars121; 18.02.2015