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

Ajay Garg ajaygargnsit at gmail.com
Tue Dec 25 18:05:31 CET 2012


Also, I think I  am now starting to get a hang of things; however, one
doubt solved raises another doubt :D

The reason I began looking out for the two-threads-approach, is because
when trying to use the GUI (Gtk) application as a dbus-service, I was
getting the error "This connection was not provided by any of .service
files".
I now see that the reason of it was :: I wasn't using
"dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)" prior to the
statement "Gtk.main()".
Now, by your helpful guidance, wherein you stated that "Gtk.main()" has the
same effect as "GObject.MainLoop.run()", I added the statement
"dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)" before
"Gtk.main()", and voila.. now I could get a proxy object, and invoke
methods remotely. Thus, two threads are now not needed.


However, it now raises a new doubt : in my second approach, wherein I used
"add_to_signal_receiver" (at the server side), and dbus-send (at the client
side), how come things worked now, since I did not add
"dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)" in this approach at
all? I presume that even in this case, dbus would need to know that the GUI
application is ok to listen to dbus-signals?

Are the different requirements in these two approaches expected? Or is it
an inconsistency with dbus-python?


On Tue, Dec 25, 2012 at 10:04 PM, Ajay Garg <ajaygargnsit at gmail.com> wrote:

> 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
>



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


More information about the Python-Dev mailing list