Viewing file: gireactor.py (5.98 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# 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']
|