25 __all__ = [
'WxDotWindow',
'WxDotFrame']
29 if wxversion.checkInstalled(
"2.8"):
30 wxversion.select(
"2.8")
32 print(
"wxversion 2.8 is not installed, installed versions are {}".format(wxversion.getInstalled()))
34 gtk.remove_log_handlers()
36 import wx.lib.wxcairo
as wxcairo
39 if 'wxMac' in wx.PlatformInfo:
41 elif 'wxMSW' in wx.PlatformInfo:
43 elif 'wxGTK' in wx.PlatformInfo:
45 gdkLib = wx.lib.wxcairo._findGDKLib()
46 gdkLib.gdk_cairo_create.restype = ctypes.c_void_p
53 x,y = event.GetPositionTuple()
59 x,y = event.GetPositionTuple()
62 self.
drag(deltax, deltay)
67 x,y = event.GetPositionTuple()
78 def drag(self, deltax, deltay):
87 class WxNullAction(WxDragAction):
91 class WxPanAction(WxDragAction):
93 self.dot_widget.set_cursor(wx.CURSOR_SIZING)
95 def drag(self, deltax, deltay):
96 self.dot_widget.x += deltax / self.dot_widget.zoom_ratio
97 self.dot_widget.y += deltay / self.dot_widget.zoom_ratio
98 self.dot_widget.Refresh()
101 self.dot_widget.set_cursor(wx.CURSOR_ARROW)
106 def drag(self, deltax, deltay):
107 self.dot_widget.zoom_ratio *= 1.005 ** (deltax + deltay)
108 self.dot_widget.zoom_to_fit_on_resize =
False 109 self.dot_widget.Refresh()
112 self.dot_widget.Refresh()
115 def drag(self, deltax, deltay):
116 self.dot_widget.Refresh()
120 cr.set_source_rgba(.5, .5, 1.0, 0.25)
125 cr.set_source_rgba(.5, .5, 1.0, 1.0)
134 x1, y1 = self.dot_widget.window2graph(self.
startmousex,
136 x2, y2 = self.dot_widget.window2graph(self.
stopmousex,
138 self.dot_widget.zoom_to_area(x1, y1, x2, y2)
141 self.dot_widget.Refresh()
144 """wxpython Frame that draws dot graphs.""" 149 wx.Panel.__init__(self, parent, id)
154 self.
x, self.
y = 0.0, 0.0
163 self.Bind(wx.EVT_PAINT, self.
OnPaint)
164 self.Bind(wx.EVT_SIZE, self.
OnResize)
166 self.Bind(wx.EVT_MOUSEWHEEL, self.
OnScroll)
168 self.Bind(wx.EVT_MOUSE_EVENTS, self.
OnMouse)
170 self.Bind(wx.EVT_KEY_DOWN, self.
OnKeyDown)
180 self.select_cbs.append(cb)
187 """Redraw the graph.""" 188 dc = wx.PaintDC(self)
191 ctx = wxcairo.ContextFromDC(dc)
192 ctx = pangocairo.CairoContext(ctx)
196 width, height = self.GetSize()
199 ctx.rectangle(0,0,width,height)
202 ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0)
206 ctx.translate(0.5*width, 0.5*height)
209 ctx.translate(-self.
x, -self.
y)
210 self.graph.draw(ctx, highlight_items=self.
highlight)
213 self.drag_action.draw(ctx)
217 if event.GetWheelRotation() > 0:
219 pos=(event.GetX(), event.GetY()))
222 pos=(event.GetX(), event.GetY()))
225 """Process key down event.""" 226 key = event.GetKeyCode()
227 if key == wx.WXK_LEFT:
230 if key == wx.WXK_RIGHT:
236 if key == wx.WXK_DOWN:
239 if key == wx.WXK_PAGEUP:
242 if key == wx.WXK_PAGEDOWN:
245 if key == wx.WXK_ESCAPE:
246 self.drag_action.abort()
258 """Get the current graph position.""" 259 return self.
x, self.
y 262 """Set the current graph position.""" 268 """Set a number of items to be hilighted.""" 275 self.
cursor = wx.StockCursor(cursor_type)
276 self.SetCursor(self.
cursor)
280 """Zoom the graph.""" 282 self.
x = self.graph.width/2
283 self.
y = self.graph.height/2
284 elif pos
is not None:
285 width, height = self.GetSize()
296 """Zoom to an area of the graph.""" 297 width, height = self.GetSize()
298 area_width = abs(x1 - x2)
299 area_height = abs(y1 - y2)
301 float(width)/float(area_width),
302 float(height)/float(area_height)
305 self.
x = (x1 + x2) / 2
306 self.
y = (y1 + y2) / 2
310 """Zoom to fit the size of the graph.""" 311 width,height = self.GetSize()
317 if float(self.graph.width) > 0
and float(self.graph.height) > 0
and width > 0
and height > 0:
319 float(width)/float(self.graph.width),
320 float(height)/float(self.graph.height)
325 ZOOM_INCREMENT = 1.25
326 ZOOM_TO_FIT_MARGIN = 12
343 """Get a drag action for this click.""" 345 button = event.GetButton()
347 control_down = event.ControlDown()
348 alt_down = event.AltDown()
349 shift_down = event.ShiftDown()
351 drag = event.Dragging()
352 motion = event.Moving()
355 if button
in (wx.MOUSE_BTN_LEFT, wx.MOUSE_BTN_MIDDLE):
367 x,y = event.GetPositionTuple()
372 if not event.Dragging():
388 if event.ButtonDown():
389 self.animation.stop()
390 self.drag_action.abort()
394 self.drag_action.on_button_press(event)
399 if event.Dragging()
or event.Moving():
400 self.drag_action.on_motion_notify(event)
403 self.drag_action.on_button_release(event)
415 self.animation.start()
418 "Get the x,y coordinates in the graph from the x,y coordinates in the window.""" 419 width, height = self.GetSize() 428 def get_url(self, x, y): 429 x, y = self.window2graph(x, y) 430 return self.graph.get_url(x, y) 432 def get_jump(self, x, y): 433 x, y = self.window2graph(x, y) 434 return self.graph.get_jump(x, y) 436 def set_filter(self, filter): 439 def set_dotcode(self, dotcode, filename='<stdin>'): 440 if isinstance(dotcode, unicode): 441 dotcode = dotcode.encode('utf8') 442 p = subprocess.Popen( 443 [self.filter, '-Txdot'], 444 stdin=subprocess.PIPE, 445 stdout=subprocess.PIPE, 446 stderr=subprocess.PIPE, 448 universal_newlines=True 450 xdotcode, error = p.communicate(dotcode) 451 if p.returncode != 0: 452 print "ERROR PARSING DOT CODE", error 453 dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, 454 message_format=error, 455 buttons=gtk.BUTTONS_OK) 456 dialog.set_title('Dot Viewer') 461 self.set_xdotcode(xdotcode) 463 # Store references to all the items 464 self.items_by_url = {} 465 for item in self.graph.nodes + self.graph.edges: 466 if item.url is not None: 467 self.items_by_url[item.url] = item 469 # Store references to subgraph states 470 self.subgraph_shapes = self.graph.subgraph_shapes 472 except ParseError, ex: 473 print "ERROR PARSING XDOT CODE" 474 dialog = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, 475 message_format=str(ex), 476 buttons=gtk.BUTTONS_OK) 477 dialog.set_title('Dot Viewer') 482 self.openfilename = filename 485 def set_xdotcode(self, xdotcode): 488 parser = XDotParser(xdotcode) 489 self.graph = parser.parse() 490 self.highlight = None 491 #self.zoom_image(self.zoom_ratio, center=True) 494 if self.openfilename is not None: 496 fp = file(self.openfilename, 'rt') 497 self.set_dotcode(fp.read(), self.openfilename) 503 class WxDotFrame(wx.Frame): 505 wx.Frame.__init__(self, None, -1, "Dot Viewer", size=(512,512)) 507 vbox = wx.BoxSizer(wx.VERTICAL) 510 toolbar = wx.ToolBar(self, -1) 511 toolbar.AddLabelTool(wx.ID_OPEN, 'Open File', 512 wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN,wx.ART_OTHER,(16,16))) 513 toolbar.AddLabelTool(wx.ID_HELP, 'Help', 514 wx.ArtProvider.GetBitmap(wx.ART_HELP,wx.ART_OTHER,(16,16)) ) 517 self.Bind(wx.EVT_TOOL, self.DoOpenFile, id=wx.ID_OPEN) 518 self.Bind(wx.EVT_TOOL, self.ShowControlsDialog, id=wx.ID_HELP) 521 self.widget = WxDotWindow(self, -1) 523 # Add elements to sizer 524 vbox.Add(toolbar, 0, wx.EXPAND) 525 vbox.Add(self.widget, 100, wx.EXPAND | wx.ALL) 530 def ShowControlsDialog(self,event): 531 dial = wx.MessageDialog(None, 534 Zoom: PageUp / PageDown\n\ 537 'Keyboard Controls', wx.OK) 540 def DoOpenFile(self,event): 541 wcd = 'All files (*)|*|GraphViz Dot Files(*.dot)|*.dot|' 543 open_dlg = wx.FileDialog(self, message='Choose a file', defaultDir=dir, defaultFile='', 544 wildcard=wcd, style=wx.OPEN|wx.CHANGE_DIR) 545 if open_dlg.ShowModal() == wx.ID_OK: 546 path = open_dlg.GetPath() 551 except IOError, error: 552 dlg = wx.MessageDialog(self, 'Error opening file\n' + str(error)) 555 except UnicodeDecodeError, error: 556 dlg = wx.MessageDialog(self, 'Error opening file\n' + str(error)) 561 def OnExit(self, event): 564 def set_dotcode(self, dotcode, filename='<stdin>'): 565 if self.widget.set_dotcode(dotcode, filename): 566 self.SetTitle(os.path.basename(filename) + ' - Dot Viewer') 567 self.widget.zoom_to_fit() 569 def set_xdotcode(self, xdotcode, filename='<stdin>'): 570 if self.widget.set_xdotcode(xdotcode): 571 self.SetTitle(os.path.basename(filename) + ' - Dot Viewer') 572 self.widget.zoom_to_fit() 574 def open_file(self, filename): 576 fp = file(filename, 'rt') 577 self.set_dotcode(fp.read(), filename) 581 dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
582 message_format=str(ex),
583 buttons=gtk.BUTTONS_OK)
584 dlg.set_title(
'Dot Viewer')
589 def set_filter(self, filter): 590 self.widget.set_filter(filter) def on_area_size_allocate(self, area, allocation)
def on_motion_notify(self, event)
def zoom_image(self, zoom_ratio, center=False, pos=None)
Zooming methods.
def on_zoom_fit(self, action)
def register_select_callback(self, cb)
User callbacks.
def zoom_to_area(self, x1, y1, x2, y2)
def __init__(self, parent, id)
def animate_to(self, x, y)
def drag(self, deltax, deltay)
def get_drag_action(self, event)
def OnKeyDown(self, event)
def set_highlight(self, items)
def window2graph(self, x, y)
def on_zoom_in(self, action)
def drag(self, deltax, deltay)
def on_motion_notify(self, event)
def on_button_release(self, event)
def on_zoom_100(self, action)
def get_current_pos(self)
Helper functions.
def OnResize(self, event)
Event handlers.
def set_cursor(self, cursor_type)
Cursor manipulation.
def on_zoom_out(self, action)
def set_current_pos(self, x, y)
def __init__(self, dot_widget)
def drag(self, deltax, deltay)
def OnScroll(self, event)
def on_button_press(self, event)
def drag(self, deltax, deltay)