You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gireactor.py 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. This module provides support for Twisted to interact with the glib
  5. mainloop via GObject Introspection.
  6. In order to use this support, simply do the following::
  7. from twisted.internet import gireactor
  8. gireactor.install()
  9. If you wish to use a GApplication, register it with the reactor::
  10. from twisted.internet import reactor
  11. reactor.registerGApplication(app)
  12. Then use twisted.internet APIs as usual.
  13. On Python 3, pygobject v3.4 or later is required.
  14. """
  15. from __future__ import division, absolute_import
  16. from twisted.python.compat import _PY3
  17. from twisted.internet.error import ReactorAlreadyRunning
  18. from twisted.internet import _glibbase
  19. from twisted.python import runtime
  20. if _PY3:
  21. # We require a sufficiently new version of pygobject, so always exists:
  22. _pygtkcompatPresent = True
  23. else:
  24. # We can't just try to import gi.pygtkcompat, because that would import
  25. # gi, and the goal here is to not import gi in cases where that would
  26. # cause segfault.
  27. from twisted.python.modules import theSystemPath
  28. _pygtkcompatPresent = True
  29. try:
  30. theSystemPath["gi.pygtkcompat"]
  31. except KeyError:
  32. _pygtkcompatPresent = False
  33. # Modules that we want to ensure aren't imported if we're on older versions of
  34. # GI:
  35. _PYGTK_MODULES = ['gobject', 'glib', 'gio', 'gtk']
  36. def _oldGiInit():
  37. """
  38. Make sure pygtk and gi aren't loaded at the same time, and import Glib if
  39. possible.
  40. """
  41. # We can't immediately prevent imports, because that confuses some buggy
  42. # code in gi:
  43. _glibbase.ensureNotImported(
  44. _PYGTK_MODULES,
  45. "Introspected and static glib/gtk bindings must not be mixed; can't "
  46. "import gireactor since pygtk2 module is already imported.")
  47. global GLib
  48. from gi.repository import GLib
  49. if getattr(GLib, "threads_init", None) is not None:
  50. GLib.threads_init()
  51. _glibbase.ensureNotImported([], "",
  52. preventImports=_PYGTK_MODULES)
  53. if not _pygtkcompatPresent:
  54. # Older versions of gi don't have compatibility layer, so just enforce no
  55. # imports of pygtk and gi at same time:
  56. _oldGiInit()
  57. else:
  58. # Newer version of gi, so we can try to initialize compatibility layer; if
  59. # real pygtk was already imported we'll get ImportError at this point
  60. # rather than segfault, so unconditional import is fine.
  61. import gi.pygtkcompat
  62. gi.pygtkcompat.enable()
  63. # At this point importing gobject will get you gi version, and importing
  64. # e.g. gtk will either fail in non-segfaulty way or use gi version if user
  65. # does gi.pygtkcompat.enable_gtk(). So, no need to prevent imports of
  66. # old school pygtk modules.
  67. from gi.repository import GLib
  68. if getattr(GLib, "threads_init", None) is not None:
  69. GLib.threads_init()
  70. class GIReactor(_glibbase.GlibReactorBase):
  71. """
  72. GObject-introspection event loop reactor.
  73. @ivar _gapplication: A C{Gio.Application} instance that was registered
  74. with C{registerGApplication}.
  75. """
  76. _POLL_DISCONNECTED = (GLib.IOCondition.HUP | GLib.IOCondition.ERR |
  77. GLib.IOCondition.NVAL)
  78. _POLL_IN = GLib.IOCondition.IN
  79. _POLL_OUT = GLib.IOCondition.OUT
  80. # glib's iochannel sources won't tell us about any events that we haven't
  81. # asked for, even if those events aren't sensible inputs to the poll()
  82. # call.
  83. INFLAGS = _POLL_IN | _POLL_DISCONNECTED
  84. OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED
  85. # By default no Application is registered:
  86. _gapplication = None
  87. def __init__(self, useGtk=False):
  88. _gtk = None
  89. if useGtk is True:
  90. from gi.repository import Gtk as _gtk
  91. _glibbase.GlibReactorBase.__init__(self, GLib, _gtk, useGtk=useGtk)
  92. def registerGApplication(self, app):
  93. """
  94. Register a C{Gio.Application} or C{Gtk.Application}, whose main loop
  95. will be used instead of the default one.
  96. We will C{hold} the application so it doesn't exit on its own. In
  97. versions of C{python-gi} 3.2 and later, we exit the event loop using
  98. the C{app.quit} method which overrides any holds. Older versions are
  99. not supported.
  100. """
  101. if self._gapplication is not None:
  102. raise RuntimeError(
  103. "Can't register more than one application instance.")
  104. if self._started:
  105. raise ReactorAlreadyRunning(
  106. "Can't register application after reactor was started.")
  107. if not hasattr(app, "quit"):
  108. raise RuntimeError("Application registration is not supported in"
  109. " versions of PyGObject prior to 3.2.")
  110. self._gapplication = app
  111. def run():
  112. app.hold()
  113. app.run(None)
  114. self._run = run
  115. self._crash = app.quit
  116. class PortableGIReactor(_glibbase.PortableGlibReactorBase):
  117. """
  118. Portable GObject Introspection event loop reactor.
  119. """
  120. def __init__(self, useGtk=False):
  121. _gtk = None
  122. if useGtk is True:
  123. from gi.repository import Gtk as _gtk
  124. _glibbase.PortableGlibReactorBase.__init__(self, GLib, _gtk,
  125. useGtk=useGtk)
  126. def registerGApplication(self, app):
  127. """
  128. Register a C{Gio.Application} or C{Gtk.Application}, whose main loop
  129. will be used instead of the default one.
  130. """
  131. raise NotImplementedError("GApplication is not currently supported on Windows.")
  132. def install(useGtk=False):
  133. """
  134. Configure the twisted mainloop to be run inside the glib mainloop.
  135. @param useGtk: should GTK+ rather than glib event loop be
  136. used (this will be slightly slower but does support GUI).
  137. """
  138. if runtime.platform.getType() == 'posix':
  139. reactor = GIReactor(useGtk=useGtk)
  140. else:
  141. reactor = PortableGIReactor(useGtk=useGtk)
  142. from twisted.internet.main import installReactor
  143. installReactor(reactor)
  144. return reactor
  145. __all__ = ['install']