Open Media Library Platform
This commit is contained in:
commit
411ad5b16f
5849 changed files with 1778641 additions and 0 deletions
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
|
||||
"""Twisted Manhole UI: User interface for direct manipulation in Twisted.
|
||||
"""
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="manholeWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Manhole</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="default_width">620</property>
|
||||
<property name="default_height">320</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<signal name="delete_event" handler="_on_manholeWindow_delete_event" last_modification_time="Mon, 27 Jan 2003 05:14:26 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuBar" id="menubar1">
|
||||
<property name="visible">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem4_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="openMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="_on_openMenuItem_activate" last_modification_time="Sun, 02 Feb 2003 18:44:51 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="reload_self">
|
||||
<property name="visible">True</property>
|
||||
<property name="tooltip" translatable="yes">Reload the manhole client code. (Only useful for client development.)</property>
|
||||
<property name="label" translatable="yes">_Reload self</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_reload_self_activate" last_modification_time="Mon, 24 Feb 2003 00:15:10 GMT"/>
|
||||
|
||||
<child internal-child="image">
|
||||
<widget class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-revert-to-saved</property>
|
||||
<property name="icon_size">1</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="separatormenuitem1">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="quitMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="_on_quitMenuItem_activate" last_modification_time="Sun, 02 Feb 2003 18:48:12 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem5">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Edit</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem5_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="cut1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-cut</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_cut1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="copy1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-copy</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_copy1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="paste1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-paste</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_paste1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="delete1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_delete1_activate" last_modification_time="Mon, 27 Jan 2003 04:50:50 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem7">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem7_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="aboutMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_About</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="_on_aboutMenuItem_activate" last_modification_time="Thu, 06 Feb 2003 19:49:53 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVPaned" id="vpaned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="output">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="overwrite">False</property>
|
||||
<property name="accepts_tab">True</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_WORD</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="input">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="overwrite">False</property>
|
||||
<property name="accepts_tab">True</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_NONE</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkStatusbar" id="statusbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="has_resize_grip">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
# -*- test-case-name: twisted.manhole.ui.test.test_gtk2manhole -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Manhole client with a GTK v2.x front-end.
|
||||
"""
|
||||
|
||||
__version__ = '$Revision: 1.9 $'[11:-2]
|
||||
|
||||
from twisted import copyright
|
||||
from twisted.internet import reactor
|
||||
from twisted.python import components, failure, log, util
|
||||
from twisted.python.reflect import prefixedMethodNames
|
||||
from twisted.spread import pb
|
||||
from twisted.spread.ui import gtk2util
|
||||
|
||||
from twisted.manhole.service import IManholeClient
|
||||
from zope.interface import implements
|
||||
|
||||
# The pygtk.require for version 2.0 has already been done by the reactor.
|
||||
import gtk
|
||||
|
||||
import code, types, inspect
|
||||
|
||||
# TODO:
|
||||
# Make wrap-mode a run-time option.
|
||||
# Explorer.
|
||||
# Code doesn't cleanly handle opening a second connection. Fix that.
|
||||
# Make some acknowledgement of when a command has completed, even if
|
||||
# it has no return value so it doesn't print anything to the console.
|
||||
|
||||
class OfflineError(Exception):
|
||||
pass
|
||||
|
||||
class ManholeWindow(components.Componentized, gtk2util.GladeKeeper):
|
||||
gladefile = util.sibpath(__file__, "gtk2manhole.glade")
|
||||
|
||||
_widgets = ('input','output','manholeWindow')
|
||||
|
||||
def __init__(self):
|
||||
self.defaults = {}
|
||||
gtk2util.GladeKeeper.__init__(self)
|
||||
components.Componentized.__init__(self)
|
||||
|
||||
self.input = ConsoleInput(self._input)
|
||||
self.input.toplevel = self
|
||||
self.output = ConsoleOutput(self._output)
|
||||
|
||||
# Ugh. GladeKeeper actually isn't so good for composite objects.
|
||||
# I want this connected to the ConsoleInput's handler, not something
|
||||
# on this class.
|
||||
self._input.connect("key_press_event", self.input._on_key_press_event)
|
||||
|
||||
def setDefaults(self, defaults):
|
||||
self.defaults = defaults
|
||||
|
||||
def login(self):
|
||||
client = self.getComponent(IManholeClient)
|
||||
d = gtk2util.login(client, **self.defaults)
|
||||
d.addCallback(self._cbLogin)
|
||||
d.addCallback(client._cbLogin)
|
||||
d.addErrback(self._ebLogin)
|
||||
|
||||
def _cbDisconnected(self, perspective):
|
||||
self.output.append("%s went away. :(\n" % (perspective,), "local")
|
||||
self._manholeWindow.set_title("Manhole")
|
||||
|
||||
def _cbLogin(self, perspective):
|
||||
peer = perspective.broker.transport.getPeer()
|
||||
self.output.append("Connected to %s\n" % (peer,), "local")
|
||||
perspective.notifyOnDisconnect(self._cbDisconnected)
|
||||
self._manholeWindow.set_title("Manhole - %s" % (peer))
|
||||
return perspective
|
||||
|
||||
def _ebLogin(self, reason):
|
||||
self.output.append("Login FAILED %s\n" % (reason.value,), "exception")
|
||||
|
||||
def _on_aboutMenuItem_activate(self, widget, *unused):
|
||||
import sys
|
||||
from os import path
|
||||
self.output.append("""\
|
||||
a Twisted Manhole client
|
||||
Versions:
|
||||
%(twistedVer)s
|
||||
Python %(pythonVer)s on %(platform)s
|
||||
GTK %(gtkVer)s / PyGTK %(pygtkVer)s
|
||||
%(module)s %(modVer)s
|
||||
http://twistedmatrix.com/
|
||||
""" % {'twistedVer': copyright.longversion,
|
||||
'pythonVer': sys.version.replace('\n', '\n '),
|
||||
'platform': sys.platform,
|
||||
'gtkVer': ".".join(map(str, gtk.gtk_version)),
|
||||
'pygtkVer': ".".join(map(str, gtk.pygtk_version)),
|
||||
'module': path.basename(__file__),
|
||||
'modVer': __version__,
|
||||
}, "local")
|
||||
|
||||
def _on_openMenuItem_activate(self, widget, userdata=None):
|
||||
self.login()
|
||||
|
||||
def _on_manholeWindow_delete_event(self, widget, *unused):
|
||||
reactor.stop()
|
||||
|
||||
def _on_quitMenuItem_activate(self, widget, *unused):
|
||||
reactor.stop()
|
||||
|
||||
def on_reload_self_activate(self, *unused):
|
||||
from twisted.python import rebuild
|
||||
rebuild.rebuild(inspect.getmodule(self.__class__))
|
||||
|
||||
|
||||
tagdefs = {
|
||||
'default': {"family": "monospace"},
|
||||
# These are message types we get from the server.
|
||||
'stdout': {"foreground": "black"},
|
||||
'stderr': {"foreground": "#AA8000"},
|
||||
'result': {"foreground": "blue"},
|
||||
'exception': {"foreground": "red"},
|
||||
# Messages generate locally.
|
||||
'local': {"foreground": "#008000"},
|
||||
'log': {"foreground": "#000080"},
|
||||
'command': {"foreground": "#666666"},
|
||||
}
|
||||
|
||||
# TODO: Factor Python console stuff back out to pywidgets.
|
||||
|
||||
class ConsoleOutput:
|
||||
_willScroll = None
|
||||
def __init__(self, textView):
|
||||
self.textView = textView
|
||||
self.buffer = textView.get_buffer()
|
||||
|
||||
# TODO: Make this a singleton tag table.
|
||||
for name, props in tagdefs.iteritems():
|
||||
tag = self.buffer.create_tag(name)
|
||||
# This can be done in the constructor in newer pygtk (post 1.99.14)
|
||||
for k, v in props.iteritems():
|
||||
tag.set_property(k, v)
|
||||
|
||||
self.buffer.tag_table.lookup("default").set_priority(0)
|
||||
|
||||
self._captureLocalLog()
|
||||
|
||||
def _captureLocalLog(self):
|
||||
return log.startLogging(_Notafile(self, "log"), setStdout=False)
|
||||
|
||||
def append(self, text, kind=None):
|
||||
# XXX: It seems weird to have to do this thing with always applying
|
||||
# a 'default' tag. Can't we change the fundamental look instead?
|
||||
tags = ["default"]
|
||||
if kind is not None:
|
||||
tags.append(kind)
|
||||
|
||||
self.buffer.insert_with_tags_by_name(self.buffer.get_end_iter(),
|
||||
text, *tags)
|
||||
# Silly things, the TextView needs to update itself before it knows
|
||||
# where the bottom is.
|
||||
if self._willScroll is None:
|
||||
self._willScroll = gtk.idle_add(self._scrollDown)
|
||||
|
||||
def _scrollDown(self, *unused):
|
||||
self.textView.scroll_to_iter(self.buffer.get_end_iter(), 0,
|
||||
True, 1.0, 1.0)
|
||||
self._willScroll = None
|
||||
return False
|
||||
|
||||
class History:
|
||||
def __init__(self, maxhist=10000):
|
||||
self.ringbuffer = ['']
|
||||
self.maxhist = maxhist
|
||||
self.histCursor = 0
|
||||
|
||||
def append(self, htext):
|
||||
self.ringbuffer.insert(-1, htext)
|
||||
if len(self.ringbuffer) > self.maxhist:
|
||||
self.ringbuffer.pop(0)
|
||||
self.histCursor = len(self.ringbuffer) - 1
|
||||
self.ringbuffer[-1] = ''
|
||||
|
||||
def move(self, prevnext=1):
|
||||
'''
|
||||
Return next/previous item in the history, stopping at top/bottom.
|
||||
'''
|
||||
hcpn = self.histCursor + prevnext
|
||||
if hcpn >= 0 and hcpn < len(self.ringbuffer):
|
||||
self.histCursor = hcpn
|
||||
return self.ringbuffer[hcpn]
|
||||
else:
|
||||
return None
|
||||
|
||||
def histup(self, textbuffer):
|
||||
if self.histCursor == len(self.ringbuffer) - 1:
|
||||
si, ei = textbuffer.get_start_iter(), textbuffer.get_end_iter()
|
||||
self.ringbuffer[-1] = textbuffer.get_text(si,ei)
|
||||
newtext = self.move(-1)
|
||||
if newtext is None:
|
||||
return
|
||||
textbuffer.set_text(newtext)
|
||||
|
||||
def histdown(self, textbuffer):
|
||||
newtext = self.move(1)
|
||||
if newtext is None:
|
||||
return
|
||||
textbuffer.set_text(newtext)
|
||||
|
||||
|
||||
class ConsoleInput:
|
||||
toplevel, rkeymap = None, None
|
||||
__debug = False
|
||||
|
||||
def __init__(self, textView):
|
||||
self.textView=textView
|
||||
self.rkeymap = {}
|
||||
self.history = History()
|
||||
for name in prefixedMethodNames(self.__class__, "key_"):
|
||||
keysymName = name.split("_")[-1]
|
||||
self.rkeymap[getattr(gtk.keysyms, keysymName)] = keysymName
|
||||
|
||||
def _on_key_press_event(self, entry, event):
|
||||
ksym = self.rkeymap.get(event.keyval, None)
|
||||
|
||||
mods = []
|
||||
for prefix, mask in [('ctrl', gtk.gdk.CONTROL_MASK), ('shift', gtk.gdk.SHIFT_MASK)]:
|
||||
if event.state & mask:
|
||||
mods.append(prefix)
|
||||
|
||||
if mods:
|
||||
ksym = '_'.join(mods + [ksym])
|
||||
|
||||
if ksym:
|
||||
rvalue = getattr(
|
||||
self, 'key_%s' % ksym, lambda *a, **kw: None)(entry, event)
|
||||
|
||||
if self.__debug:
|
||||
print ksym
|
||||
return rvalue
|
||||
|
||||
def getText(self):
|
||||
buffer = self.textView.get_buffer()
|
||||
iter1, iter2 = buffer.get_bounds()
|
||||
text = buffer.get_text(iter1, iter2, False)
|
||||
return text
|
||||
|
||||
def setText(self, text):
|
||||
self.textView.get_buffer().set_text(text)
|
||||
|
||||
def key_Return(self, entry, event):
|
||||
text = self.getText()
|
||||
# Figure out if that Return meant "next line" or "execute."
|
||||
try:
|
||||
c = code.compile_command(text)
|
||||
except SyntaxError, e:
|
||||
# This could conceivably piss you off if the client's python
|
||||
# doesn't accept keywords that are known to the manhole's
|
||||
# python.
|
||||
point = buffer.get_iter_at_line_offset(e.lineno, e.offset)
|
||||
buffer.place(point)
|
||||
# TODO: Componentize!
|
||||
self.toplevel.output.append(str(e), "exception")
|
||||
except (OverflowError, ValueError), e:
|
||||
self.toplevel.output.append(str(e), "exception")
|
||||
else:
|
||||
if c is not None:
|
||||
self.sendMessage()
|
||||
# Don't insert Return as a newline in the buffer.
|
||||
self.history.append(text)
|
||||
self.clear()
|
||||
# entry.emit_stop_by_name("key_press_event")
|
||||
return True
|
||||
else:
|
||||
# not a complete code block
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def key_Up(self, entry, event):
|
||||
# if I'm at the top, previous history item.
|
||||
textbuffer = self.textView.get_buffer()
|
||||
if textbuffer.get_iter_at_mark(textbuffer.get_insert()).get_line() == 0:
|
||||
self.history.histup(textbuffer)
|
||||
return True
|
||||
return False
|
||||
|
||||
def key_Down(self, entry, event):
|
||||
textbuffer = self.textView.get_buffer()
|
||||
if textbuffer.get_iter_at_mark(textbuffer.get_insert()).get_line() == (
|
||||
textbuffer.get_line_count() - 1):
|
||||
self.history.histdown(textbuffer)
|
||||
return True
|
||||
return False
|
||||
|
||||
key_ctrl_p = key_Up
|
||||
key_ctrl_n = key_Down
|
||||
|
||||
def key_ctrl_shift_F9(self, entry, event):
|
||||
if self.__debug:
|
||||
import pdb; pdb.set_trace()
|
||||
|
||||
def clear(self):
|
||||
buffer = self.textView.get_buffer()
|
||||
buffer.delete(*buffer.get_bounds())
|
||||
|
||||
def sendMessage(self):
|
||||
buffer = self.textView.get_buffer()
|
||||
iter1, iter2 = buffer.get_bounds()
|
||||
text = buffer.get_text(iter1, iter2, False)
|
||||
self.toplevel.output.append(pythonify(text), 'command')
|
||||
# TODO: Componentize better!
|
||||
try:
|
||||
return self.toplevel.getComponent(IManholeClient).do(text)
|
||||
except OfflineError:
|
||||
self.toplevel.output.append("Not connected, command not sent.\n",
|
||||
"exception")
|
||||
|
||||
|
||||
def pythonify(text):
|
||||
'''
|
||||
Make some text appear as though it was typed in at a Python prompt.
|
||||
'''
|
||||
lines = text.split('\n')
|
||||
lines[0] = '>>> ' + lines[0]
|
||||
return '\n... '.join(lines) + '\n'
|
||||
|
||||
class _Notafile:
|
||||
"""Curry to make failure.printTraceback work with the output widget."""
|
||||
def __init__(self, output, kind):
|
||||
self.output = output
|
||||
self.kind = kind
|
||||
|
||||
def write(self, txt):
|
||||
self.output.append(txt, self.kind)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
class ManholeClient(components.Adapter, pb.Referenceable):
|
||||
implements(IManholeClient)
|
||||
|
||||
capabilities = {
|
||||
# "Explorer": 'Set',
|
||||
"Failure": 'Set'
|
||||
}
|
||||
|
||||
def _cbLogin(self, perspective):
|
||||
self.perspective = perspective
|
||||
perspective.notifyOnDisconnect(self._cbDisconnected)
|
||||
return perspective
|
||||
|
||||
def remote_console(self, messages):
|
||||
for kind, content in messages:
|
||||
if isinstance(content, types.StringTypes):
|
||||
self.original.output.append(content, kind)
|
||||
elif (kind == "exception") and isinstance(content, failure.Failure):
|
||||
content.printTraceback(_Notafile(self.original.output,
|
||||
"exception"))
|
||||
else:
|
||||
self.original.output.append(str(content), kind)
|
||||
|
||||
def remote_receiveExplorer(self, xplorer):
|
||||
pass
|
||||
|
||||
def remote_listCapabilities(self):
|
||||
return self.capabilities
|
||||
|
||||
def _cbDisconnected(self, perspective):
|
||||
self.perspective = None
|
||||
|
||||
def do(self, text):
|
||||
if self.perspective is None:
|
||||
raise OfflineError
|
||||
return self.perspective.callRemote("do", text)
|
||||
|
||||
components.registerAdapter(ManholeClient, ManholeWindow, IManholeClient)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) 2009 Twisted Matrix Laboratories.
|
||||
"""
|
||||
Tests for the L{twisted.manhole.ui} package.
|
||||
"""
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# Copyright (c) 2009 Twisted Matrix Laboratories.
|
||||
"""
|
||||
Tests for GTK2 GUI manhole.
|
||||
"""
|
||||
|
||||
skip = False
|
||||
|
||||
try:
|
||||
import pygtk
|
||||
pygtk.require("2.0")
|
||||
except:
|
||||
skip = "GTK 2.0 not available"
|
||||
else:
|
||||
try:
|
||||
import gtk
|
||||
except ImportError:
|
||||
skip = "GTK 2.0 not available"
|
||||
except RuntimeError:
|
||||
skip = "Old version of GTK 2.0 requires DISPLAY, and we don't have one."
|
||||
else:
|
||||
if gtk.gtk_version[0] == 1:
|
||||
skip = "Requested GTK 2.0, but 1.0 was already imported."
|
||||
else:
|
||||
from twisted.manhole.ui.gtk2manhole import ConsoleInput
|
||||
|
||||
from twisted.trial.unittest import TestCase
|
||||
|
||||
from twisted.python.reflect import prefixedMethodNames
|
||||
|
||||
class ConsoleInputTests(TestCase):
|
||||
"""
|
||||
Tests for L{ConsoleInput}.
|
||||
"""
|
||||
|
||||
def test_reverseKeymap(self):
|
||||
"""
|
||||
Verify that a L{ConsoleInput} has a reverse mapping of the keysym names
|
||||
it needs for event handling to their corresponding keysym.
|
||||
"""
|
||||
ci = ConsoleInput(None)
|
||||
for eventName in prefixedMethodNames(ConsoleInput, 'key_'):
|
||||
keysymName = eventName.split("_")[-1]
|
||||
keysymValue = getattr(gtk.keysyms, keysymName)
|
||||
self.assertEqual(ci.rkeymap[keysymValue], keysymName)
|
||||
|
||||
|
||||
skip = skip
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue