Search code examples
pythonmultithreadingpython-2.7dbusgobject

How to stop a dbus gobject loop


I try to stop a gobject.MainLoop() after a few seconds.
I don't know if it's possible to set a timeout to this kind of loop, it would be perfect but I have not found that.

So, I tried to workaround this with threading but unfortunately, the main loop block others threads.
Here my code (I'm working with python 2.7):

import MediaCenter_dbusConfig
import dbus
import gobject
from dbus.mainloop.glib import DBusGMainLoop
from time import sleep
from threading import Thread


mainloop=0

class Timeout(Thread):
    global mainloop

    def __init__(self):

        Thread.__init__(self)

    def run(self):
        global mainloop

        i = 0
        while i < 30:
            sleep(1)
            i += 1
        mainloop.quit()

class Loop(Thread):
    global mainloop

    def __init__(self):

        Thread.__init__(self)

    def run(self):
        global mainloop

        sleep(5)
        mainloop.run()

def catchall_detectedDevicePopUp_from_willShowPopup_signals_handler(popup_string):
    global mainloop

    if(popup_string == "DetectedDevicePopUp.qml") :
        print(popup_string)
        mainloop.quit()

def detectedDevicePopUp_detector() :
    global mainloop

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus=MediaCenter_dbusConfig.init() # basicly do a dbus.bus.BusConnection()

    bus.add_signal_receiver(catchall_detectedDevicePopUp_from_willShowPopup_signals_handler, dbus_interface = "com.orange.mediacenter.apimanager", signal_name = "willShowPopup")

    mainloop = gobject.MainLoop()

    thread1 = Timeout()
    thread2 = Loop()
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()

Here I call detectedDevicePopUp_detector(). I'm waiting for a signal named willShowPopup. If I received a signal, I want to stop the loop and continue my program, and after 30s, if I have not received any signal, I want the same thing (stop the loop and continue my program) but here it doesn't work, my Timeout thread is blocked by my Loop thread.
Clarification: I can not edit the signals sent (I test an application).

Any ideas ?


Solution

  • As I understood the question, threading is not really wanted. Below is an example that uses gobject.timeout_add to add a maximum time that the mainloop will run if there is no signal to stop it:

    import gobject
    import dbus
    import dbus.service
    
    from dbus.mainloop.glib import DBusGMainLoop
    DBusGMainLoop(set_as_default=True)
    
    
    OPATH = "/com/example/StopLoop"
    IFACE = "com.example.StopLoop"
    BUS_NAME = "com.example.StopLoop"
    TIMEOUT = 30 * 1000
    
    
    class Example(dbus.service.Object):
        def __init__(self, loop):
            self.loop = loop
            bus = dbus.SessionBus()
            bus.request_name(BUS_NAME)
            bus_name = dbus.service.BusName(BUS_NAME, bus=bus)
            dbus.service.Object.__init__(self, bus_name, OPATH)
            # Add a timeout for how long to run mainloop
            # if no signal is received
            self.setup_timeout(TIMEOUT)
            # Listen to the "Stop" signal
            self.listen_for_signal(bus)
    
        def setup_timeout(self, timeout):
            gobject.timeout_add(timeout, self.handler)
    
        def listen_for_signal(self, bus):
            bus.add_signal_receiver(self.handler, "Stop")
    
        def handler(self):
            # This handler is used for both timeout and signal
            self.loop.quit()
    
    
    if __name__ == "__main__":
        loop = gobject.MainLoop()
        a = Example(loop)
        loop.run()
        print "Exited mainloop, continuing program..."
    

    If the Stop signal is received e.g. by doing:

    dbus-send --session --type=signal --dest=com.example.StopLoop /com/example/StopLoop com.example.StopLoop.Stop
    

    The mainloop will exit and the code will continue from where loop.run() was called.

    If no signal is received the mainloop will be stopped by the timeout (30 seconds in this case) and continue from where loop.run() was called.