монитор win32file.ReadDirectoryChangesW в приложении pythonQt - PullRequest
0 голосов
/ 13 января 2020

Я хочу иметь приложение, которое имеет пользовательский интерфейс PyQt, но также отслеживает изменения в каталоге. Я считаю, что правильный способ сделать это - иметь win32file.ReadDirectoryChangesW

. В старые времена я бы подключил выход win32file.ReadDirectoryChangesW в качестве сигнала к некоторому слоту, но я не Не знаю, использует ли Qt сигналы и слоты больше.

Еще один способ сделать это - использовать WaitForMultipleObjects, возможно, с таймаутом, но я не могу понять, как интегрировать WaitForMultipleObjects с pyqt application.

Поэтому я попытался создать отдельный поток Python для синхронного вызова win32file.ReadDirectoryChangesW, но когда во вспомогательном потоке был вызван win32file.ReadDirectoryChangesW, основной поток заблокирован.

Ниже моя тестовая программа, которая не работает Я бы хотел получать уведомления об изменениях файлов в основном потоке, но я не уверен, как это сделать. Глядя на do c для ReadDirectoryChangesW, похоже, что правильно использовать режим функции asyn c, но я пока не понял, как запустить его из Python ( большая часть кода здесь собрана из других постов, которые я видел).

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-

"""
watchpot.py

Because a watched pot never boils.

Watch a directory and bring up a GUI when there is a new file created.
Also implements a simple chat.
"""

import sys
import os
import re
import glob
import configparser
import tempfile
import shutil
import threading

WATCH_DIR="h:\\"

################################################################
### Qt SUpport

# Big surprise --- the new LaTeX distribution includes a broken Qt distribution
# Take it out of the path temporarily.

OLDPATH=os.environ['PATH']
os.environ['PATH'] = ";".join([part for part in OLDPATH.split(";") if "tex" not in part.lower()])

# Sometimes we are on cygwin32 and running with the wrong python
# interpreter, so be sure we have the anaconda3 site-packages loaded so we
# can get win32
USER=os.getenv('USER')
sys.path.append(r"c:\Users\%s\appdata\local\Continuum\anaconda3\lib\site-packages" % USER)

os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = r'~qgis directory\apps\Qt5\plugins'
os.environ['PATH'] += r';~qgis directory\apps\qgis\bin;~qgis directory\apps\Qt5\bin'


from PyQt5 import QtCore
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

# Simple chat client from:
# https://dev.to/m_herrmann/python-qt-tutorial-create-a-simple-chat-client-cgl

################################################################

################################################################
## File watching support

import win32file
import win32con

# The code we found online runs synchronouse mode, which is blocking.
# We want to run in asynchronous mode, so we don't block with the Qt event loop
# https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw

def dirwatch_function(*,watchdir):
    FILE_LIST_DIRECTORY = 0x0001
    ACTIONS = {
        1 : "Created",
        2 : "Deleted",
        3 : "Updated",
        4 : "Renamed from something",
        5 : "Renamed to something"
    }
    print("dirwatch point 0")
    hDir = win32file.CreateFile (
        watchdir,
        FILE_LIST_DIRECTORY,
        win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
        None,
        win32con.OPEN_EXISTING,
        win32con.FILE_FLAG_BACKUP_SEMANTICS,
        None
    )
    #
    # ReadDirectoryChangesW takes a previously-created
    # handle to a directory, a buffer size for results,
    # a flag to indicate whether to watch subtrees and
    # a filter of what changes to notify.
    #
    # NB Tim Juchcinski reports that he needed to up
    # the buffer size to be sure of picking up all
    # events when a large number of files were
    # deleted at once.
    #
    print("dirwatch piont 1")
    results = win32file.ReadDirectoryChangesW (
        hDir,
        1024,
        True,
        win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
        win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
        win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
        win32con.FILE_NOTIFY_CHANGE_SIZE |
        win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
        win32con.FILE_NOTIFY_CHANGE_SECURITY,
        None,
        None
    )
    print("dirwatch point 2")
    for action, file in results:
        full_filename = os.path.join (path_to_watch, file)
        print(full_filename, ACTIONS.get(action, "Unknown"))



def run():
    app = QApplication([])

    text_area = QTextEdit()
    text_area.setFocusPolicy(Qt.NoFocus)
    message = QLineEdit()
    layout = QVBoxLayout()
    layout.addWidget(text_area)
    layout.addWidget(message)
    window = QWidget()
    window.setLayout(layout)
    window.show()

    # Event handlers:
    def refresh_messages():
        #text_area.setHtml(requests.get(chat_url).text)
        text_area.append("ping")

    def send_message():
        #requests.post(chat_url, {'name': name, 'message': message.text()})
        text_area.append(message.text())
        message.clear()

    # Signals:
    message.returnPressed.connect(send_message)
    timer = QTimer()
    timer.timeout.connect(refresh_messages)
    timer.start(1000)
    app.exec_()


if __name__=="__main__":
    import argparse
    parser = argparse.ArgumentParser(description='Have a window for receiving dragged icons',
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("--debug",   action="store_true")
    parser.add_argument("--watchdir",     help="Directory to watch", default=WATCH_DIR)

    args = parser.parse_args()
    from PyQt5.QtWidgets import QApplication

    dirwatch_thread = threading.Thread(target=dirwatch_function, name='dirwatch', daemon=True,
                                       kwargs={'watchdir':args.watchdir})
    dirwatch_thread.start()

    run()

...