1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
6 A modified gtk2 reactor with a Glade dialog in-process that allows you to stop,
7 suspend, resume and inspect transports interactively.
13 from twisted.python import log, threadable, runtime, failure, util, reflect
14 from twisted.internet.gtk2reactor import Gtk2Reactor as sup
20 COLUMN_DESCRIPTION = 0
26 class GladeReactor(sup):
27 """GTK+-2 event loop reactor with GUI.
30 def listenTCP(self, port, factory, backlog=50, interface=''):
31 from _inspectro import LoggingFactory
32 factory = LoggingFactory(factory)
33 return sup.listenTCP(self, port, factory, backlog, interface)
35 def connectTCP(self, host, port, factory, timeout=30, bindAddress=None):
36 from _inspectro import LoggingFactory
37 factory = LoggingFactory(factory)
38 return sup.connectTCP(self, host, port, factory, timeout, bindAddress)
40 def listenSSL(self, port, factory, contextFactory, backlog=50, interface=''):
41 from _inspectro import LoggingFactory
42 factory = LoggingFactory(factory)
43 return sup.listenSSL(self, port, factory, contextFactory, backlog, interface)
45 def connectSSL(self, host, port, factory, contextFactory, timeout=30, bindAddress=None):
46 from _inspectro import LoggingFactory
47 factory = LoggingFactory(factory)
48 return sup.connectSSL(self, host, port, factory, contextFactory, timeout, bindAddress)
50 def connectUNIX(self, address, factory, timeout=30):
51 from _inspectro import LoggingFactory
52 factory = LoggingFactory(factory)
53 return sup.connectUNIX(self, address, factory, timeout)
55 def listenUNIX(self, address, factory, backlog=50, mode=0666):
56 from _inspectro import LoggingFactory
57 factory = LoggingFactory(factory)
58 return sup.listenUNIX(self, address, factory, backlog, mode)
60 def on_disconnect_clicked(self, w):
61 store, iter = self.servers.get_selection().get_selected()
62 store[iter][COLUMN_TRANSPORT].loseConnection()
64 def on_viewlog_clicked(self, w):
65 store, iter = self.servers.get_selection().get_selected()
67 from _inspectro import LogViewer
68 if hasattr(data, "protocol") and not data.protocol.logViewer:
69 LogViewer(data.protocol)
71 def on_inspect_clicked(self, w):
72 store, iter = self.servers.get_selection().get_selected()
74 from _inspectro import Inspectro
77 def on_suspend_clicked(self, w):
78 store, iter = self.servers.get_selection().get_selected()
80 sup.removeReader(self, data[1])
81 sup.removeWriter(self, data[1])
82 if data[COLUMN_DESCRIPTION].endswith('(suspended)'):
83 if data[COLUMN_READING]:
84 sup.addReader(self, data[COLUMN_TRANSPORT])
85 if data[COLUMN_WRITING]:
86 sup.addWriter(self, data[COLUMN_TRANSPORT])
87 data[COLUMN_DESCRIPTION] = str(data[COLUMN_TRANSPORT])
88 self.toggle_suspend(1)
90 data[0] += ' (suspended)'
91 self.toggle_suspend(0)
93 def toggle_suspend(self, suspending=0):
94 stock, nonstock = [('gtk-redo', 'Resume'),
95 ('gtk-undo', 'Suspend')][suspending]
96 b = self.xml.get_widget("suspend")
99 b.get_child().get_child().get_children()[1].set_label(nonstock)
101 def servers_selection_changed(self, w):
102 store, iter = w.get_selected()
104 self.xml.get_widget("suspend").set_sensitive(0)
105 self.xml.get_widget('disconnect').set_sensitive(0)
108 self.toggle_suspend(not
109 data[COLUMN_DESCRIPTION].endswith('(suspended)'))
110 self.xml.get_widget("suspend").set_sensitive(1)
111 self.xml.get_widget('disconnect').set_sensitive(1)
113 def on_quit_clicked(self, w):
117 self.xml = gtk.glade.XML(util.sibpath(__file__,"gladereactor.glade"))
119 for m in reflect.prefixedMethods(self, "on_"):
120 d[m.im_func.__name__] = m
121 self.xml.signal_autoconnect(d)
122 self.xml.get_widget('window1').connect('destroy',
123 lambda w: self.stop())
124 self.servers = self.xml.get_widget("servertree")
125 sel = self.servers.get_selection()
126 sel.set_mode(gtk.SELECTION_SINGLE)
127 sel.connect("changed",
128 self.servers_selection_changed)
129 ## argh coredump: self.servers_selection_changed(sel)
130 self.xml.get_widget('suspend').set_sensitive(0)
131 self.xml.get_widget('disconnect').set_sensitive(0)
132 # setup model, connect it to my treeview
133 self.model = gtk.ListStore(str, object, gobject.TYPE_BOOLEAN,
134 gobject.TYPE_BOOLEAN)
135 self.servers.set_model(self.model)
136 self.servers.set_reorderable(1)
137 self.servers.set_headers_clickable(1)
138 # self.servers.set_headers_draggable(1)
141 gtk.TreeViewColumn('Server',
142 gtk.CellRendererText(),
144 gtk.TreeViewColumn('Reading',
145 gtk.CellRendererToggle(),
147 gtk.TreeViewColumn('Writing',
148 gtk.CellRendererToggle(),
151 self.servers.append_column(col)
155 def addReader(self, reader):
156 sup.addReader(self, reader)
157 ## gtk docs suggest this - but it's stupid
158 ## self.model.set(self.model.append(),
161 self._maybeAddServer(reader, read=1)
163 def _goAway(self,reader):
164 for p in range(len(self.model)):
165 if self.model[p][1] == reader:
166 self.model.remove(self.model.get_iter_from_string(str(p)))
170 def _maybeAddServer(self, reader, read=0, write=0):
181 if not (x[2] or x[3]):
182 x[0] = x[0] + '(disconnected)'
183 self.callLater(5, self._goAway, reader)
188 write = max(write, 0)
190 self.model.append((reader,reader,read,write))
192 def addWriter(self, writer):
193 sup.addWriter(self, writer)
194 self._maybeAddServer(writer, write=1)
196 def removeReader(self, reader):
197 sup.removeReader(self, reader)
198 self._maybeAddServer(reader, read=-1)
200 def removeWriter(self, writer):
201 sup.removeWriter(self, writer)
202 self._maybeAddServer(writer, write=-1)
207 def run(self, installSignalHandlers=1):
208 self.startRunning(installSignalHandlers=installSignalHandlers)
214 """Configure the twisted mainloop to be run inside the gtk mainloop.
216 reactor = GladeReactor()
217 from twisted.internet.main import installReactor
218 installReactor(reactor)