gireactor.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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 typing import Union
  16. from gi.repository import GLib
  17. from twisted.internet import _glibbase
  18. from twisted.internet.error import ReactorAlreadyRunning
  19. from twisted.python import runtime
  20. if getattr(GLib, "threads_init", None) is not None:
  21. GLib.threads_init()
  22. class GIReactor(_glibbase.GlibReactorBase):
  23. """
  24. GObject-introspection event loop reactor.
  25. @ivar _gapplication: A C{Gio.Application} instance that was registered
  26. with C{registerGApplication}.
  27. """
  28. # By default no Application is registered:
  29. _gapplication = None
  30. def __init__(self, useGtk=False):
  31. _glibbase.GlibReactorBase.__init__(self, GLib, None)
  32. def registerGApplication(self, app):
  33. """
  34. Register a C{Gio.Application} or C{Gtk.Application}, whose main loop
  35. will be used instead of the default one.
  36. We will C{hold} the application so it doesn't exit on its own. In
  37. versions of C{python-gi} 3.2 and later, we exit the event loop using
  38. the C{app.quit} method which overrides any holds. Older versions are
  39. not supported.
  40. """
  41. if self._gapplication is not None:
  42. raise RuntimeError("Can't register more than one application instance.")
  43. if self._started:
  44. raise ReactorAlreadyRunning(
  45. "Can't register application after reactor was started."
  46. )
  47. if not hasattr(app, "quit"):
  48. raise RuntimeError(
  49. "Application registration is not supported in"
  50. " versions of PyGObject prior to 3.2."
  51. )
  52. self._gapplication = app
  53. def run():
  54. app.hold()
  55. app.run(None)
  56. self._run = run
  57. self._crash = app.quit
  58. class PortableGIReactor(_glibbase.GlibReactorBase):
  59. """
  60. Portable GObject Introspection event loop reactor.
  61. """
  62. def __init__(self, useGtk=False):
  63. super().__init__(GLib, None, useGtk=useGtk)
  64. def registerGApplication(self, app):
  65. """
  66. Register a C{Gio.Application} or C{Gtk.Application}, whose main loop
  67. will be used instead of the default one.
  68. """
  69. raise NotImplementedError("GApplication is not currently supported on Windows.")
  70. def simulate(self) -> None:
  71. """
  72. For compatibility only. Do nothing.
  73. """
  74. def install(useGtk: bool = False) -> Union[GIReactor, PortableGIReactor]:
  75. """
  76. Configure the twisted mainloop to be run inside the glib mainloop.
  77. @param useGtk: A hint that the Gtk GUI will or will not be used. Currently
  78. does not modify any behavior.
  79. """
  80. reactor: Union[GIReactor, PortableGIReactor]
  81. if runtime.platform.getType() == "posix":
  82. reactor = GIReactor(useGtk=useGtk)
  83. else:
  84. reactor = PortableGIReactor(useGtk=useGtk)
  85. from twisted.internet.main import installReactor
  86. installReactor(reactor)
  87. return reactor
  88. __all__ = ["install"]