python-3.x PyStray在主循环启动后停止工作(分离模式)

smdnsysy  于 2023-05-30  发布在  Python
关注(0)|答案(1)|浏览(234)

我正在做一个附带项目,它需要一个主进程,该进程产生一个HTTP服务器,该服务器不断侦听事件并在无限循环中处理它们。
这工作的预期,我现在试图添加支持的启动和停止的所有组件从一个图标在系统托盘。为此,我使用Pystray,它提供了一个run_detached方法,该方法应该允许图标循环在单独的线程上运行。
问题是,当我运行应用程序时,图标是正确创建的,但只要我单击图标菜单启动主进程,图标就无法使用了。未检测到右键单击,并且不可能进行进一步的交互。
一个类似于我的用例的小例子:

from PIL import Image, ImageDraw
import pystray
import time

class MyClass:
    def __init__(self) -> None:
        self.__running = False
        
    def run(self):
        while self.__running:
            time.sleep(1)
            print("running")
                
    def change_running_state(self):
        print(f"Running: {self.__running}")
        self.__running = not self.__running
        print(f"Running: {self.__running}")
        

if __name__ == "__main__":

    def create_image(width, height, color1, color2):
        image = Image.new('RGB', (width, height), color1)
        dc = ImageDraw.Draw(image)
        dc.rectangle(
            (width // 2, 0, width, height // 2),
            fill=color2)
        dc.rectangle(
            (0, height // 2, width // 2, height),
            fill=color2)
        return image

    def start():
        print("start")
        cl.change_running_state()
        cl.run()
        
    def stop():
        print("stop")
        cl.change_running_state()
        
    def exit_program():
        print("exit")
        import os
        cl.change_running_state()
        icon.stop()
        os._exit(1)
    

    icon = pystray.Icon(name = 'Doata2RuneRemainder', icon=create_image(64, 64, 'black', 'white'), menu=pystray.Menu(
        pystray.MenuItem("Start", start),
        pystray.MenuItem("Stop", stop),
        pystray.MenuItem("Exit", exit_program),
    ))
    cl = MyClass()
    icon.run_detached()
fslejnso

fslejnso1#

run_detached()方法旨在与单独的线程一起使用,以在主进程继续运行时运行系统托盘图标循环。但是,在当前代码中,您在脚本的末尾调用了icon.run_detached(),这实际上是在主线程上运行系统托盘图标循环,从而阻塞了代码其余部分的执行。
要解决此问题,您应该将系统托盘图标循环分离到其自己的线程中,以便它可以在主进程继续执行时独立运行。

from PIL import Image, ImageDraw
import pystray
import threading
import time
import os

class MyClass:
    def __init__(self):
        self.__running = False
        self.__stop_event = threading.Event()
        
    def run(self):
        while not self.__stop_event.is_set():
            time.sleep(1)
            print("running")
                
    def change_running_state(self):
        print(f"Running: {self.__running}")
        self.__running = not self.__running
        print(f"Running: {self.__running}")
        
        if self.__running:
            self.__stop_event.clear()
            t = threading.Thread(target=self.run)
            t.start()
        else:
            self.__stop_event.set()

if __name__ == "__main__":

    def create_image(width, height, color1, color2):
        image = Image.new('RGB', (width, height), color1)
        dc = ImageDraw.Draw(image)
        dc.rectangle(
            (width // 2, 0, width, height // 2),
            fill=color2)
        dc.rectangle(
            (0, height // 2, width // 2, height),
            fill=color2)
        return image

    def start(icon, item):
        print("start")
        cl.change_running_state()

    def stop(icon, item):
        print("stop")
        cl.change_running_state()

    def exit_program(icon, item):
        print("exit")
        cl.change_running_state()
        icon.stop()
        os._exit(1)

    cl = MyClass()
    icon = pystray.Icon(name='Doata2RuneRemainder', icon=create_image(64, 64, 'black', 'white'))
    icon.menu = (
        pystray.MenuItem("Start", start),
        pystray.MenuItem("Stop", stop),
        pystray.MenuItem("Exit", exit_program),
    )
    icon.run_detached()

相关问题