"""
This module provides support for Twisted to interact with the PyGTK2 mainloop.
In order to use this support, simply do the following::
| from twisted.internet import gtk2reactor
| gtk2reactor.install()
Then use twisted.internet APIs as usual. The other methods here are not
intended to be called directly.
API Stability: stable
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
"""
__all__ = ['install']
import sys, time
try:
if not hasattr(sys, 'frozen'):
import pygtk
pygtk.require('2.0')
except ImportError, AttributeError:
pass import gtk
from twisted.python import log, threadable, runtime, failure
from twisted.internet.interfaces import IReactorFDSet
from twisted.internet import main, default, error
reads = default.reads
writes = default.writes
hasReader = reads.has_key
hasWriter = writes.has_key
_simtag = None
POLL_DISCONNECTED = gtk._gobject.IO_HUP | gtk._gobject.IO_ERR | \
gtk._gobject.IO_NVAL
INFLAGS = gtk._gobject.IO_IN | POLL_DISCONNECTED
OUTFLAGS = gtk._gobject.IO_OUT | POLL_DISCONNECTED
class Gtk2Reactor(default.PosixReactorBase):
"""GTK+-2 event loop reactor.
"""
__implements__ = (default.PosixReactorBase.__implements__, IReactorFDSet)
def input_add(self, source, condition, callback):
if hasattr(source, 'fileno'):
def wrapper(source, condition, real_s=source, real_cb=callback):
return real_cb(real_s, condition)
return gtk._gobject.io_add_watch(source.fileno(), condition,
wrapper)
else:
return gtk._gobject.io_add_watch(source, condition, callback)
def addReader(self, reader):
if not hasReader(reader):
reads[reader] = self.input_add(reader, INFLAGS, self.callback)
def addWriter(self, writer):
if not hasWriter(writer):
writes[writer] = self.input_add(writer, OUTFLAGS, self.callback)
def removeAll(self):
v = reads.keys()
for reader in v:
self.removeReader(reader)
return v
def removeReader(self, reader):
if hasReader(reader):
gtk.input_remove(reads[reader])
del reads[reader]
def removeWriter(self, writer):
if hasWriter(writer):
gtk.input_remove(writes[writer])
del writes[writer]
doIterationTimer = None
def doIterationTimeout(self, *args):
self.doIterationTimer = None
return 0 def doIteration(self, delay):
log.msg(channel='system', event='iteration', reactor=self)
if gtk.events_pending():
gtk.main_iteration(0)
return
if delay == 0:
return self.doIterationTimer = gtk.timeout_add(int(delay * 1000),
self.doIterationTimeout)
gtk.main_iteration(1) if self.doIterationTimer:
gtk.timeout_remove(self.doIterationTimer)
self.doIterationTimer = None
def crash(self):
gtk.main_quit()
def run(self, installSignalHandlers=1):
self.startRunning(installSignalHandlers=installSignalHandlers)
self.simulate()
gtk.main()
def _doReadOrWrite(self, source, condition, faildict={
error.ConnectionDone: failure.Failure(error.ConnectionDone()),
error.ConnectionLost: failure.Failure(error.ConnectionLost()) }):
why = None
if condition & POLL_DISCONNECTED and \
not (condition & gtk._gobject.IO_IN):
why = main.CONNECTION_LOST
else:
try:
didRead = None
if condition & gtk._gobject.IO_IN:
why = source.doRead()
didRead = source.doRead
if not why and condition & gtk._gobject.IO_OUT:
if not source.disconnected and source.doWrite != didRead:
why = source.doWrite()
except:
why = sys.exc_info()[1]
log.msg('Error In %s' % source)
log.deferr()
if why:
self.removeReader(source)
self.removeWriter(source)
f = faildict.get(why.__class__)
if f:
source.connectionLost(f)
else:
source.connectionLost(failure.Failure(why))
def callback(self, source, condition):
log.callWithLogger(source, self._doReadOrWrite, source, condition)
self.simulate() return 1
def simulate(self):
"""Run simulation loops and reschedule callbacks.
"""
global _simtag
if _simtag is not None:
gtk.timeout_remove(_simtag)
self.runUntilCurrent()
timeout = min(self.timeout(), 0.1)
if timeout is None:
timeout = 0.1
_simtag = gtk.timeout_add(int(timeout * 1010), self.simulate)
class PortableGtkReactor(default.SelectReactor):
"""Reactor that works on Windows.
input_add is not supported on GTK+ for Win32, apparently.
"""
def crash(self):
gtk.mainquit()
def run(self, installSignalHandlers=1):
self.startRunning(installSignalHandlers=installSignalHandlers)
self.simulate()
gtk.mainloop()
def simulate(self):
"""Run simulation loops and reschedule callbacks.
"""
global _simtag
if _simtag is not None:
gtk.timeout_remove(_simtag)
self.iterate()
timeout = min(self.timeout(), 0.1)
if timeout is None:
timeout = 0.1
_simtag = gtk.timeout_add(int(timeout * 1010), self.simulate)
def install():
"""Configure the twisted mainloop to be run inside the gtk mainloop.
"""
reactor = Gtk2Reactor()
from twisted.internet.main import installReactor
installReactor(reactor)
return reactor
def portableInstall():
"""Configure the twisted mainloop to be run inside the gtk mainloop.
"""
reactor = PortableGtkReactor()
from twisted.internet.main import installReactor
installReactor(reactor)
return reactor
if runtime.platform.getType() != 'posix':
install = portableInstall