Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/343.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 多线程应用程序中的Gtk.StatusIcon冻结_Python_Python 3.x_Python Multithreading_Gtk3 - Fatal编程技术网

Python 多线程应用程序中的Gtk.StatusIcon冻结

Python 多线程应用程序中的Gtk.StatusIcon冻结,python,python-3.x,python-multithreading,gtk3,Python,Python 3.x,Python Multithreading,Gtk3,CentOs/RH上显示API下载状态的应用程序有问题。使用PyCharm IDE一切正常,但使用PyInstaller(一个文件夹)编译后,应用程序非常不稳定,我找不到错误。我每10秒运行一次线程并下载API状态,如果有更改,我会更新图标并发送通知。左键单击图标后,状态显示在Gtk.ApplicationWindow中 有时Gtk.StatusIcon无法更改状态或处于非活动状态-左/右单击不起作用(但状态更改通知会出现) 应用程序可能会意外结束 我怀疑问题出在线程中,但我找不到合适的

CentOs/RH上显示API下载状态的应用程序有问题。使用PyCharm IDE一切正常,但使用PyInstaller(一个文件夹)编译后,应用程序非常不稳定,我找不到错误。我每10秒运行一次线程并下载API状态,如果有更改,我会更新图标并发送通知。左键单击图标后,状态显示在
Gtk.ApplicationWindow

  • 有时
    Gtk.StatusIcon
    无法更改状态或处于非活动状态-左/右单击不起作用(但状态更改通知会出现)

  • 应用程序可能会意外结束

我怀疑问题出在线程中,但我找不到合适的解决方案

代码(main.py) TrayController(TrayController.py)
事实上,问题是线程,从某种意义上说,您不能使用来自多个线程的GTK API,甚至不能像以前那样使用GTK API

如果您有一个阻塞、同步操作,并且希望在操作结束时更新一些UI状态,那么应该使用;或者,如果在Python中使用thread对象,请始终使用在运行GTK主循环的同一线程中调用函数

你不应该做的是:

def do_启动(自):
Gdk.threads_init()
Gdk.threads_enter()
Gtk.Application.do_启动(自启动)
Gdk.threads_leave()
然后从运行GTK事件循环的线程以外的单独线程调用GTK API;这是完全未定义的、不可移植的行为

class ExampleSystemTrayInit(Gtk.StatusIcon):

def __init__(self):
    super().__init__()
    app.tray = Gtk.StatusIcon()
    app.tray.set_from_file(ico_start)
    app.tray.connect('popup-menu', self.on_right_click)
    app.tray.connect('activate', self.on_left_click, app)

def on_right_click(self, icon, event_button, event_time):
    self.menu = Gtk.Menu() 
    quit = Gtk.MenuItem("Quit")
    quit.connect('activate', self.quitApp)
    self.menu.append(quit)
    self.menu.show_all()
    self.menu.popup(None, None, Gtk.StatusIcon.position_menu, app.tray, event_button, event_time)

def on_left_click(self, icon, app):
    try:
        self.app = app
        if app.all_status:
            data = self.app.all_status
            Controller.show_window(self, data, self.app, refresh=False)
            #call to class MainWindow(Gtk.ApplicationWindow) and show some data
        else:
            pass;
    except Exception as e:
        print(e)
        app.tray.set_from_file(ico_disconnect)

def quitApp(self, par):
    app.quit()

class ExampleSystemTray(Gtk.Application):

def __init__(self, *args, **kwargs):
    super().__init__(
        application_id="example-system-tray2.app"
    )
    self.tray = None
    self.mainWindow = None


def do_activate(self):
    if not hasattr(self, "my_app"):
        self.hold()
        self.my_app_settings = "Primary application instance."
        self.systray = ExampleSystemTrayInit()
        TrayController(app)

    else:
        print("Already running!")

def do_startup(self):
    Gdk.threads_init()
    Gdk.threads_enter()
    Gtk.Application.do_startup(self)
    Gdk.threads_leave()


if __name__ == "__main__":
   GObject.threads_init()
   app = ExampleSystemTray()
   app.run()
class TrayController(threading.Thread):

def __init__(self, app):
    self.app = app
    self.cache = None
    threading.Thread.__init__(self, name='TrayController', )
    self.interval = 10
    self.finished = threading.Event()
    self.daemon = True
    self.start()

def run(self):
    while True:
        try:
            self.finished.wait(self.interval)
            if not self.finished.is_set():
                self.connect(self.app)
        except Exception as e:           
            print(e)

def cancel(self):
    """Stop the timer if it hasn't finished yet."""
    self.finished.set()

def connect(self, app):
    try:
        self.result = GetJson.get_json(self, api_view)
        if self.result == None:
            self.cache = None
            self.show_status(2)
            app.all_status = None
        elif (self.cache != self.result):
            self.cache = self.result
            app.all_status = AllStatusInstance(self.result)
            self.show_status(app.all_status.status)
            Controller.show_main_window(self, app.all_staus, app, refresh=True) 
    #call to class  MainWindow(Gtk.ApplicationWindow) if window is visble, then refresh 
             

    except Exception as e:
        print(e)
        self.show_status(2)
        app.all_status = None

def show_status(self, status):
    self.status = status
    if self.status == 0:
        return self.change_tray_icon(ico_red, notify_red, tag_red, widget=Gtk.StatusIcon)
    elif self.status == 1:
        return self.change_tray_icon(ico_gray, notify_gray, tag_gray, widget=Gtk.StatusIcon)
    elif self.status == 2:
        return self.change_tray_icon(ico_disconnect, notify_disconnect, tag_disconnect, 
               widget=Gtk.StatusIcon)

def change_tray_icon(self, icon, notification, tag, widget):
    if self.app.tray.get_title() != tag:
        self.app.tray.set_from_file(icon)
        self.app.tray.set_title(tag)
        self.notify("Notification:", notification, icon)

def notify(self, title, body, link):
    notify2.init("Alert", mainloop=None)
    icon = GdkPixbuf.Pixbuf.new_from_file(link)
    n = notify2.Notification(title, body)
    n.set_icon_from_pixbuf(icon)
    n.set_urgency(notify2.URGENCY_CRITICAL)
    n.show()


class AllStatusInstance(object):
__instance = None

def __new__(cls, val):
    if AllStatusInstance.__instance is None:
        AllStatusInstance.__instance = object.__new__(cls)
    AllStatusInstance.__instance.val = val
    return AllStatusInstance.__instance