PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。Linux/X11, macOS および Microsoft Windows をサポートしています。配布ライセンスは LGPL で公開されています。
Watchdog は、ファイルシステムのイベントを監視するための Python API とシェルユーティリティです [1]。
Python で、特定のディレクトリ内にアップロードされるファイルを監視できるようなライブラリがないか探したところ、参考サイト [3] で watchdog が紹介されていました。早速、紹介されていたサンプルを PySide6 の GUI アプリに組み込んで動作確認をしていたところ、ときどき Segmentation Fault でコアダンプを吐いてアプリが終了してしまいます。
コアダンプを解析して原因を探るのが正しいやり方なのでしょうが、イベント発生の相性が PySide6 (Qt6) のシグナル/スロットのやり方と合わないのかもしれないと勝手にあたりをつけました。QObject でラップして、むりくり Qt 風のシグナル/スロットのやり方にしたところ、コアダンプを吐くことなく動作するようになったので、単純なサンプルにまとめました。
下記の OS 環境で動作確認をしています。
![]() |
RHEL 9.4 | x86_64 |
Python | 3.12.1 | |
PySide6 | 6.7.1 | |
watchdog | 4.0.1 |
import os | |
import sys | |
from os.path import expanduser | |
from PySide6.QtCore import QObject, Signal | |
from PySide6.QtWidgets import ( | |
QApplication, | |
QMainWindow, | |
QPlainTextEdit, | |
) | |
from watchdog.events import FileSystemEventHandler | |
from watchdog.observers import Observer | |
class WatchDog(QObject, FileSystemEventHandler): | |
onAnyEvent = Signal(str) | |
def __init__(self): | |
super().__init__() | |
def on_any_event(self, e): | |
msg = '%s : %s : %s' % ( | |
str(e.is_directory), | |
e.event_type, | |
e.src_path | |
) | |
self.onAnyEvent.emit(msg) | |
class Example(QMainWindow): | |
def __init__(self): | |
super().__init__() | |
self.setWindowTitle('WatchDog test') | |
dir_target = os.path.join(expanduser("~"), 'tmp') | |
if not os.path.exists(dir_target): | |
os.mkdir(dir_target) | |
self.output = output = QPlainTextEdit() | |
output.setStyleSheet('font-family: monospace;') | |
output.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) | |
output.setReadOnly(True) | |
self.setCentralWidget(output) | |
dog = WatchDog() | |
dog.onAnyEvent.connect(self.update_output) | |
observer = Observer() | |
observer.schedule(dog, path=dir_target, recursive=True) | |
observer.start() | |
def update_output(self, msg: str): | |
self.output.insertPlainText(msg + '\n') | |
self.output.verticalScrollBar().setValue( | |
self.output.verticalScrollBar().maximum() | |
) | |
def main(): | |
app = QApplication(sys.argv) | |
ex = Example() | |
ex.show() | |
sys.exit(app.exec()) | |
if __name__ == '__main__': | |
main() |
このサンプルでは、ローカルアカウントに ~/tmp ディレクトリを作成します。このディレクトリ内を監視して、変化があった際にはシグナルを発生させ、参考サイト [3] で紹介されているサンプルの EventHandler が出力する文字列と同等の内容をスロットへ渡します。サンプルの GUI アプリは受け取った文字列を QPlainTextEdit のインスタンス上に表示します。
なお、本サンプルは GUI アプリなので、イベントループ内で動作しています。そのため、watchdog 用に監視ループを生成する必要はありません。
サンプルの実行例を以下に示します。
[bitwalk@rhel9 tmp]$ touch test.txt
[bitwalk@rhel9 tmp]$
実用的には FileSystemEventHandler クラスの on_any_event メソッドを使うと情報量が多すぎるかもしれません。例えば、新たにファイルが生成された時だけを監視したいのであれば on_created メソッドを利用するなどして、監視する情報を絞ることができます。API の詳細は、参考サイト [2] にあります。
ひょっとしたら Qt にもディレクトリを監視するクラスが用意されているかもしれないと調べてみたら QFileSystemWatcher というクラスがありました [4]。使い勝手が watchdog と違います。こちらのサンプルの紹介はまたの機会にします。
参考サイト
- gorakhargosh/watchdog: Python library and shell utilities to monitor filesystem events.
- Watchdog — watchdog documentation
- 【一番短い】Pythonでフォルダ監視するサンプル | 初心者DIYプログラミング入門 [2023-03-01]
- QFileSystemWatcher - Qt for Python

にほんブログ村
#オープンソース

0 件のコメント:
コメントを投稿