from graphdisplay import GraphDisplay from drivers import PyGameDriver from render import MyGraphRenderer import pygame # TODO BAD BAD class View: SCALEMAX = 100 SCALEMIN = 1 def __init__ (self, display): self.display = display self.margin = 0.6 self.setscale (75) self.setoffset (0, 0) def calculate_zoom_to_fit (self, view): width, height = self.display.size return min(float(width) / view.width, float(height) / view.height, float(view.SCALEMAX) / view.scale) def setoffset (self, offsetx, offsety): "Set the (x,y) origin of the rectangle where the graph will be rendered." self.ofsx = offsetx self.ofsy = offsety #ofsx = property (lambda self, off: off - self.margin ) #ofsy = property (lambda self, off: off - self.margin ) def setscale (self, scale): scale = max(min(scale, self.SCALEMAX), self.SCALEMIN) self.scale = float(scale) self.bboxh = self.display.size[1] #margin = property (lambda self: int(self.MARGIN * self.scale)) width = property (lambda self: int(self.display.size[0] * self.scale) + (2 * self.margin)) height = property (lambda self: int(self.display.size[1] * self.scale) + (2 * self.margin)) def map (self, x, y): "" mx = (x * self.scale) + 2*self.margin*self.scale my = (self.display.size[1] - y* self.scale) - 2 * self.margin*self.scale return int(mx), int(my) def shiftoffset(self, dx, dy): self.ofsx += dx self.ofsy += dy def reoffset(self, swidth, sheight): offsetx = noffsetx = self.ofsx offsety = noffsety = self.ofsy width = self.width height = self.height # if it fits, center it, otherwise clamp if width <= swidth: noffsetx = (width - swidth) // 2 else: noffsetx = min(max(0, offsetx), width - swidth) if height <= sheight: noffsety = (height - sheight) // 2 else: noffsety = min(max(0, offsety), height - sheight) self.ofsx = noffsetx self.ofsy = noffsety def shiftscale (self, factor, fix=None): if fix is None: fixx, fixy = self.display.size fixx //= 2 fixy //= 2 else: fixx, fixy = fix x, y = self.revmap(fixx, fixy) w, h = self.display.size self.setscale(self.scale * factor, w, h) newx, newy = self.map(x, y) self.shiftoffset(newx - fixx, newy - fixy) def calculate_scale_to_fit (self, (width,height)): maxw, maxh = self.display.size scale = min(float(maxw) / width, float(maxh) / height) self.setscale (scale) def revmap(self, px, py): return ((px + (self.ofsx - self.margin)) / self.scale, self.bboxh - (py + (self.ofsy - self.margin)) / self.scale) def getboundingbox(self): "Get the rectangle where the graph will be rendered." return (-self.ofsx, -self.ofsy, self.width, self.height) def visible(self, x1, y1, x2, y2): """Is any part of the box visible (i.e. within the bounding box)? We have to perform clipping ourselves because with big graphs the coordinates may sometimes become longs and cause OverflowErrors within pygame. """ return x1 < self.width and x2 > 0 and y1 < self.height and y2 > 0 def visible_node (self, x, y, w, h): x, y = self.map (x, y) nw2 = int(w * self.scale)//2 nh2 = int(h * self.scale)//2 return True return x-nw2 < w and x+nw2 > 0 and y-nh2 < h and y+nh2 > 0 def visible_edge (self, x1, y1, x2, y2): return True w, h = self.width, self.height x1, y1 = self.map(x1, y1) x2, y2 = self.map(x2, y2) return x1 < w and y1 < h and x2 > 0 and y2 > 0 class MyDisplay (GraphDisplay): def __init__(self, size): self.driver = PyGameDriver() self.viewers_history = [] self.forward_viewers_history = [] self.highlight_word = None self.highlight_obj = None self.viewer = None self.method_cache = {} self.key_cache = {} self.ascii_key_cache = {} self.status_bar_height = 0 self.searchstr = None self.searchpos = 0 self.searchresults = [] self.size = size self.driver.resize (size) self.initialize_keys() self.font = pygame.font.Font (self.STATUSBARFONT, 16) def updated_viewer(self, view,keep_highlight=False): width , height = self.size view.reoffset(width, height) if not keep_highlight: self.sethighlight() self.update_status_bar() self.must_redraw = True def redraw_now(self): self.status_bar_height = 0 pygame.display.flip() self.must_redraw = False def display_graph (self, graph): rend = MyGraphRenderer (self.driver) view = View (self) view.calculate_scale_to_fit(graph.boundingbox) rend.render (graph, view) self.redraw_now () def process_event (self, event): graph = event.get ('layout', None) if graph: self.display_graph (graph) self.driver.update() def controller_process_event(self, event): method = self.method_cache.get(event.type, KeyError) if method is KeyError: method = getattr(self, 'process_%s' % (pygame.event.event_name(event.type),), None) self.method_cache[method] = method if method is not None: method(event) def notifymousepos(self, pos): pass def notifyclick (self, pos): pass def zoom (self, amount): pass def loop (self): self.dragging = self.click_origin = self.click_time = None try: EventQueue = [] while True: if not EventQueue: EventQueue.append(pygame.event.wait()) EventQueue.extend(pygame.event.get()) self.controller_process_event(EventQueue.pop(0)) except StopIteration: pass