【问题标题】:Python - add multiple files to folder but run event only oncePython - 将多个文件添加到文件夹但只运行一次事件
【发布时间】:2021-04-19 11:25:03
【问题描述】:

我正在尝试制作一个看门狗来监听文件夹更改(添加/删除)文件。

我的问题是,每次我从这个文件夹(及其子文件夹)复制创建/删除 几个文件 时,事件链都会为每个文件一个接一个地启动。

在创建/删除多个文件后,如何使on_event() 方法仅被调用一次?

假设我将两张图片复制到此文件夹中。

我希望事件处理程序在文件传输完成后仅被调用一次,并且两次 - 每个图像一次 - 因为它目前有效。

谢谢!

代码在带有 python 3.7 的树莓派 3 上运行。

代码如下:

    import os
    import time
    import psutil
    from watchdog.observers import Observer
    from watchdog.events import FileSystemEventHandler
    
    i = 0
    
    
def show_stats():
    global i
    read = "read #" + str(i) + ":"
    mem = "\nmemory in use: " + str(psutil.virtual_memory().percent)+"%"
    cpu = "\ncpu load: " + str(psutil.cpu_percent())+"%"
    temp = "\ncurrent " + \
        os.popen("vcgencmd measure_temp").readline().replace(
            "=", ": ").replace("'C", " C°")
    end = "\n=================="
    i += 1
    stats = read + mem + cpu + temp + end
    return stats
    
    
    class Watcher:
        DIRECTORY_TO_WATCH = r'/home/pi/Desktop/jsSlider/images'
    
        def __init__(self):
            self.observer = Observer()
            print("watching ", self.DIRECTORY_TO_WATCH, "...")
    
        def run(self):
            event_handler = Handler()
            self.observer.schedule(
                event_handler, self.DIRECTORY_TO_WATCH, recursive=True)
            self.observer.start()
            try:
                while True:
                    time.sleep(5)
                    print(show_stats())
            except Exception as e:
                self.observer.stop()
                print(e)
            self.observer.join()
    
    
    class Handler(FileSystemEventHandler):
        @staticmethod
        def on_event(event):
            wait = 1
            elif event.event_type == 'created' or event.event_type == 'deleted':
                print("Received event - %s. " %event.src_path, str(event.event_type))
                time.sleep(wait) #i found that its best to give some timeout between commands because it overwhelmed the pi for some reason (one second seems to be enough)...
                os.system('python /home/pi/Desktop/Slider/scripts/arr_edit.py') #recreate the JS array
                time.sleep(wait)
                os.system('cp -r /home/pi/Desktop/jsSlider/scripts/imgArr.js /home/pi/Desktop/jsSlider/themes/1') #copy the newly created JS array to its place
                time.sleep(wait)
                os.system('sudo pkill chromium') #"refresh" the page -the kiosk mode reactivates the process...
                # os.system('cls')
                print('done!')
    
    
    if __name__ == '__main__':
        w = Watcher()
        w.run()

编辑我

在某个诊所,连接到电视的 rpi3 很差,在 kiosk 模式下工作以显示来自本地 html 文件的图像(使用一些 js 代码 - 幻灯片使用现有的 JS 脚本运行 - 如果需要,我可以上传所有内容| 图片也在 pi 上)。

我想要实现的是自动:

  1. 重新构建 JS 数组(使用有效的 python 脚本 - 下面的代码 (arr_edit.py))。
  2. 将新阵列复制到所需位置。 (shell 命令)
  3. 并使用“pkill chromium”重新启动 chromium。 (shell 命令)

现在,我不能允许每次有人复制/删除 多个 图像时,命令每次都会运行 - 这意味着:

每当添加 2 张以上的图像时,我都无法“重新启动”自助服务终端 (sudo pkill chromium) 每次创建文件时。

每次您复制多个文件(在这种情况下为图像)时,对于在文件夹中创建的每个图像,都会调用一个完全独立的 event.created,因此对于 5 个图像,将是 5 个不同的 event.created 事件,每个事件都会触发 on_event() 方法,从而使信息亭连续重启 5 次。 (现在想想如果发生 50 个文件传输会发生什么 - pi 会崩溃)

因此,我需要一种方法在文件传输完成后仅调用命令 1 次,不管文件夹中有多少文件已更改/创建/删除。

arr_edit.py(不完全是我的代码):

import os
dir_path = r'/home/pi/Desktop/jsSlider/images'
file_path = r'/home/pi/Desktop/jsSlider/scripts/imgArr.js'

