3

I have a Python script that does stuff based on D-Bus events, simplified version of that:

import dbus
from dbus.mainloop.glib import DBusGMainLoop
import gobject

DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()

# Initialize a main loop
mainloop = gobject.MainLoop()
bus.add_signal_receiver(cb_udisk_dev_add, signal_name='DeviceAdded', dbus_interface="org.freedesktop.UDisks")
bus.add_signal_receiver(cb_udisk_dev_rem, signal_name='DeviceRemoved', dbus_interface="org.freedesktop.UDisks")

mainloop.run()

This calls the cb_udisk_dev_add and -rem callback functions. Now I would like to have a timed callback function which I like to call, say every 5 minutes.

It seems that mainloop.run() is a blocked function, so I think I need to add a timer of sorts to the mainloop...?

I have tried implementing a few periodically executing functions from: Executing periodic actions in Python but they are all blocking too, soo the mainloop.run() doesn't get executed.

Any suggestions?

svenema
  • 1,766
  • 2
  • 23
  • 45
  • D-Bus Signals are intended to be an INTER-process protocol, not INTRA-process. Yes, that main loop is "blocking". If your goal is to set off signals at an interval, start the process you have listed above, and run *another* process that has the timers, or on a schedule to set off the signals to the bus your main.loop() process is listening too. (edited to be more technically correct re: DBus signaling) – DDeMartini Dec 11 '17 at 15:59
  • The D-Bus signals are INTER-process. They come from Udisks, and are handled in the mentioned script. What I like to add is a timed callback function that does stuff every now and then, independently of D-Bus. -- More (very) specifically, this is for add media player, I want to save the volume level to a file every 5 minutes (doing it per every volume change is too resource heavy, imagine turning the volume knob and the volume gets saved at every click..) – svenema Dec 11 '17 at 16:31
  • Sounds like you'd need to overload the main loop to inject some stepper or timer function, if you want this in one process. Or you run a tickler process (separately) to drop the signal, or fork a process with the timer that sends the signal, or of you're not trying to handle the single, perform some other operation. – DDeMartini Dec 11 '17 at 16:38

1 Answers1

6

You could use the glib's g_timeout_add_seconds function that registers a callback function to be executed in GMainloop's context. In python, this function is encapsulated in GObject, and you can try the below example code:

from gi.repository import GObject

def hello():
   print("Hello world!\n")
   return True

GObject.timeout_add_seconds(1, hello)
loop = GObject.MainLoop()
loop.run()
Houcheng
  • 2,674
  • 25
  • 32
  • 4
    When implementing this, I made the mistake of leaving out the `return True`. From the documentation: `The function is called repeatedly until it returns false, at which point the timeout is automatically destroyed and the function will not be called again.` – Seth Jan 22 '20 at 13:41
  • @Seth this was exactly my problem! thanks! I am adding this comment to maybe bring some more focus on your very important note. – Guenther Schmitz May 15 '20 at 09:48
  • GObject.timeout_add_seconds is deprecated; use GLib.timeout_add_seconds instead – MatthiasRoelandts Apr 05 '21 at 14:24