Search code examples
pythonfilesystemwatcher

How to pass additional arguments to the FileSystemEventHandler class to allow for user input?


I am creating a watchdog in python where the user is able to select a folder to monitor. In order to use the selected watch directory I had to pass the variable into both my event handler and the scheduler. My result is below. The program first ask for the direcotry and avoids using any global variables.

WATCHDOG.py

import time
import logging
import tkinter as tk
from tkinter import filedialog

from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler, FileSystemEventHandler


class OnMyWatch:

    def __init__(self, dir):
        self.observer = Observer()
        self.dir = dir 

    def run(self):
        event_handler = Handler()
        self.observer.schedule(event_handler, self.dir, recursive = True)
        self.observer.start()
        try:
            while True:
                time.sleep(5)
        except:
            self.observer.stop()
            print("Observer Stopped")

        self.observer.join()



class Handler(FileSystemEventHandler):
    def __init__(self, dir): 
        self.dir = dir


if __name__ == "__main__":
    root = tk.Tk()
    root.withdraw()

    dir = filedialog.askdirectory()

    watch = OnMyWatch(dir)
    watch.run()

Solution

  • The direct answer is that any function or method in a module can read its global variables without doing anything special. As long as a function does not use a variable name is its parameter list or assign a value to a variable, it is not in the function namespace and python will look elsewhere for it (perhaps in a class namespace or the module namespace).

    More to the point...

    Looking at the docs on_any_event is not a static method and I don't see any advantage to making it static in your code. FileSystemEventHandler is meant to be extended with the information you want for your event handling. Since you want the destination directory to be available in the handler, just create an initializer that puts it there.

    I've done a bit of rearranging to put your script level code inside the if __name__=="__main__": block and I've extended your classes to keep the data pulled in by the GUI. Now your obserser is decoupled from the GUI and whatever variables it happens to create. You could have more than one observer running in different threads and you could replace the GUI with something else (like a test script) if you'd like.

    You can add other data to the classes, this is just the minimum to get your print to work.

    import time
    import logging 
    import tkinter as tk
    from tkinter import filedialog
    from watchdog.observers import Observer 
    from watchdog.events import LoggingEventHandler, FileSystemEventHandler
    
    class OnMyWatch:
    
        def __init__(self, watch_dir, dest_dir):
            self.watch_dir = watch_dir
            self.dest_dir = dest_dir
            self.observer = Observer()
    
        def run(self):
            event_handler = Handler(self.dest_dir)
            self.observer.schedule(event_handler, self.watch_dir, recursive = True)
            self.observer.start()
            try:
                while True:
                    time.sleep(5)
            except:
                self.observer.stop()
                print("Observer Stopped")
    
            self.observer.join()
    
    class Handler(FileSystemEventHandler):
    
        def __init__(self, dest_dir): 
            self.dest_dir = dest_dir
    
        def on_any_event(self, event):
            print(self.dest_dir)
    
    if __name__ == "__main__": 
        root = tk.Tk()
        root.withdraw()
        WATCH_DIRECTORY = filedialog.askdirectory()
        WATCH_DESTINATION = filedialog.askdirectory()
        watch = OnMyWatch(WATCH_DIRECTORY, WATCH_DESTINATION)
        watch.run()
    

    NOTE: Its okay to use global variables like you did on small scripts. The code above is written with future uses in mind, but really its over-designed for a simple script. Some people think that you shouldn't put more into code than than required because predictions about future use are often wrong.