Ticket #4558: gi-support.2.patch
| File gi-support.2.patch, 31.4 KB (added by , 6 years ago) |
|---|
-
twisted/internet/_glibbase.py
=== added file 'twisted/internet/_glibbase.py'
1 # -*- test-case-name: twisted.internet.test -*- 2 # Copyright (c) Twisted Matrix Laboratories. 3 # Copyright (c) 2011-2012 Canonical Ltd. 4 # See LICENSE for details. 5 """ 6 This module provides base support for Twisted to interact with the glib/gtk 7 mainloops. 8 9 The classes in this module should not be used directly, but rather you should 10 import gireactor or gtk3reactor for GObject Introspection based applications, 11 or glib2reactor or gtk2reactor for applications using legacy static bindings. 12 """ 13 14 import signal 15 16 from twisted.internet import base, posixbase, selectreactor 17 from twisted.internet.interfaces import IReactorFDSet 18 from twisted.python import log, runtime 19 from twisted.python.compat import set 20 from zope.interface import implements 21 22 class GlibSignalMixin(object): 23 24 if runtime.platformType == 'posix': 25 26 def _handleSignals(self): 27 # Let the base class do its thing, but pygtk is probably 28 # going to stomp on us so go beyond that and set up some 29 # signal handling which pygtk won't mess with. This would 30 # be better done by letting this reactor select a 31 # different implementation of installHandler for 32 # _SIGCHLDWaker to use. Then, at least, we could fall 33 # back to our extension module. See #4286. 34 from twisted.internet.process import ( 35 reapAllProcesses as _reapAllProcesses) 36 base._SignalReactorMixin._handleSignals(self) 37 signal.signal(signal.SIGCHLD, 38 lambda *a: self.callFromThread(_reapAllProcesses)) 39 if getattr(signal, "siginterrupt", None) is not None: 40 signal.siginterrupt(signal.SIGCHLD, False) 41 # Like the base, reap processes now in case a process 42 # exited before the handlers above were installed. 43 _reapAllProcesses() 44 45 46 class GlibWaker(posixbase._UnixWaker): 47 """ 48 Run scheduled events after waking up. 49 """ 50 51 def doRead(self): 52 posixbase._UnixWaker.doRead(self) 53 self.reactor._simulate() 54 55 56 class GlibReactorBase(GlibSignalMixin, 57 posixbase.PosixReactorBase, posixbase._PollLikeMixin): 58 """ 59 GObject event loop reactor. 60 61 @ivar _sources: A dictionary mapping L{FileDescriptor} instances to 62 GSource handles. 63 64 @ivar _reads: A set of L{FileDescriptor} instances currently monitored for 65 reading. 66 67 @ivar _writes: A set of L{FileDescriptor} instances currently monitored for 68 writing. 69 70 @ivar _simtag: A GSource handle for the next L{simulate} call. 71 """ 72 implements(IReactorFDSet) 73 74 # Install a waker that knows it needs to call C{_simulate} in order to run 75 # callbacks queued from a thread: 76 _wakerFactory = GlibWaker 77 78 def __init__(self): 79 self._simtag = None 80 self._reads = set() 81 self._writes = set() 82 self._sources = {} 83 posixbase.PosixReactorBase.__init__(self) 84 85 def input_add(self, source, condition, callback): 86 """This is a stub to be implemented by inheriting classes.""" 87 raise NotImplementedError() 88 89 def _ioEventCallback(self, source, condition): 90 """ 91 Called by event loop when an I/O event occurs. 92 """ 93 log.callWithLogger( 94 source, self._doReadOrWrite, source, source, condition) 95 return True # True = don't auto-remove the source 96 97 def _add(self, source, primary, other, primaryFlag, otherFlag): 98 """ 99 Add the given L{FileDescriptor} for monitoring either for reading or 100 writing. If the file is already monitored for the other operation, we 101 delete the previous registration and re-register it for both reading 102 and writing. 103 """ 104 if source in primary: 105 return 106 flags = primaryFlag 107 if source in other: 108 self._source_remove(self._sources[source]) 109 flags |= otherFlag 110 self._sources[source] = self.input_add( 111 source, flags, self._ioEventCallback) 112 primary.add(source) 113 114 def addReader(self, reader): 115 """ 116 Add a L{FileDescriptor} for monitoring of data available to read. 117 """ 118 self._add(reader, self._reads, self._writes, 119 self.INFLAGS, self.OUTFLAGS) 120 121 def addWriter(self, writer): 122 """ 123 Add a L{FileDescriptor} for monitoring ability to write data. 124 """ 125 self._add(writer, self._writes, self._reads, 126 self.OUTFLAGS, self.INFLAGS) 127 128 def getReaders(self): 129 """ 130 Retrieve the list of current L{FileDescriptor} monitored for reading. 131 """ 132 return list(self._reads) 133 134 def getWriters(self): 135 """ 136 Retrieve the list of current L{FileDescriptor} monitored for writing. 137 """ 138 return list(self._writes) 139 140 def removeAll(self): 141 """ 142 Remove monitoring for all registered L{FileDescriptor}s. 143 """ 144 return self._removeAll(self._reads, self._writes) 145 146 def _remove(self, source, primary, other, flags): 147 """ 148 Remove monitoring the given L{FileDescriptor} for either reading or 149 writing. If it's still monitored for the other operation, we 150 re-register the L{FileDescriptor} for only that operation. 151 """ 152 if source not in primary: 153 return 154 self._source_remove(self._sources[source]) 155 primary.remove(source) 156 if source in other: 157 self._sources[source] = self.input_add( 158 source, flags, self._ioEventCallback) 159 else: 160 self._sources.pop(source) 161 162 def removeReader(self, reader): 163 """ 164 Stop monitoring the given L{FileDescriptor} for reading. 165 """ 166 self._remove(reader, self._reads, self._writes, self.OUTFLAGS) 167 168 def removeWriter(self, writer): 169 """ 170 Stop monitoring the given L{FileDescriptor} for writing. 171 """ 172 self._remove(writer, self._writes, self._reads, self.INFLAGS) 173 174 def iterate(self, delay=0): 175 """ 176 One iteration of the event loop, for trial's use. 177 178 This is not used for actual reactor runs. 179 """ 180 self.runUntilCurrent() 181 while self._pending(): 182 self._iteration(0) 183 184 def crash(self): 185 """ 186 Crash the reactor. 187 """ 188 posixbase.PosixReactorBase.crash(self) 189 self._crash() 190 191 def stop(self): 192 """ 193 Stop the reactor. 194 """ 195 posixbase.PosixReactorBase.stop(self) 196 # The base implementation only sets a flag, to ensure shutting down is 197 # not reentrant. Unfortunately, this flag is not meaningful to the 198 # gobject event loop. We therefore call wakeUp() to ensure the event 199 # loop will call back into Twisted once this iteration is done. This 200 # will result in self.runUntilCurrent() being called, where the stop 201 # flag will trigger the actual shutdown process, eventually calling 202 # crash() which will do the actual gobject event loop shutdown. 203 self.wakeUp() 204 205 def run(self, installSignalHandlers=True): 206 """ 207 Run the reactor. 208 """ 209 self.callWhenRunning(self._reschedule) 210 self.startRunning(installSignalHandlers=installSignalHandlers) 211 if self._started: 212 self._run() 213 214 def callLater(self, *args, **kwargs): 215 """ 216 Schedule a C{DelayedCall}. 217 """ 218 result = posixbase.PosixReactorBase.callLater(self, *args, **kwargs) 219 # Make sure we'll get woken up at correct time to handle this new 220 # scheduled call: 221 self._reschedule() 222 return result 223 224 def _reschedule(self): 225 """ 226 Schedule a glib timeout for C{_simulate}. 227 """ 228 if self._simtag is not None: 229 self._source_remove(self._simtag) 230 self._simtag = None 231 timeout = self.timeout() 232 if timeout is not None: 233 self._simtag = self._timeout_add(int(timeout * 1000), 234 self._simulate) 235 236 def _simulate(self): 237 """ 238 Run timers, and then reschedule glib timeout for next scheduled event. 239 """ 240 self.runUntilCurrent() 241 self._reschedule() 242 243 244 class PortableGlibReactorBase(GlibSignalMixin, selectreactor.SelectReactor): 245 """ 246 Portable GObject event loop reactor. 247 """ 248 def __init__(self): 249 self._simtag = None 250 selectreactor.SelectReactor.__init__(self) 251 252 def crash(self): 253 selectreactor.SelectReactor.crash(self) 254 self._crash() 255 256 def run(self, installSignalHandlers=True): 257 self.startRunning(installSignalHandlers=installSignalHandlers) 258 self.idle_add(self.simulate) 259 self._run() 260 261 def simulate(self): 262 """ 263 Run simulation loops and reschedule callbacks. 264 """ 265 if self._simtag is not None: 266 self._source_remove(self._simtag) 267 self.iterate() 268 timeout = min(self.timeout(), 0.1) 269 if timeout is None: 270 timeout = 0.1 271 self._simtag = self._timeout_add(int(timeout * 1000), self.simulate) -
twisted/internet/gireactor.py
=== added file 'twisted/internet/gireactor.py'
1 # -*- test-case-name: twisted.internet.test -*- 2 # Copyright (c) Twisted Matrix Laboratories. 3 # Copyright (c) 2011-2012 Canonical Ltd. 4 # See LICENSE for details. 5 6 """ 7 This module provides support for Twisted to interact with the glib/gtk3 8 mainloop via GObject Introspection. 9 10 In order to use this support, simply do the following:: 11 12 | from twisted.internet import gireactor 13 | gireactor.install() 14 15 Then use twisted.internet APIs as usual. The other methods here are not 16 intended to be called directly. 17 18 When installing the reactor, you can choose whether to use the glib 19 event loop or the GTK+ event loop which is based on it but adds GUI 20 integration. 21 """ 22 23 import sys 24 25 if 'gobject' in sys.modules: 26 raise ImportError(('Introspected and static bindings must not be mixed.' 27 ' Use glib2reactor or gtk2reactor instead.')) 28 29 30 from gi.repository import GLib 31 # We need to override sys.modules with these to prevent imports. 32 # This is required, as importing these can result in SEGFAULTs. 33 sys.modules['glib'] = None 34 sys.modules['gobject'] = None 35 sys.modules['gio'] = None 36 sys.modules['gtk'] = None 37 38 from twisted.internet import _glibbase 39 from twisted.python import runtime 40 41 GLib.threads_init() 42 43 44 class GIReactor(_glibbase.GlibReactorBase): 45 """ 46 GObject event loop reactor. 47 48 @ivar _sources: A dictionary mapping L{FileDescriptor} instances to 49 GSource handles. 50 51 @ivar _reads: A set of L{FileDescriptor} instances currently monitored for 52 reading. 53 54 @ivar _writes: A set of L{FileDescriptor} instances currently monitored for 55 writing. 56 57 @ivar _simtag: A GSource handle for the next L{simulate} call. 58 """ 59 _POLL_DISCONNECTED = (GLib.IOCondition.HUP | GLib.IOCondition.ERR | 60 GLib.IOCondition.NVAL) 61 _POLL_IN = GLib.IOCondition.IN 62 _POLL_OUT = GLib.IOCondition.OUT 63 64 # glib's iochannel sources won't tell us about any events that we haven't 65 # asked for, even if those events aren't sensible inputs to the poll() 66 # call. 67 INFLAGS = _POLL_IN | _POLL_DISCONNECTED 68 OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED 69 70 def __init__(self, useGtk=False): 71 _glibbase.GlibReactorBase.__init__(self) 72 73 self._source_remove = GLib.source_remove 74 self._timeout_add = GLib.timeout_add 75 76 if useGtk: 77 from gi.repository import Gtk 78 79 self._pending = Gtk.events_pending 80 self._iteration = Gtk.main_iteration_do 81 self._crash = Gtk.main_quit 82 self._run = Gtk.main 83 else: 84 self.context = GLib.main_context_default() 85 self._pending = self.context.pending 86 self._iteration = self.context.iteration 87 self.loop = GLib.MainLoop() 88 self._crash = lambda: GLib.idle_add(self.loop.quit) 89 self._run = self.loop.run 90 91 # The input_add function in pygtk1 checks for objects with a 92 # 'fileno' method and, if present, uses the result of that method 93 # as the input source. The pygtk2 input_add does not do this. The 94 # function below replicates the pygtk1 functionality. 95 96 # In addition, pygtk maps gtk.input_add to _gobject.io_add_watch, and 97 # g_io_add_watch() takes different condition bitfields than 98 # gtk_input_add(). We use g_io_add_watch() here in case pygtk fixes this 99 # bug. 100 def input_add(self, source, condition, callback): 101 if hasattr(source, 'fileno'): 102 # handle python objects 103 def wrapper(source, condition, real_s=source, real_cb=callback): 104 return real_cb(real_s, condition) 105 return GLib.io_add_watch(source.fileno(), condition, wrapper) 106 else: 107 return GLib.io_add_watch(source, condition, callback) 108 109 110 class PortableGIReactor(_glibbase.PortableGlibReactorBase): 111 """ 112 Portable GObject Introspection event loop reactor. 113 """ 114 def __init__(self, useGtk=False): 115 _glibbase.PortableGlibReactorBase.__init__(self) 116 117 self._source_remove = GLib.source_remove 118 self._idle_add = GLib.idle_add 119 self._timeout_add = GLib.timeout_add 120 121 if useGtk: 122 from gi.repository import Gtk 123 124 self._crash = Gtk.main_quit 125 self._run = Gtk.main 126 else: 127 self.loop = GLib.MainLoop() 128 self._crash = lambda: GLib.idle_add(self.loop.quit) 129 self._run = self.loop.run 130 131 132 def install(useGtk=False): 133 """ 134 Configure the twisted mainloop to be run inside the glib mainloop. 135 136 @param useGtk: should GTK+ rather than glib event loop be 137 used (this will be slightly slower but does support GUI). 138 """ 139 if runtime.platform.getType() == 'posix': 140 reactor = GIReactor(useGtk=useGtk) 141 else: 142 reactor = PortableGIReactor(useGtk=useGtk) 143 144 from twisted.internet.main import installReactor 145 installReactor(reactor) 146 return reactor 147 148 149 __all__ = ['install'] -
twisted/internet/gtk2reactor.py
=== modified file 'twisted/internet/gtk2reactor.py'
integration. 21 21 """ 22 22 23 23 # System Imports 24 import sys , signal24 import sys 25 25 26 from zope.interface import implements 26 if 'gi' in sys.modules: 27 raise ImportError(('Introspected and static bindings must not be mixed.' 28 ' Use twisted.internet.gireactor instead.')) 29 30 # Disable gi imports to avoid potential problems. 31 sys.modules['gi'] = None 27 32 28 33 try: 29 34 if not hasattr(sys, 'frozen'): … … if hasattr(gobject, "threads_init"): 40 45 gobject.threads_init() 41 46 42 47 # Twisted Imports 43 from twisted.python import log, runtime 44 from twisted.python.compat import set 45 from twisted.internet.interfaces import IReactorFDSet 46 from twisted.internet import base, posixbase, selectreactor 47 48 POLL_DISCONNECTED = gobject.IO_HUP | gobject.IO_ERR | gobject.IO_NVAL 49 50 # glib's iochannel sources won't tell us about any events that we haven't 51 # asked for, even if those events aren't sensible inputs to the poll() 52 # call. 53 INFLAGS = gobject.IO_IN | POLL_DISCONNECTED 54 OUTFLAGS = gobject.IO_OUT | POLL_DISCONNECTED 55 56 57 58 def _our_mainquit(): 59 # XXX: gtk.main_quit() (which is used for crash()) raises an exception if 60 # gtk.main_level() == 0; however, all the tests freeze if we use this 61 # function to stop the reactor. what gives? (I believe this may have been 62 # a stupid mistake where I forgot to import gtk here... I will remove this 63 # comment if the tests pass) 64 import gtk 65 if gtk.main_level(): 66 gtk.main_quit() 67 68 69 70 class _Gtk2SignalMixin(object): 71 if runtime.platformType == 'posix': 72 def _handleSignals(self): 73 # Let the base class do its thing, but pygtk is probably 74 # going to stomp on us so go beyond that and set up some 75 # signal handling which pygtk won't mess with. This would 76 # be better done by letting this reactor select a 77 # different implementation of installHandler for 78 # _SIGCHLDWaker to use. Then, at least, we could fall 79 # back to our extension module. See #4286. 80 from twisted.internet.process import reapAllProcesses as _reapAllProcesses 81 base._SignalReactorMixin._handleSignals(self) 82 signal.signal(signal.SIGCHLD, lambda *a: self.callFromThread(_reapAllProcesses)) 83 if getattr(signal, "siginterrupt", None) is not None: 84 signal.siginterrupt(signal.SIGCHLD, False) 85 # Like the base, reap processes now in case a process 86 # exited before the handlers above were installed. 87 _reapAllProcesses() 88 48 from twisted.internet import _glibbase 49 from twisted.python import runtime 89 50 90 51 91 class _Gtk2Waker(posixbase._UnixWaker): 92 """ 93 Run scheduled events after waking up. 94 """ 95 96 def doRead(self): 97 posixbase._UnixWaker.doRead(self) 98 self.reactor._simulate() 99 100 101 102 class Gtk2Reactor(_Gtk2SignalMixin, posixbase.PosixReactorBase, posixbase._PollLikeMixin): 52 class Gtk2Reactor(_glibbase.GlibReactorBase): 103 53 """ 104 54 GTK+-2 event loop reactor. 105 55 … … class Gtk2Reactor(_Gtk2SignalMixin, posi 130 80 131 81 @ivar _simtag: A gtk timeout handle for the next L{_simulate} call. 132 82 """ 133 implements(IReactorFDSet) 134 135 _POLL_DISCONNECTED = POLL_DISCONNECTED 83 _POLL_DISCONNECTED = gobject.IO_HUP | gobject.IO_ERR | gobject.IO_NVAL 136 84 _POLL_IN = gobject.IO_IN 137 85 _POLL_OUT = gobject.IO_OUT 138 86 139 # Install a waker that knows it needs to call C{_simulate} in order to run 140 # callbacks queued from a thread: 141 _wakerFactory = _Gtk2Waker 87 # glib's iochannel sources won't tell us about any events that we haven't 88 # asked for, even if those events aren't sensible inputs to the poll() 89 # call. 90 INFLAGS = _POLL_IN | _POLL_DISCONNECTED 91 OUTFLAGS = _POLL_OUT | _POLL_DISCONNECTED 142 92 143 93 def __init__(self, useGtk=True): 144 self._simtag = None145 self._reads = set() 146 self._ writes = set()147 self._ sources = {}148 posixbase.PosixReactorBase.__init__(self) 94 _glibbase.GlibReactorBase.__init__(self) 95 96 self._source_remove = gobject.source_remove 97 self._timeout_add = gobject.timeout_add 98 149 99 # pre 2.3.91 the glib iteration and mainloop functions didn't release 150 100 # global interpreter lock, thus breaking thread and signal support. 151 101 if getattr(gobject, "pygtk_version", ()) >= (2, 3, 91) and not useGtk: 152 102 self.context = gobject.main_context_default() 153 self._ _pending = self.context.pending154 self._ _iteration = self.context.iteration103 self._pending = self.context.pending 104 self._iteration = self.context.iteration 155 105 self.loop = gobject.MainLoop() 156 self._ _crash = self.loop.quit157 self._ _run = self.loop.run106 self._crash = self.loop.quit 107 self._run = self.loop.run 158 108 else: 159 109 import gtk 160 self.__pending = gtk.events_pending 161 self.__iteration = gtk.main_iteration 162 self.__crash = _our_mainquit 163 self.__run = gtk.main 110 111 def mainquit(): 112 if gtk.main_level(): 113 gtk.main_quit() 114 115 self._pending = gtk.events_pending 116 self._iteration = gtk.main_iteration 117 self._crash = mainquit 118 self._run = gtk.main 164 119 165 120 166 121 # The input_add function in pygtk1 checks for objects with a … … class Gtk2Reactor(_Gtk2SignalMixin, posi 182 137 return gobject.io_add_watch(source, condition, callback) 183 138 184 139 185 def _ioEventCallback(self, source, condition): 186 """ 187 Called by event loop when an I/O event occurs. 188 """ 189 log.callWithLogger( 190 source, self._doReadOrWrite, source, source, condition) 191 return 1 # 1=don't auto-remove the source 192 193 194 def _add(self, source, primary, other, primaryFlag, otherFlag): 195 """ 196 Add the given L{FileDescriptor} for monitoring either for reading or 197 writing. If the file is already monitored for the other operation, we 198 delete the previous registration and re-register it for both reading 199 and writing. 200 """ 201 if source in primary: 202 return 203 flags = primaryFlag 204 if source in other: 205 gobject.source_remove(self._sources[source]) 206 flags |= otherFlag 207 self._sources[source] = self.input_add( 208 source, flags, self._ioEventCallback) 209 primary.add(source) 210 211 212 def addReader(self, reader): 213 """ 214 Add a L{FileDescriptor} for monitoring of data available to read. 215 """ 216 self._add(reader, self._reads, self._writes, INFLAGS, OUTFLAGS) 217 218 219 def addWriter(self, writer): 220 """ 221 Add a L{FileDescriptor} for monitoring ability to write data. 222 """ 223 self._add(writer, self._writes, self._reads, OUTFLAGS, INFLAGS) 224 225 226 def getReaders(self): 227 """ 228 Retrieve the list of current L{FileDescriptor} monitored for reading. 229 """ 230 return list(self._reads) 231 232 233 def getWriters(self): 234 """ 235 Retrieve the list of current L{FileDescriptor} monitored for writing. 236 """ 237 return list(self._writes) 238 239 240 def removeAll(self): 241 """ 242 Remove monitoring for all registered L{FileDescriptor}s. 243 """ 244 return self._removeAll(self._reads, self._writes) 245 246 247 def _remove(self, source, primary, other, flags): 248 """ 249 Remove monitoring the given L{FileDescriptor} for either reading or 250 writing. If it's still monitored for the other operation, we 251 re-register the L{FileDescriptor} for only that operation. 252 """ 253 if source not in primary: 254 return 255 gobject.source_remove(self._sources[source]) 256 primary.remove(source) 257 if source in other: 258 self._sources[source] = self.input_add( 259 source, flags, self._ioEventCallback) 260 else: 261 self._sources.pop(source) 262 263 264 def removeReader(self, reader): 265 """ 266 Stop monitoring the given L{FileDescriptor} for reading. 267 """ 268 self._remove(reader, self._reads, self._writes, OUTFLAGS) 269 270 271 def removeWriter(self, writer): 272 """ 273 Stop monitoring the given L{FileDescriptor} for writing. 274 """ 275 self._remove(writer, self._writes, self._reads, INFLAGS) 276 277 278 def iterate(self, delay=0): 279 """ 280 One iteration of the event loop, for trial's use. 281 282 This is not used for actual reactor runs. 283 """ 284 self.runUntilCurrent() 285 while self.__pending(): 286 self.__iteration(0) 287 288 289 def crash(self): 290 """ 291 Crash the reactor. 292 """ 293 posixbase.PosixReactorBase.crash(self) 294 self.__crash() 295 296 297 def stop(self): 298 """ 299 Stop the reactor. 300 """ 301 posixbase.PosixReactorBase.stop(self) 302 # The base implementation only sets a flag, to ensure shutting down is 303 # not reentrant. Unfortunately, this flag is not meaningful to the 304 # gobject event loop. We therefore call wakeUp() to ensure the event 305 # loop will call back into Twisted once this iteration is done. This 306 # will result in self.runUntilCurrent() being called, where the stop 307 # flag will trigger the actual shutdown process, eventually calling 308 # crash() which will do the actual gobject event loop shutdown. 309 self.wakeUp() 310 311 312 def run(self, installSignalHandlers=1): 313 """ 314 Run the reactor. 315 """ 316 self.callWhenRunning(self._reschedule) 317 self.startRunning(installSignalHandlers=installSignalHandlers) 318 if self._started: 319 self.__run() 320 321 322 def callLater(self, *args, **kwargs): 323 """ 324 Schedule a C{DelayedCall}. 325 """ 326 result = posixbase.PosixReactorBase.callLater(self, *args, **kwargs) 327 # Make sure we'll get woken up at correct time to handle this new 328 # scheduled call: 329 self._reschedule() 330 return result 331 332 333 def _reschedule(self): 334 """ 335 Schedule a glib timeout for C{_simulate}. 336 """ 337 if self._simtag is not None: 338 gobject.source_remove(self._simtag) 339 self._simtag = None 340 timeout = self.timeout() 341 if timeout is not None: 342 self._simtag = gobject.timeout_add(int(timeout * 1000), 343 self._simulate) 344 345 346 def _simulate(self): 347 """ 348 Run timers, and then reschedule glib timeout for next scheduled event. 349 """ 350 self.runUntilCurrent() 351 self._reschedule() 352 353 354 355 class PortableGtkReactor(_Gtk2SignalMixin, selectreactor.SelectReactor): 140 class PortableGtkReactor(_glibbase.PortableGlibReactorBase): 356 141 """ 357 142 Reactor that works on Windows. 358 143 359 144 Sockets aren't supported by GTK+'s input_add on Win32. 360 145 """ 361 _simtag = None 146 def __init__(self, useGtk=False): 147 _glibbase.PortableGlibReactorBase.__init__(self) 362 148 363 def crash(self): 364 selectreactor.SelectReactor.crash(self) 365 import gtk 366 # mainquit is deprecated in newer versions 367 if gtk.main_level(): 368 if hasattr(gtk, 'main_quit'): 369 gtk.main_quit() 370 else: 371 gtk.mainquit() 372 373 374 def run(self, installSignalHandlers=1): 375 import gtk 376 self.startRunning(installSignalHandlers=installSignalHandlers) 377 gobject.timeout_add(0, self.simulate) 378 # mainloop is deprecated in newer versions 379 if self._started: 380 if hasattr(gtk, 'main'): 381 gtk.main() 382 else: 383 gtk.mainloop() 384 385 386 def simulate(self): 387 """ 388 Run simulation loops and reschedule callbacks. 389 """ 390 if self._simtag is not None: 391 gobject.source_remove(self._simtag) 392 self.iterate() 393 timeout = min(self.timeout(), 0.01) 394 if timeout is None: 395 timeout = 0.01 396 # grumble 397 self._simtag = gobject.timeout_add(int(timeout * 1010), self.simulate) 149 self._source_remove = gobject.source_remove 150 self._idle_add = gobject.idle_add 151 self._timeout_add = gobject.timeout_add 398 152 153 if useGtk: 154 import gtk 155 156 def mainquit(): 157 if gtk.main_level(): 158 gtk.main_quit() 159 160 self._crash = mainquit 161 self._run = gtk.main 162 else: 163 self.loop = gobject.MainLoop() 164 self._crash = lambda: gobject.idle_add(self.loop.quit) 165 self._run = self.loop.run 399 166 400 167 401 168 def install(useGtk=True): … … def install(useGtk=True): 411 178 return reactor 412 179 413 180 414 415 181 def portableInstall(useGtk=True): 416 182 """ 417 183 Configure the twisted mainloop to be run inside the gtk mainloop. … … def portableInstall(useGtk=True): 422 188 return reactor 423 189 424 190 425 426 191 if runtime.platform.getType() != 'posix': 427 192 install = portableInstall 428 193 429 194 430 431 195 __all__ = ['install'] -
twisted/internet/gtk3reactor.py
=== added file 'twisted/internet/gtk3reactor.py'
1 2 """ 3 This module provides support for Twisted to interact with the gtk3 mainloop. 4 This is like gi, but slightly slower and requires a working $DISPLAY. 5 6 In order to use this support, simply do the following:: 7 8 | from twisted.internet import gtk3reactor 9 | gtk3reactor.install() 10 11 Then use twisted.internet APIs as usual. The other methods here are not 12 intended to be called directly. 13 14 When installing the reactor, you can choose whether to use the glib 15 event loop or the GTK+ event loop which is based on it but adds GUI 16 integration. 17 18 Maintainer: Itamar Shtull-Trauring 19 """ 20 21 from twisted.internet import gireactor 22 from twisted.python import runtime 23 24 25 class Gtk3Reactor(gireactor.GIReactor): 26 """ 27 The reactor using the gtk3 mainloop. 28 """ 29 30 def __init__(self, useGtk=True): 31 """ 32 Override init to set the C{useGtk} flag. 33 """ 34 gireactor.GIReactor.__init__(self, useGtk=useGtk) 35 36 37 class PortableGtk3Reactor(gireactor.PortableGIReactor): 38 """ 39 Portable GTK+ 3.x reactor. 40 """ 41 def __init__(self, useGtk=True): 42 """ 43 Override init to set the C{useGtk} flag. 44 """ 45 gireactor.PortableGIReactor.__init__(self, useGtk=useGtk) 46 47 48 def install(useGtk=True): 49 """ 50 Configure the twisted mainloop to be run inside the glib mainloop. 51 52 @param useGtk: should GTK+ rather than glib event loop be 53 used (this will be slightly slower but does support GUI). 54 """ 55 if runtime.platform.getType() == 'posix': 56 reactor = Gtk3Reactor(useGtk=useGtk) 57 else: 58 reactor = PortableGtk3Reactor(useGtk=useGtk) 59 60 from twisted.internet.main import installReactor 61 installReactor(reactor) 62 return reactor 63 64 65 __all__ = ['install'] -
twisted/internet/test/reactormixins.py
=== modified file 'twisted/internet/test/reactormixins.py'
class ReactorBuilder: 56 56 # it's not _really_ worth it to support on other platforms, 57 57 # since no one really wants to use it on other platforms. 58 58 _reactors.extend([ 59 "twisted.internet.gireactor.PortableGIReactor", 60 "twisted.internet.gtk3reactor.PortableGtk3Reactor", 59 61 "twisted.internet.gtk2reactor.PortableGtkReactor", 60 62 "twisted.internet.win32eventreactor.Win32Reactor", 61 63 "twisted.internet.iocpreactor.reactor.IOCPReactor"]) 62 64 else: 63 65 _reactors.extend([ 66 "twisted.internet.gireactor.GIReactor", 67 "twisted.internet.gtk3reactor.Gtk3Reactor", 64 68 "twisted.internet.glib2reactor.Glib2Reactor", 65 69 "twisted.internet.gtk2reactor.Gtk2Reactor", 66 70 "twisted.internet.kqreactor.KQueueReactor"]) -
twisted/plugins/twisted_reactors.py
=== modified file 'twisted/plugins/twisted_reactors.py'
select = Reactor( 11 11 'select', 'twisted.internet.selectreactor', 'select(2)-based reactor.') 12 12 wx = Reactor( 13 13 'wx', 'twisted.internet.wxreactor', 'wxPython integration reactor.') 14 gi = Reactor( 15 'gi', 'twisted.internet.gireactor', 'GObject Introspection integration reactor.') 16 gtk3 = Reactor( 17 'gtk3', 'twisted.internet.gtk3reactor', 'Gtk3 integration reactor.') 14 18 gtk = Reactor( 15 19 'gtk', 'twisted.internet.gtkreactor', 'Gtk1 integration reactor.') 16 20 gtk2 = Reactor(
