123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
-
- """
- This module provides support for Twisted to interact with the glib
- mainloop via GObject Introspection.
-
- In order to use this support, simply do the following::
-
- from twisted.internet import gireactor
- gireactor.install()
-
- If you wish to use a GApplication, register it with the reactor::
-
- from twisted.internet import reactor
- reactor.registerGApplication(app)
-
- Then use twisted.internet APIs as usual.
-
- On Python 3, pygobject v3.4 or later is required.
- """
-
- from __future__ import division, absolute_import
-
- from twisted.python.compat import _PY3
- from twisted.internet.error import ReactorAlreadyRunning
- from twisted.internet import _glibbase
- from twisted.python import runtime
-
- if _PY3:
- # We require a sufficiently new version of pygobject, so always exists:
- _pygtkcompatPresent = True
- else:
- # We can't just try to import gi.pygtkcompat, because that would import
- # gi, and the goal here is to not import gi in cases where that would
- # cause segfault.
- from twisted.python.modules import theSystemPath
- _pygtkcompatPresent = True
- try:
- theSystemPath["gi.pygtkcompat"]
- except KeyError:
- _pygtkcompatPresent = False
-
-
- # Modules that we want to ensure aren't imported if we're on older versions of
- # GI:
- _PYGTK_MODULES = ['gobject', 'glib', 'gio', 'gtk']
-
- def _oldGiInit():
- """
- Make sure pygtk and gi aren't loaded at the same time, and import Glib if
- possible.
- """
- # We can't immediately prevent imports, because that confuses some buggy
- # code in gi:
- _glibbase.ensureNotImported(
- _PYGTK_MODULES,
- "Introspected and static glib/gtk bindings must not be mixed; can't "
- "import gireactor since pygtk2 module is already imported.")
-
- global GLib
- from gi.repository import GLib
- if getattr(GLib, "threads_init", None) is not None:
- GLib.threads_init()
-
- _glibbase.ensureNotImported([], "",
- preventImports=_PYGTK_MODULES)
-
-
- if not _pygtkcompatPresent:
- # Older versions of gi don't have compatibility layer, so just enforce no
- # imports of pygtk and gi at same time:
- _oldGiInit()
- else:
- # Newer version of gi, so we can try to initialize compatibility layer; if
- # real pygtk was already imported we'll get ImportError at this point
- # rather than segfault, so unconditional import is fine.
- import gi.pygtkcompat
- gi.pygtkcompat.enable()
- # At this point importing gobject will get you gi version, and importing
- # e.g. gtk will either fail in non-segfaulty way or use gi version if user
- # does gi.pygtkcompat.enable_gtk(). So, no need to prevent imports of
- # old school pygtk modules.
- from gi.repository import GLib
- if getattr(GLib, "threads_init", None) is not None:
- GLib.threads_init()
-
-
-
- class GIReactor(_glibbase.GlibReactorBase):
- """
- GObject-introspection event loop reactor.
-
- @ivar _gapplication: A C{Gio.Application} instance that was registered
- with C{registerGApplication}.
- """
- _POLL_DISCONNECTED = (GLib.IOCondition.HUP | GLib.IOCondition.ERR |
- GLib.IOCondition.NVAL)
- _POLL_IN = GLib.IOCondition.IN
- _POLL_OUT = GLib.IOCondition.OUT
-
- # glib's iochannel sources won't tell us about any events that we haven't
- # asked for, even if those events aren't sensible inputs to the poll()
- # call.
- INFLAGS = _POLL_IN | _POLL_DISCONNECTED
- OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED
-
- # By default no Application is registered:
- _gapplication = None
-
-
- def __init__(self, useGtk=False):
- _gtk = None
- if useGtk is True:
- from gi.repository import Gtk as _gtk
-
- _glibbase.GlibReactorBase.__init__(self, GLib, _gtk, useGtk=useGtk)
-
-
- def registerGApplication(self, app):
- """
- Register a C{Gio.Application} or C{Gtk.Application}, whose main loop
- will be used instead of the default one.
-
- We will C{hold} the application so it doesn't exit on its own. In
- versions of C{python-gi} 3.2 and later, we exit the event loop using
- the C{app.quit} method which overrides any holds. Older versions are
- not supported.
- """
- if self._gapplication is not None:
- raise RuntimeError(
- "Can't register more than one application instance.")
- if self._started:
- raise ReactorAlreadyRunning(
- "Can't register application after reactor was started.")
- if not hasattr(app, "quit"):
- raise RuntimeError("Application registration is not supported in"
- " versions of PyGObject prior to 3.2.")
- self._gapplication = app
- def run():
- app.hold()
- app.run(None)
- self._run = run
-
- self._crash = app.quit
-
-
-
- class PortableGIReactor(_glibbase.PortableGlibReactorBase):
- """
- Portable GObject Introspection event loop reactor.
- """
- def __init__(self, useGtk=False):
- _gtk = None
- if useGtk is True:
- from gi.repository import Gtk as _gtk
-
- _glibbase.PortableGlibReactorBase.__init__(self, GLib, _gtk,
- useGtk=useGtk)
-
-
- def registerGApplication(self, app):
- """
- Register a C{Gio.Application} or C{Gtk.Application}, whose main loop
- will be used instead of the default one.
- """
- raise NotImplementedError("GApplication is not currently supported on Windows.")
-
-
-
- def install(useGtk=False):
- """
- Configure the twisted mainloop to be run inside the glib mainloop.
-
- @param useGtk: should GTK+ rather than glib event loop be
- used (this will be slightly slower but does support GUI).
- """
- if runtime.platform.getType() == 'posix':
- reactor = GIReactor(useGtk=useGtk)
- else:
- reactor = PortableGIReactor(useGtk=useGtk)
-
- from twisted.internet.main import installReactor
- installReactor(reactor)
- return reactor
-
-
- __all__ = ['install']
|