| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
""" |
|---|
| 6 |
A modified gtk2 reactor with a Glade dialog in-process that allows you to stop, |
|---|
| 7 |
suspend, resume and inspect transports interactively. |
|---|
| 8 |
""" |
|---|
| 9 |
|
|---|
| 10 |
__all__ = ['install'] |
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
from twisted.python import log, threadable, runtime, failure, util, reflect |
|---|
| 14 |
from twisted.internet.gtk2reactor import Gtk2Reactor as sup |
|---|
| 15 |
|
|---|
| 16 |
import gtk |
|---|
| 17 |
import gobject |
|---|
| 18 |
import gtk.glade |
|---|
| 19 |
|
|---|
| 20 |
COLUMN_DESCRIPTION = 0 |
|---|
| 21 |
COLUMN_TRANSPORT = 1 |
|---|
| 22 |
COLUMN_READING = 2 |
|---|
| 23 |
COLUMN_WRITING = 3 |
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
class GladeReactor(sup): |
|---|
| 27 |
"""GTK+-2 event loop reactor with GUI. |
|---|
| 28 |
""" |
|---|
| 29 |
|
|---|
| 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) |
|---|
| 34 |
|
|---|
| 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) |
|---|
| 39 |
|
|---|
| 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) |
|---|
| 44 |
|
|---|
| 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) |
|---|
| 49 |
|
|---|
| 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) |
|---|
| 54 |
|
|---|
| 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) |
|---|
| 59 |
|
|---|
| 60 |
def on_disconnect_clicked(self, w): |
|---|
| 61 |
store, iter = self.servers.get_selection().get_selected() |
|---|
| 62 |
store[iter][COLUMN_TRANSPORT].loseConnection() |
|---|
| 63 |
|
|---|
| 64 |
def on_viewlog_clicked(self, w): |
|---|
| 65 |
store, iter = self.servers.get_selection().get_selected() |
|---|
| 66 |
data = store[iter][1] |
|---|
| 67 |
from _inspectro import LogViewer |
|---|
| 68 |
if hasattr(data, "protocol") and not data.protocol.logViewer: |
|---|
| 69 |
LogViewer(data.protocol) |
|---|
| 70 |
|
|---|
| 71 |
def on_inspect_clicked(self, w): |
|---|
| 72 |
store, iter = self.servers.get_selection().get_selected() |
|---|
| 73 |
data = store[iter] |
|---|
| 74 |
from _inspectro import Inspectro |
|---|
| 75 |
Inspectro(data[1]) |
|---|
| 76 |
|
|---|
| 77 |
def on_suspend_clicked(self, w): |
|---|
| 78 |
store, iter = self.servers.get_selection().get_selected() |
|---|
| 79 |
data = store[iter] |
|---|
| 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) |
|---|
| 89 |
else: |
|---|
| 90 |
data[0] += ' (suspended)' |
|---|
| 91 |
self.toggle_suspend(0) |
|---|
| 92 |
|
|---|
| 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") |
|---|
| 97 |
b.set_use_stock(1) |
|---|
| 98 |
b.set_label(stock) |
|---|
| 99 |
b.get_child().get_child().get_children()[1].set_label(nonstock) |
|---|
| 100 |
|
|---|
| 101 |
def servers_selection_changed(self, w): |
|---|
| 102 |
store, iter = w.get_selected() |
|---|
| 103 |
if iter is None: |
|---|
| 104 |
self.xml.get_widget("suspend").set_sensitive(0) |
|---|
| 105 |
self.xml.get_widget('disconnect').set_sensitive(0) |
|---|
| 106 |
else: |
|---|
| 107 |
data = store[iter] |
|---|
| 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) |
|---|
| 112 |
|
|---|
| 113 |
def on_quit_clicked(self, w): |
|---|
| 114 |
self.stop() |
|---|
| 115 |
|
|---|
| 116 |
def __init__(self): |
|---|
| 117 |
self.xml = gtk.glade.XML(util.sibpath(__file__,"gladereactor.glade")) |
|---|
| 118 |
d = {} |
|---|
| 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 |
|
|---|
| 130 |
self.xml.get_widget('suspend').set_sensitive(0) |
|---|
| 131 |
self.xml.get_widget('disconnect').set_sensitive(0) |
|---|
| 132 |
|
|---|
| 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 |
|
|---|
| 139 |
|
|---|
| 140 |
for col in [ |
|---|
| 141 |
gtk.TreeViewColumn('Server', |
|---|
| 142 |
gtk.CellRendererText(), |
|---|
| 143 |
text=0), |
|---|
| 144 |
gtk.TreeViewColumn('Reading', |
|---|
| 145 |
gtk.CellRendererToggle(), |
|---|
| 146 |
active=2), |
|---|
| 147 |
gtk.TreeViewColumn('Writing', |
|---|
| 148 |
gtk.CellRendererToggle(), |
|---|
| 149 |
active=3)]: |
|---|
| 150 |
|
|---|
| 151 |
self.servers.append_column(col) |
|---|
| 152 |
col.set_resizable(1) |
|---|
| 153 |
sup.__init__(self) |
|---|
| 154 |
|
|---|
| 155 |
def addReader(self, reader): |
|---|
| 156 |
sup.addReader(self, reader) |
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
|
|---|
| 160 |
|
|---|
| 161 |
self._maybeAddServer(reader, read=1) |
|---|
| 162 |
|
|---|
| 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))) |
|---|
| 167 |
return |
|---|
| 168 |
|
|---|
| 169 |
|
|---|
| 170 |
def _maybeAddServer(self, reader, read=0, write=0): |
|---|
| 171 |
p = 0 |
|---|
| 172 |
for x in self.model: |
|---|
| 173 |
if x[1] == reader: |
|---|
| 174 |
if reader == 0: |
|---|
| 175 |
reader += 1 |
|---|
| 176 |
x[2] += read |
|---|
| 177 |
x[3] += write |
|---|
| 178 |
x[2] = max(x[2],0) |
|---|
| 179 |
x[3] = max(x[3],0) |
|---|
| 180 |
|
|---|
| 181 |
if not (x[2] or x[3]): |
|---|
| 182 |
x[0] = x[0] + '(disconnected)' |
|---|
| 183 |
self.callLater(5, self._goAway, reader) |
|---|
| 184 |
return |
|---|
| 185 |
p += 1 |
|---|
| 186 |
else: |
|---|
| 187 |
read = max(read,0) |
|---|
| 188 |
write = max(write, 0) |
|---|
| 189 |
if read or write: |
|---|
| 190 |
self.model.append((reader,reader,read,write)) |
|---|
| 191 |
|
|---|
| 192 |
def addWriter(self, writer): |
|---|
| 193 |
sup.addWriter(self, writer) |
|---|
| 194 |
self._maybeAddServer(writer, write=1) |
|---|
| 195 |
|
|---|
| 196 |
def removeReader(self, reader): |
|---|
| 197 |
sup.removeReader(self, reader) |
|---|
| 198 |
self._maybeAddServer(reader, read=-1) |
|---|
| 199 |
|
|---|
| 200 |
def removeWriter(self, writer): |
|---|
| 201 |
sup.removeWriter(self, writer) |
|---|
| 202 |
self._maybeAddServer(writer, write=-1) |
|---|
| 203 |
|
|---|
| 204 |
def crash(self): |
|---|
| 205 |
gtk.main_quit() |
|---|
| 206 |
|
|---|
| 207 |
def run(self, installSignalHandlers=1): |
|---|
| 208 |
self.startRunning(installSignalHandlers=installSignalHandlers) |
|---|
| 209 |
self.simulate() |
|---|
| 210 |
gtk.main() |
|---|
| 211 |
|
|---|
| 212 |
|
|---|
| 213 |
def install(): |
|---|
| 214 |
"""Configure the twisted mainloop to be run inside the gtk mainloop. |
|---|
| 215 |
""" |
|---|
| 216 |
reactor = GladeReactor() |
|---|
| 217 |
from twisted.internet.main import installReactor |
|---|
| 218 |
installReactor(reactor) |
|---|
| 219 |
return reactor |
|---|