python-3.xpython-multithreadingpygtk

show popup window from a thread pygtk make GUI freeze


I'm developing a graphical interface with pygtk 3, In parallel with this interface, I'm running a function in a 2nd thread, which reads a file that is updated by an external program.

When the value in this file exceeds a certain value, I'd like to display a popup window to warn the user that this value has been reached.

The problem is that when the popup window is displayed, the program freeze.

After looking at several similar questions, I've understood that you can't launch another gtk window in a second thread, but no matter how hard I look, I can't figure out how to do it...maybe by emitting a signal from the thread that would be picked up by gtk main loop? but I haven't figured out how to do it properly.

here is a simplified code(i didn't put all the code of the GUI it's to big and no relevent for this issue), so just a main gtk window, a popup window and the function reading the file.


import gi

gi.require_version("Gtk", "3.0")

from gi.repository import Gtk
from threading import Thread
import time
    
    
#The Popup window classe    
class DeepMonitorWindow(Gtk.Window):
    def __init__(self):
        super().__init__(title="Sounder")
        
        self.set_border_width(10)
        label=Gtk.Label(label="som popup message")
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
        hbox.pack_start(label,True,True,6)
        button=Gtk.Button.new_with_label("OK")
        button.connect("clicked",self.on_click)
        hbox.pack_start(button,True,True,6)
        self.add(hbox)
    def on_click(self,event):
        self.destroy() 

#the function that run ine the thread
def DeepMonitor():
    
    flagUpDown=True
    while(1):
        
        with open("seasave_shared.txt","r") as f:
            depth=f.readlines()[-1]
            print(float(depth.lstrip().strip("\n")))
            if float(depth.lstrip().strip("\n")) > 150 and flagUpDown==True :
                print("deep reached 150m")
                window=DeepMonitorWindow()
                window.show_all()
                flagUpDown=False
            if float(depth.lstrip().strip("\n")) < 150 and flagUpDown==False:
                print("deep reached 150m")
                window=DeepMonitorWindow()
                window.show_all()
                flagUpDown=True
                break
        time.sleep(1)     

#the main window        
class StationSheet():
    
    
    def __init__(self):
        
             
        
        self.window=Gtk.Window() 
        self.window.show_all()
        self.window.connect("destroy", Gtk.main_quit)
        
        #the thread
        t=Thread(target=DeepMonitor)
        t.start()
    
def main():
    
    app = StationSheet()
    Gtk.main()
 


if __name__ == "__main__":
    main()

Solution

  • I finally found a way to make it work! I got my inspiration from the following link: https://pygobject.readthedocs.io/en/latest/guide/threading.html

    use lang-python GLib.idle_add(function) to display the window and use lang-python thread.deamon() before launching the thread.

    I've also created a function to display the popup window.

    here's the working code

    import gi
    
    gi.require_version("Gtk", "3.0")
    
    from gi.repository import Gtk
    from threading import Thread
    import time
        
        
    #The Popup window classe    
    class DeepMonitorWindow(Gtk.Window):
        def __init__(self):
            super().__init__(title="Sounder")
            
            self.set_border_width(10)
            label=Gtk.Label(label="som popup message")
            hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
            hbox.pack_start(label,True,True,6)
            button=Gtk.Button.new_with_label("OK")
            button.connect("clicked",self.on_click)
            hbox.pack_start(button,True,True,6)
            self.add(hbox)
        def on_click(self,event):
            self.destroy() 
    
        
    
    #the main window        
    class StationSheet():
        
        
        def __init__(self):
            
                 
            
            self.window=Gtk.Window() 
            self.window.show_all()
            self.window.connect("destroy", Gtk.main_quit)
            
            #the thread
            t=Thread(target=self.DeepMonitor)
            t.daemon=True
            t.start()
            
        #the function that run ine the thread
        def show_popup(self):
            p=DeepMonitorWindow()
            p.set_transient_for(self.window)
            p.show_all()
        def DeepMonitor():
            
            flagUpDown=True
            while(1):
                
                with open("seasave_shared.txt","r") as f:
                    depth=f.readlines()[-1]
                    print(float(depth.lstrip().strip("\n")))
                    if float(depth.lstrip().strip("\n")) > 150 and flagUpDown==True :
                        print("deep reached 150m")
                        window=DeepMonitorWindow()
                        window.show_all()
                        flagUpDown=False
                    if float(depth.lstrip().strip("\n")) < 150 and flagUpDown==False:
                        print("deep reached 150m")
                        window=DeepMonitorWindow()
                        window.show_all()
                        flagUpDown=True
                        break
                time.sleep(1) 
        
    def main():
        
        app = StationSheet()
        Gtk.main()