from __future__ import generators import dottest import pygame from pygame.locals import * class Graph: Width = 640 Height = 480 FontFile = None FontSize = 20 StepX = 120 StepY = 32 Cx = (Width - 3*StepX) // 2 Cy = Height // 2 - 3*StepY def __init__(self, root): pygame.init() self.screen = pygame.display.set_mode((self.Width, self.Height)) self.font = pygame.font.Font(self.FontFile, self.FontSize) self.font.set_bold(0) self.cursor = (30, self.Height//7) if not isinstance(root, Box): root = enter(root) self.root = root self.VisibleMax = max(self.Width // self.StepX, self.Height // self.StepY) + 1 def position(self, (x, y)): return self.Cx+x*self.StepX, self.Cy+y*self.StepY def draw(self): root = self.root() for box, (x, y) in root.visibleboxes(self.VisibleMax): px, py = self.position((x, y)) box.draw(self, px, py, x == y == 0) self.flip() def flip(self): #self.screen.fill((255, 0, 0), # (0, self.Height-self.StepY, self.Width, self.StepY)) pygame.display.flip() self.screen.fill((0, 0, 0)) def changeroot(self, newroot, steps=7): if newroot is not None: oldroot = self.root() self.root = newroot newroot = self.root() oldboxes = dict(oldroot.visibleboxes(self.VisibleMax)) newboxes = dict(newroot.visibleboxes(self.VisibleMax)) movement = {} for box in oldboxes: if box in newboxes: movement[box] = (self.position(oldboxes[box]), self.position(newboxes[box])) movement1 = movement.items() commonboxes = movement.copy() for dir in ['left', 'up', 'right', 'down']: for box in commonboxes: box1 = getattr(box, dir)() while box1 is not None and box1 not in movement: if box1 in oldboxes: mov = (self.position(oldboxes[box1]), self.position(newboxes[box])) elif box1 in newboxes: mov = (self.position(oldboxes[box]), self.position(newboxes[box1])) else: break movement[box1] = mov movement1.append((box1, mov)) box1 = getattr(box1, dir)() movement1.reverse() for t in range(steps): if t&1 and pygame.event.peek([KEYDOWN]): continue f2 = float(t) / steps f1 = 1.0 - f2 for box, ((x1, y1), (x2, y2)) in movement1: x = int(x1*f1 + x2*f2) y = int(y1*f1 + y2*f2) box.draw(self, x, y, box == newroot, box in commonboxes) self.flip() self.draw() def main(self): self.draw() while 1: event = pygame.event.wait() if event.type == KEYDOWN: if event.key == K_DOWN: self.changeroot(self.root().down()) elif event.key == K_RIGHT: self.changeroot(self.root().right()) elif event.key == K_UP: self.changeroot(self.root().up()) elif event.key == K_LEFT: self.changeroot(self.root().left()) elif event.key == K_ESCAPE: raise SystemExit elif event.type == QUIT: raise SystemExit def enter_simple(obj): return Box(repr(obj)) def enter_tuple(obj): color = (96,96,255) if obj: boxes = [HBox('(...) [%d]' % i, color) for i in range(len(obj))] for i in range(len(boxes)-1): boxes[i].set_right(boxes[i+1]) for i in range(len(boxes)): boxes[i].set_down(enter(obj[i])) return boxes[0] else: return Box('()', color) def enter_list(obj): color = (255,96,96) if obj: boxes = [HBox('[...] [%d]' % i, color) for i in range(len(obj))] for i in range(len(boxes)-1): boxes[i].set_right(boxes[i+1]) for i in range(len(boxes)): boxes[i].set_down(enter(obj[i])) return boxes[0] else: return Box('[]', color) def enter_dict(obj): color = (96,255,96) if obj: items = obj.items() items.sort() boxes = [HBox('%r:' % (items[i][0],), color) for i in range(len(items))] for i in range(len(boxes)-1): boxes[i].set_right(boxes[i+1]) for i in range(len(boxes)): boxes[i].set_down(enter(items[i][1])) return boxes[0] else: return Box('{}', color) def enter_default(obj): color = (255,255,255) attrcolor = (192,255,192) header = Box(getattr(obj, '__class__', type(obj)).__name__, color) try: dict = obj.__dict__ except AttributeError: pass else: if dict: items = dict.items() items.sort() boxes = [Box('. %s' % items[i][0], attrcolor) for i in range(len(items))] for i in range(len(boxes)-1): boxes[i].set_down(boxes[i+1]) for i in range(len(boxes)): boxes[i].set_right(enter(items[i][1])) header.set_down(boxes[0]) return header Enterers = { bool: enter_simple, int: enter_simple, str: enter_simple, type(None): enter_simple, tuple: enter_tuple, list: enter_list, dict: enter_dict, } class enter: left = up = lambda self: None def __init__(self, obj): self.obj = obj self.box = None def __call__(self): if self.box is None: enter1 = Enterers.get(type(self.obj), enter_default) box = enter1(self.obj) box.left = self.left box.up = self.up self.box = box return self.box class Box: MarginX = 16 MarginY = 8 left = up = right = down = lambda self: None def __init__(self, text, bgcolor=(160,160,160), fgcolor=(0,0,0)): self.text = text self.bgcolor = bgcolor self.fgcolor = fgcolor def __call__(self): return self def draw(self, graph, x, y, highlight=0, link=1): linkcolor = (128,128,96) if highlight: fgcolor = (0,0,0) bgcolor = (255,0,0) else: fgcolor = self.fgcolor bgcolor = self.bgcolor text = self.text[:100] s = graph.font.render(text, 1, fgcolor, bgcolor) Width = graph.StepX - self.MarginX Height = graph.StepY - self.MarginY screen = graph.screen screen.set_clip((x, y, Width, Height)) screen.fill(bgcolor) screen.blit(s, (x+8, y+2)) screen.set_clip() if link: if self.up() is not None: screen.fill(linkcolor, (x+Width//2-1, y-self.MarginY, 3, self.MarginY)) if self.left() is not None: screen.fill(linkcolor, (x-self.MarginX, y+Height//2-1, self.MarginX, 3)) if self.down() is not None: screen.fill(linkcolor, (x+Width//2-1, y+Height, 3, 2)) if self.right() is not None: screen.fill(linkcolor, (x+Width, y+Height//2-1, 5, 3)) def set_right(self, other): self.right = other other.left = self def set_down(self, other): self.down = other other.up = self def visibleboxes(self, visible_max): box = self y = 0 while box.up() is not None and y + visible_max > 0: box = box.up() y -= 1 while box is not None and y < visible_max: x = 0 box1 = box while box1.left() is not None and x + visible_max > 0: box1 = box1.left() x -= 1 while box1 is not None and x < visible_max: yield box1, (x, y) box1 = box1.right() x += 1 box = box.down() y += 1 class HBox(Box): def visibleboxes(self, visible_max): box = self x = 0 while box.left() is not None and x + visible_max > 0: box = box.left() x -= 1 while box is not None and x < visible_max: y = 0 box1 = box while box1.up() is not None and y + visible_max > 0: box1 = box1.up() y -= 1 while box1 is not None and y < visible_max: yield box1, (x, y) box1 = box1.down() y += 1 box = box.right() x += 1 if __name__ == '__main__': #b = Box('hello') #b.right = [Box('something')] + range(20) import compiler, sys p = compiler.parse(open(sys.argv[0]).read()) g = Graph(p) g.main()