Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: third_party/twisted_8_1/twisted/manhole/ui/spelunk_gnome.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- Python -*-
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """Object browser GUI, GnomeCanvas implementation.
7 """
8
9 from twisted.python import log
10
11 # TODO:
12 # gzigzag-style navigation
13
14 class SillyModule:
15 def __init__(self, module, prefix):
16 self.__module = module
17 self.__prefix = prefix
18
19 def __getattr__(self, attr):
20 try:
21 return getattr(self.__module, self.__prefix + attr)
22 except AttributeError:
23 return getattr(self.__module, attr)
24
25
26 # We use gnome.ui because that's what happens to have Python bindings
27 # for the Canvas. I think this canvas widget is available seperately
28 # in "libart", but nobody's given me Python bindings for just that.
29
30 # The Gnome canvas is said to be modeled after the Tk canvas, so we
31 # could probably write this in Tk too. But my experience is with GTK,
32 # not with Tk, so this is what I use.
33
34 import gnome.ui
35 gnome = SillyModule(gnome.ui, 'Gnome')
36
37 import gtk
38 (True, False) = (gtk.TRUE, gtk.FALSE)
39 gtk = SillyModule(gtk, 'Gtk')
40
41 import GDK
42
43 from twisted.python import reflect, text
44 from twisted.spread import pb
45 from twisted.manhole import explorer
46
47 import string, sys, types
48 import UserList
49 _PIXELS_PER_UNIT=10
50
51 #### Support class.
52
53 class PairList(UserList.UserList):
54 """An ordered list of key, value pairs.
55
56 Kinda like an ordered dictionary. Made with small data sets
57 in mind, as get() does a linear search, not hashing.
58 """
59 def get(self, key):
60 i = 0
61 for k, v in self.data:
62 if key == k:
63 return (i, v)
64 i = i + 1
65 else:
66 return (None, None)
67
68 def keys(self):
69 return map(lambda x: x[0], self.data)
70
71
72 #### Public
73
74 class SpelunkDisplay(gnome.Canvas):
75 """Spelunk widget.
76
77 The top-level widget for this module. This gtk.Widget is where the
78 explorer display will be, and this object is also your interface to
79 creating new visages.
80 """
81 def __init__(self, aa=False):
82 gnome.Canvas.__init__(self, aa)
83 self.set_pixels_per_unit(_PIXELS_PER_UNIT)
84 self.visages = {}
85
86 def makeDefaultCanvas(self):
87 """Make myself the default canvas which new visages are created on.
88 """
89 # XXX: For some reason, the 'canvas' and 'parent' properties
90 # of CanvasItems aren't accessible thorugh pygnome.
91 Explorer.canvas = self
92
93 def receiveExplorer(self, xplorer):
94 if self.visages.has_key(xplorer.id):
95 log.msg("Using cached visage for %d" % (xplorer.id, ))
96 # Ikk. Just because we just received this explorer, that
97 # doesn't necessarily mean its attributes are fresh. Fix
98 # that, either by having this side pull or the server
99 # side push.
100 visage = self.visages[xplorer.id]
101 #xplorer.give_properties(visage)
102 #xplorer.give_attributes(visage)
103 else:
104 log.msg("Making new visage for %d" % (xplorer.id, ))
105 self.visages[xplorer.id] = xplorer.newVisage(self.root(),
106 self)
107
108 #### Base classes
109
110 class Explorer(pb.RemoteCache):
111 """Base class for all RemoteCaches of explorer.Explorer cachables.
112
113 Meaning that when an Explorer comes back over the wire, one of
114 these is created. From this, you can make a Visage for the
115 SpelunkDisplay, or a widget to display as an Attribute.
116 """
117 canvas = None
118 # From our cache:
119 id = None
120 identifier = None
121 explorerClass = None
122 attributeGroups = None
123
124 def newVisage(self, group, canvas=None):
125 """Make a new visage for the object I explore.
126
127 Returns a Visage.
128 """
129 canvas = canvas or self.canvas
130 klass = spelunkerClassTable.get(self.explorerClass, None)
131 if (not klass) or (klass[0] is None):
132 log.msg("%s not in table, using generic" % self.explorerClass)
133 klass = GenericVisage
134 else:
135 klass = klass[0]
136 spelunker = klass(self, group, canvas)
137 if hasattr(canvas, "visages") \
138 and not canvas.visages.has_key(self.id):
139 canvas.visages[self.id] = spelunker
140
141 self.give_properties(spelunker)
142
143 self.give_attributes(spelunker)
144
145 return spelunker
146
147 def newAttributeWidget(self, group):
148 """Make a new attribute item for my object.
149
150 Returns a gtk.Widget.
151 """
152 klass = spelunkerClassTable.get(self.explorerClass, None)
153 if (not klass) or (klass[1] is None):
154 log.msg("%s not in table, using generic" % self.explorerClass)
155 klass = GenericAttributeWidget
156 else:
157 klass = klass[1]
158
159 return klass(self, group)
160
161 def give_properties(self, spelunker):
162 """Give a spelunker my properties in an ordered list.
163 """
164 valuelist = PairList()
165 for p in spelunker.propertyLabels.keys():
166 value = getattr(self, p, None)
167 valuelist.append((p,value))
168 spelunker.fill_properties(valuelist)
169
170 def give_attributes(self, spelunker):
171 for a in spelunker.groupLabels.keys():
172 things = getattr(self, a)
173 spelunker.fill_attributeGroup(a, things)
174
175 class _LooseBoxBorder:
176 box = None
177 color = 'black'
178 width = 1
179 def __init__(self, box):
180 self.box = box
181
182 class LooseBox(gnome.CanvasGroup):
183 def __init__(self):
184 self.border = _LooseBoxBorder(self)
185
186 class Visage(gnome.CanvasGroup):
187 """A \"face\" of an object under exploration.
188
189 A Visage is a representation of an object presented to the user.
190 The \"face\" in \"interface\".
191
192 'propertyLabels' and 'groupLabels' are lists of (key, name)
193 2-ples, with 'key' being the string the property or group is
194 denoted by in the code, and 'name' being the pretty human-readable
195 string you want me to show on the Visage. These attributes are
196 accumulated from base classes as well.
197
198 I am a gnome.CanvasItem (more specifically, CanvasGroup).
199 """
200 color = {'border': '#006644'}
201 border_width = 8
202 detail_level = 0
203 # These are mappings from the strings the code calls these by
204 # and the pretty names you want to see on the screen.
205 # (e.g. Capitalized or localized)
206 propertyLabels = []
207 groupLabels = []
208
209 drag_x0 = 0
210 drag_y0 = 0
211
212 def __init__(self, explorer, rootGroup, canvas):
213 """Place a new Visage of an explorer in a canvas group.
214
215 I also need a 'canvas' reference is for certain coordinate
216 conversions, and pygnome doesn't give access to my GtkObject's
217 .canvas attribute. :(
218 """
219 # Ugh. PyGtk/GtkObject/GnomeCanvas interfacing grits.
220 gnome.CanvasGroup.__init__(self,
221 _obj = rootGroup.add('group')._o)
222
223 self.propertyLabels = PairList()
224 reflect.accumulateClassList(self.__class__, 'propertyLabels',
225 self.propertyLabels)
226 self.groupLabels = PairList()
227 reflect.accumulateClassList(self.__class__, 'groupLabels',
228 self.groupLabels)
229
230 self.explorer = explorer
231 self.identifier = explorer.identifier
232 self.objectId = explorer.id
233
234 self.canvas = canvas
235 self.rootGroup = rootGroup
236
237 self.ebox = gtk.EventBox()
238 self.ebox.set_name("Visage")
239 self.frame = gtk.Frame(self.identifier)
240 self.container = gtk.VBox()
241 self.ebox.add(self.frame)
242 self.frame.add(self.container)
243
244 self.canvasWidget = self.add('widget', widget=self.ebox,
245 x=0, y=0, anchor=gtk.ANCHOR_NW,
246 size_pixels=0)
247
248 self.border = self.add('rect', x1=0, y1=0,
249 x2=1, y2=1,
250 fill_color=None,
251 outline_color=self.color['border'],
252 width_pixels=self.border_width)
253
254 self.subtable = {}
255
256 self._setup_table()
257
258 # TODO:
259 # Collapse me
260 # Movable/resizeable me
261 # Destroy me
262 # Set my detail level
263
264 self.frame.connect("size_allocate", self.signal_size_allocate,
265 None)
266 self.connect("destroy", self.signal_destroy, None)
267 self.connect("event", self.signal_event)
268
269 self.ebox.show_all()
270
271 # Our creator will call our fill_ methods when she has the goods.
272
273 def _setup_table(self):
274 """Called by __init__ to set up my main table.
275
276 You can easily override me instead of clobbering __init__.
277 """
278
279 table = gtk.Table(len(self.propertyLabels), 2)
280 self.container.add(table)
281 table.set_name("PropertyTable")
282 self.subtable['properties'] = table
283 row = 0
284
285 for p, name in self.propertyLabels:
286 label = gtk.Label(name)
287 label.set_name("PropertyName")
288 label.set_data("property", p)
289 table.attach(label, 0, 1, row, row + 1)
290 label.set_alignment(0, 0)
291 row = row + 1
292
293 # XXX: make these guys collapsable
294 for g, name in self.groupLabels:
295 table = gtk.Table(1, 2)
296 self.container.add(table)
297 table.set_name("AttributeGroupTable")
298 self.subtable[g] = table
299 label = gtk.Label(name)
300 label.set_name("AttributeGroupTitle")
301 table.attach(label, 0, 2, 0, 1)
302
303 def fill_properties(self, propValues):
304 """Fill in values for my properites.
305
306 Takes a list of (name, value) pairs. 'name' should be one of
307 the keys in my propertyLabels, and 'value' either an Explorer
308 or a string.
309 """
310 table = self.subtable['properties']
311
312 table.resize(len(propValues), 2)
313
314 # XXX: Do I need to destroy previously attached children?
315
316 for name, value in propValues:
317 self.fill_property(name, value)
318
319 table.show_all()
320
321 def fill_property(self, property, value):
322 """Set a value for a particular property.
323
324 'property' should be one of the keys in my propertyLabels.
325 """
326 row, name = self.propertyLabels.get(property)
327 if type(value) is not types.InstanceType:
328 widget = gtk.Label(str(value))
329 widget.set_alignment(0, 0)
330 else:
331 widget = value.newAttributeWidget(self)
332 widget.set_name("PropertyValue")
333
334 self.subtable['properties'].attach(widget, 1, 2, row, row+1)
335
336 def fill_attributeGroup(self, group, attributes):
337 """Provide members of an attribute group.
338
339 'group' should be one of the keys in my groupLabels, and
340 'attributes' a list of (name, value) pairs, with each value as
341 either an Explorer or string.
342 """
343
344 # XXX: How to indicate detail level of members?
345
346 table = self.subtable[group]
347 if not attributes:
348 table.hide()
349 return
350
351 table.resize(len(attributes)+1, 2)
352
353 # XXX: Do I need to destroy previously attached children?
354
355 row = 1 # 0 is title
356
357 for name, value in attributes.items():
358 label = gtk.Label(name)
359 label.set_name("AttributeName")
360 label.set_alignment(0, 0)
361
362 if type(value) is types.StringType:
363 widget = gtk.Label(value)
364 widget.set_alignment(0, 0)
365 else:
366 widget = value.newAttributeWidget(self)
367
368 table.attach(label, 0, 1, row, row + 1)
369 table.attach(widget, 1, 2, row, row + 1)
370 row = row + 1
371
372 table.show_all()
373
374 def signal_event(self, widget, event=None):
375 if not event:
376 log.msg("Huh? got event signal with no event.")
377 return
378 if event.type == GDK.BUTTON_PRESS:
379 if event.button == 1:
380 self.drag_x0, self.drag_y0 = event.x, event.y
381 return True
382 elif event.type == GDK.MOTION_NOTIFY:
383 if event.state & GDK.BUTTON1_MASK:
384 self.move(event.x - self.drag_x0, event.y - self.drag_y0)
385 self.drag_x0, self.drag_y0 = event.x, event.y
386 return True
387 return False
388
389 def signal_size_allocate(self, frame_widget,
390 unusable_allocation, unused_data):
391 (x, y, w, h) = frame_widget.get_allocation()
392
393 # XXX: allocation PyCObject is apparently unusable!
394 # (w, h) = allocation.width, allocation.height
395
396 w, h = (float(w)/_PIXELS_PER_UNIT, float(h)/_PIXELS_PER_UNIT)
397
398 x1, y1 = (self.canvasWidget['x'], self.canvasWidget['y'])
399
400 b = self.border
401 (b['x1'], b['y1'], b['x2'], b['y2']) = (x1, y1, x1+w, y1+h)
402
403 def signal_destroy(self, unused_object, unused_data):
404 del self.explorer
405
406 del self.canvasWidget
407 del self.border
408
409 del self.ebox
410 del self.frame
411 del self.container
412
413 self.subtable.clear()
414
415
416 class AttributeWidget(gtk.Widget):
417 """A widget briefly describing an object.
418
419 This is similar to a Visage, but has far less detail. This should
420 display only essential identifiying information, a gtk.Widget
421 suitable for including in a single table cell.
422
423 (gtk.Widgets are used here instead of the more graphically
424 pleasing gnome.CanvasItems because I was too lazy to re-write
425 gtk.table for the canvas. A new table widget/item would be great
426 though, not only for canvas prettiness, but also because we could
427 use one with a mone pythonic API.)
428
429 """
430 def __init__(self, explorer, parent):
431 """A new AttributeWidget describing an explorer.
432 """
433 self.parent = parent
434
435 self.explorer = explorer
436 self.identifier = explorer.identifier
437 self.id = explorer.id
438
439 widgetObj = self._makeWidgetObject()
440 gtk.Widget.__init__(self, _obj=widgetObj)
441 self.set_name("AttributeValue")
442 self.connect("destroy", self.signal_destroy, None)
443 self.connect("button-press-event", self.signal_buttonPressEvent,
444 None)
445
446 def getTextForLabel(self):
447 """Returns text for my label.
448
449 The default implementation of AttributeWidget is a gtk.Label
450 widget. You may override this method to change the text which
451 appears in the label. However, if you don't want to be a
452 label, override _makeWidgetObject instead.
453 """
454 return self.identifier
455
456 def _makeWidgetObject(self):
457 """Make the GTK widget object that is me.
458
459 Called by __init__ to construct the GtkObject I wrap-- the ._o
460 member of a pygtk GtkObject. Isn't subclassing GtkObjects in
461 Python fun?
462 """
463 ebox = gtk.EventBox()
464 label = gtk.Label(self.getTextForLabel())
465 label.set_alignment(0,0)
466 ebox.add(label)
467 return ebox._o
468
469 def signal_destroy(self, unused_object, unused_data):
470 del self.explorer
471
472 def signal_buttonPressEvent(self, widget, eventButton, unused_data):
473 if eventButton.type == GDK._2BUTTON_PRESS:
474 if self.parent.canvas.visages.has_key(self.explorer.id):
475 visage = self.parent.canvas.visages[self.explorer.id]
476 else:
477 visage = self.explorer.newVisage(self.parent.rootGroup,
478 self.parent.canvas)
479 (x, y, w, h) = self.get_allocation()
480 wx, wy = self.parent.canvas.c2w(x, y)
481
482 x1, y1, x2, y2 = self.parent.get_bounds()
483
484 v_x1, v_y1, v_x2, v_y2 = visage.get_bounds()
485
486 visage.move(x2 - v_x1, wy + y1 - v_y1)
487
488
489 #### Widget-specific subclasses of Explorer, Visage, and Attribute
490
491 # Instance
492
493 class ExplorerInstance(Explorer):
494 pass
495
496 class InstanceVisage(Visage):
497 # Detail levels:
498 # Just me
499 # me and my class
500 # me and my whole class heirarchy
501
502 propertyLabels = [('klass', "Class")]
503 groupLabels = [('data', "Data"),
504 ('methods', "Methods")]
505
506 detail = 0
507
508 def __init__(self, explorer, group, canvas):
509 Visage.__init__(self, explorer, group, canvas)
510
511 class_identifier = self.explorer.klass.name
512 # XXX: include partial module name in class?
513 self.frame.set_label("%s (%s)" % (self.identifier,
514 class_identifier))
515
516 class InstanceAttributeWidget(AttributeWidget):
517 def getTextForLabel(self):
518 return "%s instance" % (self.explorer.klass.name,)
519
520
521 # Class
522
523 class ExplorerClass(Explorer):
524 pass
525
526 class ClassVisage(Visage):
527 propertyLabels = [("name", "Name"),
528 ("module", "Module"),
529 ("bases", "Bases")]
530 groupLabels = [('data', "Data"),
531 ('methods', "Methods")]
532
533 def fill_properties(self, propValues):
534 Visage.fill_properties(self, propValues)
535 basesExplorer = propValues.get('bases')[1]
536 basesExplorer.view.callRemote("get_elements").addCallback(self.fill_base s)
537
538 def fill_bases(self, baseExplorers):
539 box = gtk.HBox()
540 for b in baseExplorers:
541 box.add(b.newAttributeWidget(self))
542 row = self.propertyLabels.get('bases')[0]
543 self.subtable["properties"].attach(box, 1, 2, row, row+1)
544 box.show_all()
545
546 class ClassAttributeWidget(AttributeWidget):
547 def getTextForLabel(self):
548 return self.explorer.name
549
550
551 # Function
552
553 class ExplorerFunction(Explorer):
554 pass
555
556 class FunctionAttributeWidget(AttributeWidget):
557 def getTextForLabel(self):
558 signature = self.explorer.signature
559 arglist = []
560 for arg in xrange(len(signature)):
561 name = signature.name[arg]
562 hasDefault, default = signature.get_default(arg)
563 if hasDefault:
564 if default.explorerClass == "ExplorerImmutable":
565 default = default.value
566 else:
567 # XXX
568 pass
569 a = "%s=%s" % (name, default)
570 elif signature.is_varlist(arg):
571 a = "*%s" % (name,)
572 elif signature.is_keyword(arg):
573 a = "**%s" % (name,)
574 else:
575 a = name
576 arglist.append(a)
577
578 return string.join(arglist, ", ")
579
580
581 # Method
582
583 class ExplorerMethod(ExplorerFunction):
584 pass
585
586 class MethodAttributeWidget(FunctionAttributeWidget):
587 pass
588
589 class ExplorerBulitin(Explorer):
590 pass
591
592 class ExplorerModule(Explorer):
593 pass
594
595 class ExplorerSequence(Explorer):
596 pass
597
598
599 # Sequence
600
601 class SequenceVisage(Visage):
602 propertyLabels = [('len', 'length')]
603 # XXX: add elements group
604
605 class SequenceAttributeWidget(AttributeWidget):
606 def getTextForLabel(self):
607 # XXX: Differentiate between lists and tuples.
608 if self.explorer.len:
609 txt = "list of length %d" % (self.explorer.len,)
610 else:
611 txt = "[]"
612 return txt
613
614
615 # Mapping
616
617 class ExplorerMapping(Explorer):
618 pass
619
620 class MappingVisage(Visage):
621 propertyLabels = [('len', 'length')]
622 # XXX: add items group
623
624 class MappingAttributeWidget(AttributeWidget):
625 def getTextForLabel(self):
626 if self.explorer.len:
627 txt = "dict with %d elements" % (self.explorer.len,)
628 else:
629 txt = "{}"
630 return txt
631
632 class ExplorerImmutable(Explorer):
633 pass
634
635
636 # Immutable
637
638 class ImmutableVisage(Visage):
639 def __init__(self, explorer, rootGroup, canvas):
640 Visage.__init__(self, explorer, rootGroup, canvas)
641 widget = explorer.newAttributeWidget(self)
642 self.container.add(widget)
643 self.container.show_all()
644
645 class ImmutableAttributeWidget(AttributeWidget):
646 def getTextForLabel(self):
647 return repr(self.explorer.value)
648
649
650 #### misc. module definitions
651
652 spelunkerClassTable = {
653 "ExplorerInstance": (InstanceVisage, InstanceAttributeWidget),
654 "ExplorerFunction": (None, FunctionAttributeWidget),
655 "ExplorerMethod": (None, MethodAttributeWidget),
656 "ExplorerImmutable": (ImmutableVisage, ImmutableAttributeWidget),
657 "ExplorerClass": (ClassVisage, ClassAttributeWidget),
658 "ExplorerSequence": (SequenceVisage, SequenceAttributeWidget),
659 "ExplorerMapping": (MappingVisage, MappingAttributeWidget),
660 }
661 GenericVisage = Visage
662 GenericAttributeWidget = AttributeWidget
663
664 pb.setCopierForClassTree(sys.modules[__name__],
665 Explorer, 'twisted.manhole.explorer')
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/manhole/ui/pywidgets.py ('k') | third_party/twisted_8_1/twisted/names/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698