[Python-Dev] Running GUI and "GObject.mainloop.run()" together?

Ajay Garg ajaygargnsit at gmail.com
Tue Dec 25 17:34:21 CET 2012


Thanks Simon.

Thanks for the extensive info; however it needs some hours (if not days :P)
to be digested.

On Tue, Dec 25, 2012 at 9:24 PM, Simon McVittie <
simon.mcvittie at collabora.co.uk> wrote:

> On 24/12/12 08:26, Ajay Garg wrote:
> > For a recap of the brief history, I have a parent process, that is
> > spawning  a child process via "subprocess".
> > Currently, the child-process is a GUI process; however, I intend to
> > "behave" it as a dbus-service as well.
>
> In general that is something that can work, but it's necessary to
> understand a bit about how main loops work, and how the modules of your
> process deal with a main loop.
>
> Just saying "GUI" is not very informative: there are dozens of GUI
> frameworks that you might be using, each with their own requirements and
> oddities. If you say Gtk, or Qt, or Tk, or Windows MFC, or whatever
> specific GUI framework you're using, then it becomes possible to say
> something concrete about your situation.
>
> Based on later mails in the thread you seem to be using Gtk.
>
> I should note here that you seem to be using PyGtk (the "traditional"
> Gtk 2 Python binding), which is deprecated. The modern version is to use
> PyGI, the Python GObject-Introspection binding, and Gtk 3.
>
> When using PyGI, you have a choice of two D-Bus implementations: either
> GDBus (part of gi.repository.GIO), or dbus-python ("import dbus"). I
> would recommend GDBus, since dbus-python is constrained by backwards
> compatibility with some flawed design decisions.
>
> However, assuming you're stuck with dbus-python:
>
> > I then used composition, wherein another  class, "RemoteListener"
> > deriving  from "dbus.service.Object" was made an attribute of the "main"
> > class. That worked.
> > However, when  I do
> >
> >                dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
> >                GObject.mainloop.run()
> >
> > in the "RemoteListener"'s __init__ method, the GUI of the "main" class
> > fails to load (apparently, because the "mainloop.run()" causes the
> > singular, main-thread to go into busy-wait).
>
> Almost; it's not a busy-wait. GObject.mainloop.run() is the equivalent
> of this pseudocode:
>
>     def run(self):
>         while not global_default_main_context.someone_has_called_quit:
>             if global_default_main_context.has_more_events():
>                 global_default_main_context.process_next_event()
>             else:
>                 global_default_main_context.wait_for_an_event()
>
> so it will loop until someone calls GObject.mainloop.quit() or
> equivalent, or forever if that never happens - but as long as nothing
> "interesting" happens, it will block on a poll() or select() syscall in
> what my pseudocode calls wait_for_an_event(), which is the right thing
> to do in event-driven programming like GLib/Gtk.
>
> (If you replace the last line of my pseudocode with "continue", that
> would be a busy-wait.)
>
> > I tried option b), but now instantiating "RemoteListener" in a separate
> > thread
>
> It is unclear whether the dbus-glib main loop glue (as set up by
> DBusGMainLoop) is thread-safe or not. The safest assumption is always
> "if you don't know whether foo is thread-safe, it probably isn't". In
> any case, if it *is* thread-safe, the subset of it that's exposed
> through dbus-python isn't enough to use it in multiple threads.
>
> GDBus, as made available via PyGI (specifically, gi.repository.GIO), is
> known to be thread-safe.
>
> > Is there a way to run GUI and a dbus-service together?
>
> The general answer: only if either the GUI and the D-Bus code
> run in different threads, or if they run in the same thread and can be
> made to share a main context.
>
> The specific answer for Gtk: yes, they can easily share a main context.
>
> This:
>
> > dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
>
> sets up dbus-python's mainloop integration to integrate with the global
> default main-context in GLib (implementation detail: it currently uses
> dbus-glib to do that). What that means is that whenever a D-Bus
> connection started by dbus-python wants to listen for events on a
> socket, or wait for a timeout, it will ask GLib to add those to the
> global default main context as event sources.
>
> This:
>
> > GObject.mainloop.run()
>
> iterates GLib's global default main context, analogous to the pseudocode
> I mentioned before. Any "interesting" events that happen will cause your
> code to be executed.
>
> A typical GUI application also needs to run the main loop to
> wait for events. In PyGtk, you'd typically do that with:
>
> > Gtk.main()
>
> Gtk also uses GLib's global default main context, so this is pretty
> similar to GObject.mainloop.run() - if you just remove the call to
> GObject.mainloop.run() and use Gtk.main() instead, everything should be
> fine.
>
> > As per http://www.pygtk.org/pygtk2reference/class-
> > gobjectmainloop.html, it seems that we must be able to add event
> > sources to gobject.mainloop
>
> Yes. For instance, gobject.timeout_add(), gobject.idle_add() and
> gobject.io_add_watch() all add event sources to the default main context.
>
> dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) tells dbus-python
> that when it needs to add an event source to "the" main loop, it should
> use equivalent C functions in GLib to do so.
>
> (In principle, DBusGMainLoop ought to take a GObject.MainContext as an
> optional argument - but that's never been implemented, and it currently
> always uses the default main context, which is the same one Gtk uses,
> and which should only be iterated from the main thread.)
>
> > Once the event sources are added, each instance of gobject.mainloop
> > (in its particular thread), will cater  to only those sources.
>
> No, that's not true; gobject.mainloop is a namespace for a set of global
> functions, not an object. If you must use multiple threads (not
> recommended), please see the GLib C API documentation for details of how
> main loops and main contexts relate, then the PyGtk documentation to see
> how that translates into Python.
>
> > How is dbus."mainloop.glib.DBusGMainLoop(set_as_default=True)"
> > related to gobject.mainloop?
>
> It instantiates a new DBusGMainLoop and sets it as dbus-python's global
> default main-loop-integration object. (With hindsight, DBusGMainLoop was
> a poor choice of name - it should have been DBusGMainIntegration or
> something.) The result is that whenever a new dbus.connection.Connection
> is instantiated, it will call methods on that DBusGMainLoop to connect
> its event sources up to the default GLib main context, which is the same
> one used by Gtk.
>
> dbus.bus.BusConnection, dbus.Bus, dbus.SessionBus etc. are
> dbus.connection.Connection subclasses, so anything I say about
> dbus.connection.Connection applies equally to them.
>
> > How is dbus."mainloop.glib.DBusGMainLoop(set_as_default=False)"
> > related to gobject.mainloop?
>
> It instantiates a new DBusGMainLoop and doesn't use it for anything. If
> you save the returned DBusGMainLoop in a variable (e.g.
> my_dbus_g_main_loop = DBusGMainLoop(...)), then you can pass a keyword
> argument mainloop=my_dbus_g_main_loop to a dbus.connection.Connection
> constructor, and that dbus.connection.Connection will use that
> DBusGMainLoop instead of dbus-python's global default. In practice, only
> a very unusual application would need to do that.
>
> There is currently no point in having more than one DBusGMainLoop; it
> would become useful if dbus-glib was thread-safe, and if dbus-python
> supported non-default GLib main-contexts.
>
> > Is it necessary at all to specify
> > "mainloop.glib.DBusGMainLoop(set_as_default=True)" or
> > "mainloop.glib.DBusGMainLoop(set_as_default=False)" when using
> > gobject.mainloop?
>
> Yes. Otherwise, dbus-python has no way to know that your application is
> going to be iterating the GLib main loop, as opposed to Qt or Tk or
> Enlightenment or something.
>
> > currently for the client, I am having the (client) (parent) process
> > run the command "dbus-send" via the python-subprocess  API.
> > Does there exist a python API to do it in a cleaner manner?
>
> Yes, either dbus-python or GDBus. Each of those can do everything
> dbus-send can, and more.
>

For a start, could you please point me to the paradigm to send a
dbus-signal from the client to the server (where the server has the
"add_to_signal_receiver" been set up).

>From the limited googling that I did, I remember someone saying that for
sending a signal, the typical setting-up-of-a-proxy-object is not required;
however, I could not hit upon the exact dbus-python mechanism to send a
signal :-\



>
>     S
> _______________________________________________
> dbus mailing list
> dbus at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dbus
>



-- 
Regards,
Ajay
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20121225/e5265263/attachment.html>


More information about the Python-Dev mailing list