Я хочу иметь приложение, которое имеет пользовательский интерфейс 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()