directory = os.fsencode(dir_path)
arr_name = 'images=[\n'
start_str = '{"img":"./images/'
end_str = '"},\n'
images = ''


def writer(array, imagesList):
    str_to_write = array + imagesList + ']'
    f = open(file_path, 'w')
    f.write(str_to_write)
    f.close


file_list = os.listdir(directory)
for file in file_list:
    filename = os.fsdecode(file)
    if filename.endswith(".jpg") or filename.endswith(".jpeg") or filename.endswith(".webp") or filename.endswith(".webp"):
        if file == file_list[len(file_list)-1]:
            end_str = '"}\n'
        images += start_str + filename + end_str
        continue
    else:
        continue

writer(arr_name, images)

输出 JS 数组(来自 imgArr.js 的示例):

images=[
{"img":"./images/246.jpg"},
{"img":"./images/128.jpg"},
{"img":"./images/238.webp"},
{"img":"./images/198.jpg"},
{"img":"./images/247.webp"}
]

【问题讨论】:

  • 我没有查看代码,但一种解决方案是在每次目录内容更改时启动计时器。我的内容在x 的时间内没有改变,然后执行你的函数。
  • 据我了解,这会产生另一个问题:每次计时器结束时,无论文件夹是否更改,都会执行该函数。如果我没有正确理解这个想法,请纠正我。谢谢!
  • 只有在观察者检测到变化时才应该调用计时器。我没有使用watchdog 包,但如果仍然需要,我会在今晚晚些时候深入研究并提供编码解决方案。
  • 我会尝试实施一次仍然希望看到另一个解决方案
  • 退后一步……简单地说,您实际上试图通过这种机制实现什么?可能有更简单、更可靠的方法。

标签: python events raspberry-pi event-handling python-watchdog


【解决方案1】:

正如马克在 cmets 中建议的那样,
我添加了一个检查,看看 js 文件在过去 5 分钟内是否发生了变化。
如果文件更改,
再等待 5 分钟,然后重新启动 cange(如果已将更多文件添加到文件夹中),以便在此运行中也显示新的、更大的文件。
像魅力一样工作!
非常感谢!!
这是最后的 watchdog.py

import os
import time
import psutil
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

i = 0


def show_stats():
    global i
    read = "read #" + str(i) + ":"
    mem = "\nmemory in use: " + str(psutil.virtual_memory().percent)+"%"
    cpu = "\ncpu load: " + str(psutil.cpu_percent())+"%"
    temp = "\ncurrent " + \
        os.popen("vcgencmd measure_temp").readline().replace(
            "=", ": ").replace("'C", " C°")
    end = "\n=================="
    i += 1
    stats = read + mem + cpu + temp + end
    return stats


def wait_for_file(file):
    time.sleep(300)
    if age(file) >= 5:
        modify()


def modify():
    os.system('python /home/pi/Desktop/jsSlider/scripts/arr_edit.py')
    os.system(
        'cp -r /home/pi/Desktop/jsSlider/scripts/imgArr.js /home/pi/Desktop/jsSlider/themes/1')
    time.sleep(1)
    os.system('sudo pkill chromium')
    # os.system('cls')
    print("done!\nwatching...")


def age(filename):
    return ((time.time() - os.path.getmtime(filename))//60)


class Watcher:
    DIRECTORY_TO_WATCH = r'/home/pi/Desktop/jsSlider/images'

    def __init__(self):
        self.observer = Observer()
        print("watching ", self.DIRECTORY_TO_WATCH, "...")

    def run(self):
        event_handler = Handler()
        self.observer.schedule(
            event_handler, self.DIRECTORY_TO_WATCH, recursive=True)
        self.observer.start()
        try:
            while True:
                time.sleep(5)
                print(show_stats())
        except Exception as e:
            self.observer.stop()
            print(e)
        self.observer.join()


class Handler(FileSystemEventHandler):
    @ staticmethod
    def on_any_event(event):
        file = r'/home/pi/Desktop/jsSlider/scripts/imgArr.js'
        if event.event_type == 'created' or event.event_type == 'deleted':
            print("Received event - %s. " %
                  event.src_path, str(event.event_type))
            time.sleep(5)
            if age(file) < 5:
                wait_for_file(file)
            else:
                modify()


if __name__ == '__main__':
    w = Watcher()
    w.run() 

【讨论】:

  • 干得好。您也可以接受自己的答案。
猜你喜欢
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-03
  • 2018-09-08
  • 2017-06-17
  • 1970-01-01
  • 2010-10-02
相关资源
最近更新 更多