xdot.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright 2008-2015 Jose Fonseca
4 #
5 # This program is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as published
7 # by the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #
18 
19 '''Visualize dot graphs via the xdot format.'''
20 
21 __author__ = "Jose Fonseca et al"
22 
23 __version__ = "0.7"
24 
25 
26 import os
27 import sys
28 import subprocess
29 import math
30 import colorsys
31 import time
32 import re
33 import optparse
34 
35 import gi
36 gi.require_version('Gtk', '3.0')
37 gi.require_version('PangoCairo', '1.0')
38 
39 from gi.repository import GLib
40 from gi.repository import GObject
41 from gi.repository import Gtk
42 from gi.repository import Gdk
43 from gi.repository import GdkPixbuf
44 from gi.repository import Pango
45 from gi.repository import PangoCairo
46 import cairo
47 
48 
49 # see http://www.graphviz.org/pub/scm/graphviz-cairo/plugin/cairo/gvrender_cairo.c
50 
51 # For pygtk inspiration and guidance see:
52 # - http://mirageiv.berlios.de/
53 # - http://comix.sourceforge.net/
54 
55 
56 class Pen:
57  """Store pen attributes."""
58 
59  def __init__(self):
60  # set default attributes
61  self.color = (0.0, 0.0, 0.0, 1.0)
62  self.fillcolor = (0.0, 0.0, 0.0, 1.0)
63  self.linewidth = 1.0
64  self.fontsize = 14.0
65  self.fontname = "Times-Roman"
66  self.bold = False
67  self.italic = False
68  self.underline = False
69  self.superscript = False
70  self.subscript = False
71  self.strikethrough = False
72  self.overline = False
73 
74  self.dash = ()
75 
76  def copy(self):
77  """Create a copy of this pen."""
78  pen = Pen()
79  pen.__dict__ = self.__dict__.copy()
80  return pen
81 
82  def highlighted(self):
83  pen = self.copy()
84  pen.color = (1, 0, 0, 1)
85  pen.fillcolor = (1, .8, .8, 1)
86  return pen
87 
88 
89 class Shape:
90  """Abstract base class for all the drawing shapes."""
91 
92  def __init__(self):
93  pass
94 
95  def draw(self, cr, highlight=False):
96  """Draw this shape with the given cairo context"""
97  raise NotImplementedError
98 
99  def select_pen(self, highlight):
100  if highlight:
101  if not hasattr(self, 'highlight_pen'):
102  self.highlight_pen = self.pen.highlighted()
103  return self.highlight_pen
104  else:
105  return self.pen
106 
107  def search_text(self, regexp):
108  return False
109 
110 
112 
113  LEFT, CENTER, RIGHT = -1, 0, 1
114 
115  def __init__(self, pen, x, y, j, w, t):
116  Shape.__init__(self)
117  self.pen = pen.copy()
118  self.x = x
119  self.y = y
120  self.j = j # Centering
121  self.w = w # width
122  self.t = t # text
123 
124  def draw(self, cr, highlight=False):
125 
126  try:
127  layout = self.layout
128  except AttributeError:
129  layout = PangoCairo.create_layout(cr)
130 
131  # set font options
132  # see http://lists.freedesktop.org/archives/cairo/2007-February/009688.html
133  context = layout.get_context()
134  fo = cairo.FontOptions()
135  fo.set_antialias(cairo.ANTIALIAS_DEFAULT)
136  fo.set_hint_style(cairo.HINT_STYLE_NONE)
137  fo.set_hint_metrics(cairo.HINT_METRICS_OFF)
138  try:
139  PangoCairo.context_set_font_options(context, fo)
140  except TypeError:
141  # XXX: Some broken pangocairo bindings show the error
142  # 'TypeError: font_options must be a cairo.FontOptions or None'
143  pass
144  except KeyError:
145  # cairo.FontOptions is not registered as a foreign struct in older PyGObject versions.
146  # https://git.gnome.org/browse/pygobject/commit/?id=b21f66d2a399b8c9a36a1758107b7bdff0ec8eaa
147  pass
148 
149  # set font
150  font = Pango.FontDescription()
151 
152  # https://developer.gnome.org/pango/stable/PangoMarkupFormat.html
153  markup = GObject.markup_escape_text(self.t)
154  if self.pen.bold:
155  markup = '<b>' + markup + '</b>'
156  if self.pen.italic:
157  markup = '<i>' + markup + '</i>'
158  if self.pen.underline:
159  markup = '<span underline="single">' + markup + '</span>'
160  if self.pen.strikethrough:
161  markup = '<s>' + markup + '</s>'
162  if self.pen.superscript:
163  markup = '<sup><small>' + markup + '</small></sup>'
164  if self.pen.subscript:
165  markup = '<sub><small>' + markup + '</small></sub>'
166 
167  success, attrs, text, accel_char = Pango.parse_markup(markup, -1, '\x00')
168  assert success
169  layout.set_attributes(attrs)
170 
171  font.set_family(self.pen.fontname)
172  font.set_absolute_size(self.pen.fontsize*Pango.SCALE)
173  layout.set_font_description(font)
174 
175  # set text
176  layout.set_text(text, -1)
177 
178  # cache it
179  self.layout = layout
180  else:
181  PangoCairo.update_layout(cr, layout)
182 
183  descent = 2 # XXX get descender from font metrics
184 
185  width, height = layout.get_size()
186  width = float(width)/Pango.SCALE
187  height = float(height)/Pango.SCALE
188 
189  # we know the width that dot thinks this text should have
190  # we do not necessarily have a font with the same metrics
191  # scale it so that the text fits inside its box
192  if width > self.w:
193  f = self.w / width
194  width = self.w # equivalent to width *= f
195  height *= f
196  descent *= f
197  else:
198  f = 1.0
199 
200  if self.j == self.LEFT:
201  x = self.x
202  elif self.j == self.CENTER:
203  x = self.x - 0.5*width
204  elif self.j == self.RIGHT:
205  x = self.x - width
206  else:
207  assert 0
208 
209  y = self.y - height + descent
210 
211  cr.move_to(x, y)
212 
213  cr.save()
214  cr.scale(f, f)
215  cr.set_source_rgba(*self.select_pen(highlight).color)
216  PangoCairo.show_layout(cr, layout)
217  cr.restore()
218 
219  if 0: # DEBUG
220  # show where dot thinks the text should appear
221  cr.set_source_rgba(1, 0, 0, .9)
222  if self.j == self.LEFT:
223  x = self.x
224  elif self.j == self.CENTER:
225  x = self.x - 0.5*self.w
226  elif self.j == self.RIGHT:
227  x = self.x - self.w
228  cr.move_to(x, self.y)
229  cr.line_to(x+self.w, self.y)
230  cr.stroke()
231 
232  def search_text(self, regexp):
233  return regexp.search(self.t) is not None
234 
235 
237 
238  def __init__(self, pen, x0, y0, w, h, path):
239  Shape.__init__(self)
240  self.pen = pen.copy()
241  self.x0 = x0
242  self.y0 = y0
243  self.w = w
244  self.h = h
245  self.path = path
246 
247  def draw(self, cr, highlight=False):
248  pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.path)
249  sx = float(self.w)/float(pixbuf.get_width())
250  sy = float(self.h)/float(pixbuf.get_height())
251  cr.save()
252  cr.translate(self.x0, self.y0 - self.h)
253  cr.scale(sx, sy)
254  Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0)
255  cr.paint()
256  cr.restore()
257 
258 
260 
261  def __init__(self, pen, x0, y0, w, h, filled=False):
262  Shape.__init__(self)
263  self.pen = pen.copy()
264  self.x0 = x0
265  self.y0 = y0
266  self.w = w
267  self.h = h
268  self.filled = filled
269 
270  def draw(self, cr, highlight=False):
271  cr.save()
272  cr.translate(self.x0, self.y0)
273  cr.scale(self.w, self.h)
274  cr.move_to(1.0, 0.0)
275  cr.arc(0.0, 0.0, 1.0, 0, 2.0*math.pi)
276  cr.restore()
277  pen = self.select_pen(highlight)
278  if self.filled:
279  cr.set_source_rgba(*pen.fillcolor)
280  cr.fill()
281  else:
282  cr.set_dash(pen.dash)
283  cr.set_line_width(pen.linewidth)
284  cr.set_source_rgba(*pen.color)
285  cr.stroke()
286 
287 
289 
290  def __init__(self, pen, points, filled=False):
291  Shape.__init__(self)
292  self.pen = pen.copy()
293  self.points = points
294  self.filled = filled
295 
296  def draw(self, cr, highlight=False):
297  x0, y0 = self.points[-1]
298  cr.move_to(x0, y0)
299  for x, y in self.points:
300  cr.line_to(x, y)
301  cr.close_path()
302  pen = self.select_pen(highlight)
303  if self.filled:
304  cr.set_source_rgba(*pen.fillcolor)
305  cr.fill_preserve()
306  cr.fill()
307  else:
308  cr.set_dash(pen.dash)
309  cr.set_line_width(pen.linewidth)
310  cr.set_source_rgba(*pen.color)
311  cr.stroke()
312 
313 
315 
316  def __init__(self, pen, points):
317  Shape.__init__(self)
318  self.pen = pen.copy()
319  self.points = points
320 
321  def draw(self, cr, highlight=False):
322  x0, y0 = self.points[0]
323  cr.move_to(x0, y0)
324  for x1, y1 in self.points[1:]:
325  cr.line_to(x1, y1)
326  pen = self.select_pen(highlight)
327  cr.set_dash(pen.dash)
328  cr.set_line_width(pen.linewidth)
329  cr.set_source_rgba(*pen.color)
330  cr.stroke()
331 
332 
334 
335  def __init__(self, pen, points, filled=False):
336  Shape.__init__(self)
337  self.pen = pen.copy()
338  self.points = points
339  self.filled = filled
340 
341  def draw(self, cr, highlight=False):
342  x0, y0 = self.points[0]
343  cr.move_to(x0, y0)
344  for i in range(1, len(self.points), 3):
345  x1, y1 = self.points[i]
346  x2, y2 = self.points[i + 1]
347  x3, y3 = self.points[i + 2]
348  cr.curve_to(x1, y1, x2, y2, x3, y3)
349  pen = self.select_pen(highlight)
350  if self.filled:
351  cr.set_source_rgba(*pen.fillcolor)
352  cr.fill_preserve()
353  cr.fill()
354  else:
355  cr.set_dash(pen.dash)
356  cr.set_line_width(pen.linewidth)
357  cr.set_source_rgba(*pen.color)
358  cr.stroke()
359 
360 
362 
363  def __init__(self, shapes):
364  Shape.__init__(self)
365  self.shapes = shapes
366 
367  def draw(self, cr, highlight=False):
368  for shape in self.shapes:
369  shape.draw(cr, highlight=highlight)
370 
371  def search_text(self, regexp):
372  for shape in self.shapes:
373  if shape.search_text(regexp):
374  return True
375  return False
376 
377 
378 class Url(object):
379 
380  def __init__(self, item, url, highlight=None):
381  self.item = item
382  self.url = url
383  if highlight is None:
384  highlight = set([item])
385  self.highlight = highlight
386 
387 
388 class Jump(object):
389 
390  def __init__(self, item, x, y, highlight=None, url=None):
391  self.item = item
392  self.x = x
393  self.y = y
394  if highlight is None:
395  highlight = set([item])
396  self.highlight = highlight
397  self.url = url
398 
399 
401  """Base class for graph nodes and edges."""
402 
403  def __init__(self, shapes):
404  CompoundShape.__init__(self, shapes)
405 
406  def is_inside(self, x, y):
407  return False
408 
409  def get_url(self, x, y):
410  return None
411 
412  def get_jump(self, x, y):
413  return None
414 
415 
416 class Node(Element):
417 
418  def __init__(self, id, x, y, w, h, shapes, url):
419  Element.__init__(self, shapes)
420 
421  self.id = id
422  self.x = x
423  self.y = y
424 
425  self.x1 = x - 0.5*w
426  self.y1 = y - 0.5*h
427  self.x2 = x + 0.5*w
428  self.y2 = y + 0.5*h
429 
430  self.url = url
431 
432  def is_inside(self, x, y):
433  return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2
434 
435  def get_url(self, x, y):
436  if self.url is None:
437  return None
438  if self.is_inside(x, y):
439  return Url(self, self.url)
440  return None
441 
442  def get_jump(self, x, y):
443  if self.is_inside(x, y):
444  return Jump(self, self.x, self.y)
445  return None
446 
447  def __repr__(self):
448  return "<Node %s>" % self.id
449 
450 
451 def square_distance(x1, y1, x2, y2):
452  deltax = x2 - x1
453  deltay = y2 - y1
454  return deltax*deltax + deltay*deltay
455 
456 
457 class Edge(Element):
458 
459  def __init__(self, src, dst, points, shapes, url):
460  Element.__init__(self, shapes)
461  self.src = src
462  self.dst = dst
463  self.points = points
464  self.url = url
465 
466  RADIUS = 10
467 
468  def is_inside_begin(self, x, y):
469  return square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS
470 
471  def is_inside_end(self, x, y):
472  return square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS
473 
474  def is_inside(self, x, y):
475  if self.is_inside_begin(x, y):
476  return True
477  if self.is_inside_end(x, y):
478  return True
479  return False
480 
481  def get_jump(self, x, y):
482  if self.is_inside_begin(x, y):
483  return Jump(self, self.dst.x, self.dst.y, highlight=set([self, self.dst]),url=self.url)
484  if self.is_inside_end(x, y):
485  return Jump(self, self.src.x, self.src.y, highlight=set([self, self.src]),url=self.url)
486  return None
487 
488  def __repr__(self):
489  return "<Edge %s -> %s>" % (self.src, self.dst)
490 
491 
492 class Graph(Shape):
493 
494  def __init__(self, width=1, height=1, shapes=(), nodes=(), edges=(), subgraph_shapes={}):
495  Shape.__init__(self)
496 
497  self.width = width
498  self.height = height
499  self.shapes = shapes
500  self.nodes = nodes
501  self.edges = edges
502  self.subgraph_shapes = subgraph_shapes
503 
504  def get_size(self):
505  return self.width, self.height
506 
507  def draw(self, cr, highlight_items=None):
508  if highlight_items is None:
509  highlight_items = ()
510  cr.set_source_rgba(0.0, 0.0, 0.0, 1.0)
511 
512  cr.set_line_cap(cairo.LINE_CAP_BUTT)
513  cr.set_line_join(cairo.LINE_JOIN_MITER)
514 
515  for shape in self.shapes:
516  shape.draw(cr)
517  for edge in self.edges:
518  edge.draw(cr, highlight=(edge in highlight_items))
519  for node in self.nodes:
520  node.draw(cr, highlight=(node in highlight_items))
521 
522  def get_element(self, x, y):
523  for node in self.nodes:
524  if node.is_inside(x, y):
525  return node
526  for edge in self.edges:
527  if edge.is_inside(x, y):
528  return edge
529 
530  def get_url(self, x, y):
531  for node in self.nodes:
532  url = node.get_url(x, y)
533  if url is not None:
534  return url
535  return None
536 
537  def get_jump(self, x, y):
538  for edge in self.edges:
539  jump = edge.get_jump(x, y)
540  if jump is not None:
541  return jump
542  for node in self.nodes:
543  jump = node.get_jump(x, y)
544  if jump is not None:
545  return jump
546  return None
547 
548 
549 BOLD = 1
550 ITALIC = 2
551 UNDERLINE = 4
552 SUPERSCRIPT = 8
553 SUBSCRIPT = 16
554 STRIKE_THROUGH = 32
555 OVERLINE = 64
556 
557 
559  """Parser for xdot drawing attributes.
560  See also:
561  - http://www.graphviz.org/doc/info/output.html#d:xdot
562  """
563 
564  def __init__(self, parser, buf):
565  self.parser = parser
566  self.buf = self.unescape(buf)
567  self.pos = 0
568 
569  self.pen = Pen()
570  self.shapes = []
571 
572  def __nonzero__(self):
573  return self.pos < len(self.buf)
574 
575  def unescape(self, buf):
576  buf = buf.replace('\\"', '"')
577  buf = buf.replace('\\n', '\n')
578  return buf
579 
580  def read_code(self):
581  pos = self.buf.find(b" ", self.pos)
582  res = self.buf[self.pos:pos]
583  self.pos = pos + 1
584  self.skip_space()
585  res = res.decode('utf-8')
586  return res
587 
588  def skip_space(self):
589  while self.pos < len(self.buf) and self.buf[self.pos : self.pos + 1].isspace():
590  self.pos += 1
591 
592  def read_int(self):
593  return int(self.read_code())
594 
595  def read_float(self):
596  return float(self.read_code())
597 
598  def read_point(self):
599  x = self.read_float()
600  y = self.read_float()
601  return self.transform(x, y)
602 
603  def read_text(self):
604  num = self.read_int()
605  pos = self.buf.find(b"-", self.pos) + 1
606  self.pos = pos + num
607  res = self.buf[pos:self.pos]
608  self.skip_space()
609  res = res.decode('utf-8')
610  return res
611 
612  def read_polygon(self):
613  n = self.read_int()
614  p = []
615  for i in range(n):
616  x, y = self.read_point()
617  p.append((x, y))
618  return p
619 
620  def read_color(self):
621  # See http://www.graphviz.org/doc/info/attrs.html#k:color
622  c = self.read_text()
623  c1 = c[:1]
624  if c1 == '#':
625  hex2float = lambda h: float(int(h, 16)/255.0)
626  r = hex2float(c[1:3])
627  g = hex2float(c[3:5])
628  b = hex2float(c[5:7])
629  try:
630  a = hex2float(c[7:9])
631  except (IndexError, ValueError):
632  a = 1.0
633  return r, g, b, a
634  elif c1.isdigit() or c1 == ".":
635  # "H,S,V" or "H S V" or "H, S, V" or any other variation
636  h, s, v = map(float, c.replace(",", " ").split())
637  r, g, b = colorsys.hsv_to_rgb(h, s, v)
638  a = 1.0
639  return r, g, b, a
640  elif c1 == "[" or c1 == "(":
641  sys.stderr.write('warning: color gradients not supported yet\n')
642  return None
643  else:
644  return self.lookup_color(c)
645 
646  def lookup_color(self, c):
647  try:
648  color = Gdk.color_parse(c)
649  except ValueError:
650  pass
651  else:
652  s = 1.0/65535.0
653  r = color.red*s
654  g = color.green*s
655  b = color.blue*s
656  a = 1.0
657  return r, g, b, a
658 
659  try:
660  dummy, scheme, index = c.split('/')
661  r, g, b = brewer_colors[scheme][int(index)]
662  except (ValueError, KeyError):
663  pass
664  else:
665  s = 1.0/255.0
666  r = r*s
667  g = g*s
668  b = b*s
669  a = 1.0
670  return r, g, b, a
671 
672  sys.stderr.write("warning: unknown color '%s'\n" % c)
673  return None
674 
675  def parse(self):
676  s = self
677 
678  while s:
679  op = s.read_code()
680  if op == "c":
681  color = s.read_color()
682  if color is not None:
683  self.handle_color(color, filled=False)
684  elif op == "C":
685  color = s.read_color()
686  if color is not None:
687  self.handle_color(color, filled=True)
688  elif op == "S":
689  # http://www.graphviz.org/doc/info/attrs.html#k:style
690  style = s.read_text()
691  if style.startswith("setlinewidth("):
692  lw = style.split("(")[1].split(")")[0]
693  lw = float(lw)
694  self.handle_linewidth(lw)
695  elif style in ("solid", "dashed", "dotted"):
696  self.handle_linestyle(style)
697  elif op == "F":
698  size = s.read_float()
699  name = s.read_text()
700  self.handle_font(size, name)
701  elif op == "T":
702  x, y = s.read_point()
703  j = s.read_int()
704  w = s.read_float()
705  t = s.read_text()
706  self.handle_text(x, y, j, w, t)
707  elif op == "t":
708  f = s.read_int()
710  elif op == "E":
711  x0, y0 = s.read_point()
712  w = s.read_float()
713  h = s.read_float()
714  self.handle_ellipse(x0, y0, w, h, filled=True)
715  elif op == "e":
716  x0, y0 = s.read_point()
717  w = s.read_float()
718  h = s.read_float()
719  self.handle_ellipse(x0, y0, w, h, filled=False)
720  elif op == "L":
721  points = self.read_polygon()
722  self.handle_line(points)
723  elif op == "B":
724  points = self.read_polygon()
725  self.handle_bezier(points, filled=False)
726  elif op == "b":
727  points = self.read_polygon()
728  self.handle_bezier(points, filled=True)
729  elif op == "P":
730  points = self.read_polygon()
731  self.handle_polygon(points, filled=True)
732  elif op == "p":
733  points = self.read_polygon()
734  self.handle_polygon(points, filled=False)
735  elif op == "I":
736  x0, y0 = s.read_point()
737  w = s.read_float()
738  h = s.read_float()
739  path = s.read_text()
740  self.handle_image(x0, y0, w, h, path)
741  else:
742  sys.stderr.write("error: unknown xdot opcode '%s'\n" % op)
743  sys.exit(1)
744 
745  return self.shapes
746 
747  def transform(self, x, y):
748  return self.parser.transform(x, y)
749 
750  def handle_color(self, color, filled=False):
751  if filled:
752  self.pen.fillcolor = color
753  else:
754  self.pen.color = color
755 
756  def handle_linewidth(self, linewidth):
757  self.pen.linewidth = linewidth
758 
759  def handle_linestyle(self, style):
760  if style == "solid":
761  self.pen.dash = ()
762  elif style == "dashed":
763  self.pen.dash = (6, ) # 6pt on, 6pt off
764  elif style == "dotted":
765  self.pen.dash = (2, 4) # 2pt on, 4pt off
766 
767  def handle_font(self, size, name):
768  self.pen.fontsize = size
769  self.pen.fontname = name
770 
771  def handle_font_characteristics(self, flags):
772  self.pen.bold = bool(flags & BOLD)
773  self.pen.italic = bool(flags & ITALIC)
774  self.pen.underline = bool(flags & UNDERLINE)
775  self.pen.superscript = bool(flags & SUPERSCRIPT)
776  self.pen.subscript = bool(flags & SUBSCRIPT)
777  self.pen.strikethrough = bool(flags & STRIKE_THROUGH)
778  self.pen.overline = bool(flags & OVERLINE)
779  if self.pen.overline:
780  sys.stderr.write('warning: overlined text not supported yet\n')
781 
782  def handle_text(self, x, y, j, w, t):
783  self.shapes.append(TextShape(self.pen, x, y, j, w, t))
784 
785  def handle_ellipse(self, x0, y0, w, h, filled=False):
786  if filled:
787  # xdot uses this to mean "draw a filled shape with an outline"
788  self.shapes.append(EllipseShape(self.pen, x0, y0, w, h, filled=True))
789  self.shapes.append(EllipseShape(self.pen, x0, y0, w, h))
790 
791  def handle_image(self, x0, y0, w, h, path):
792  self.shapes.append(ImageShape(self.pen, x0, y0, w, h, path))
793 
794  def handle_line(self, points):
795  self.shapes.append(LineShape(self.pen, points))
796 
797  def handle_bezier(self, points, filled=False):
798  if filled:
799  # xdot uses this to mean "draw a filled shape with an outline"
800  self.shapes.append(BezierShape(self.pen, points, filled=True))
801  self.shapes.append(BezierShape(self.pen, points))
802 
803  def handle_polygon(self, points, filled=False):
804  if filled:
805  # xdot uses this to mean "draw a filled shape with an outline"
806  self.shapes.append(PolygonShape(self.pen, points, filled=True))
807  self.shapes.append(PolygonShape(self.pen, points))
808 
809 
810 EOF = -1
811 SKIP = -2
812 
813 
814 class ParseError(Exception):
815 
816  def __init__(self, msg=None, filename=None, line=None, col=None):
817  self.msg = msg
818  self.filename = filename
819  self.line = line
820  self.col = col
821 
822  def __str__(self):
823  return ':'.join([str(part) for part in (self.filename, self.line, self.col, self.msg) if part != None])
824 
825 
826 class Scanner:
827  """Stateless scanner."""
828 
829  # should be overriden by derived classes
830  tokens = []
831  symbols = {}
832  literals = {}
833  ignorecase = False
834 
835  def __init__(self):
836  flags = re.DOTALL
837  if self.ignorecase:
838  flags |= re.IGNORECASE
839  self.tokens_re = re.compile(
840  b'|'.join([b'(' + regexp + b')' for type, regexp, test_lit in self.tokens]),
841  flags
842  )
843 
844  def next(self, buf, pos):
845  if pos >= len(buf):
846  return EOF, b'', pos
847  mo = self.tokens_re.match(buf, pos)
848  if mo:
849  text = mo.group()
850  type, regexp, test_lit = self.tokens[mo.lastindex - 1]
851  pos = mo.end()
852  if test_lit:
853  type = self.literals.get(text, type)
854  return type, text, pos
855  else:
856  c = buf[pos : pos + 1]
857  return self.symbols.get(c, None), c, pos + 1
858 
859 
860 class Token:
861 
862  def __init__(self, type, text, line, col):
863  self.type = type
864  self.text = text
865  self.line = line
866  self.col = col
867 
868 
869 class Lexer:
870 
871  # should be overriden by derived classes
872  scanner = None
873  tabsize = 8
874 
875  newline_re = re.compile(br'\r\n?|\n')
876 
877  def __init__(self, buf = None, pos = 0, filename = None, fp = None):
878  if fp is not None:
879  try:
880  fileno = fp.fileno()
881  length = os.path.getsize(fp.name)
882  import mmap
883  except:
884  # read whole file into memory
885  buf = fp.read()
886  pos = 0
887  else:
888  # map the whole file into memory
889  if length:
890  # length must not be zero
891  buf = mmap.mmap(fileno, length, access = mmap.ACCESS_READ)
892  pos = os.lseek(fileno, 0, 1)
893  else:
894  buf = b''
895  pos = 0
896 
897  if filename is None:
898  try:
899  filename = fp.name
900  except AttributeError:
901  filename = None
902 
903  self.buf = buf
904  self.pos = pos
905  self.line = 1
906  self.col = 1
907  self.filename = filename
908 
909  def next(self):
910  while True:
911  # save state
912  pos = self.pos
913  line = self.line
914  col = self.col
915 
916  type, text, endpos = self.scanner.next(self.buf, pos)
917  assert isinstance(text, bytes)
918  assert pos + len(text) == endpos
919  self.consume(text)
920  type, text = self.filter(type, text)
921  self.pos = endpos
922 
923  if type == SKIP:
924  continue
925  elif type is None:
926  msg = 'unexpected char %r' % (text,)
927  raise ParseError(msg, self.filename, line, col)
928  else:
929  break
930  return Token(type = type, text = text, line = line, col = col)
931 
932  def consume(self, text):
933  # update line number
934  pos = 0
935  for mo in self.newline_re.finditer(text, pos):
936  self.line += 1
937  self.col = 1
938  pos = mo.end()
939 
940  # update column number
941  while True:
942  tabpos = text.find(b'\t', pos)
943  if tabpos == -1:
944  break
945  self.col += tabpos - pos
946  self.col = ((self.col - 1)//self.tabsize + 1)*self.tabsize + 1
947  pos = tabpos + 1
948  self.col += len(text) - pos
949 
950 
951 class Parser:
952 
953  def __init__(self, lexer):
954  self.lexer = lexer
955  self.lookahead = self.lexer.next()
956 
957  def match(self, type):
958  if self.lookahead.type != type:
959  raise ParseError(
960  msg = 'unexpected token %r' % self.lookahead.text,
961  filename = self.lexer.filename,
962  line = self.lookahead.line,
963  col = self.lookahead.col)
964 
965  def skip(self, type):
966  while self.lookahead.type != type:
967  if self.lookahead.type == EOF:
968  raise ParseError(
969  msg = 'unexpected end of file',
970  filename = self.lexer.filename,
971  line = self.lookahead.line,
972  col = self.lookahead.col)
973  self.consume()
974 
975  def consume(self):
976  token = self.lookahead
977  self.lookahead = self.lexer.next()
978  return token
979 
980 
981 ID = 0
982 STR_ID = 1
983 HTML_ID = 2
984 EDGE_OP = 3
985 
986 LSQUARE = 4
987 RSQUARE = 5
988 LCURLY = 6
989 RCURLY = 7
990 COMMA = 8
991 COLON = 9
992 SEMI = 10
993 EQUAL = 11
994 PLUS = 12
995 
996 STRICT = 13
997 GRAPH = 14
998 DIGRAPH = 15
999 NODE = 16
1000 EDGE = 17
1001 SUBGRAPH = 18
1002 
1003 
1005 
1006  # token regular expression table
1007  tokens = [
1008  # whitespace and comments
1009  (SKIP,
1010  br'[ \t\f\r\n\v]+|'
1011  br'//[^\r\n]*|'
1012  br'/\*.*?\*/|'
1013  br'#[^\r\n]*',
1014  False),
1015 
1016  # Alphanumeric IDs
1017  (ID, br'[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*', True),
1018 
1019  # Numeric IDs
1020  (ID, br'-?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)', False),
1021 
1022  # String IDs
1023  (STR_ID, br'"[^"\\]*(?:\\.[^"\\]*)*"', False),
1024 
1025  # HTML IDs
1026  (HTML_ID, br'<[^<>]*(?:<[^<>]*>[^<>]*)*>', False),
1027 
1028  # Edge operators
1029  (EDGE_OP, br'-[>-]', False),
1030  ]
1031 
1032  # symbol table
1033  symbols = {
1034  b'[': LSQUARE,
1035  b']': RSQUARE,
1036  b'{': LCURLY,
1037  b'}': RCURLY,
1038  b',': COMMA,
1039  b':': COLON,
1040  b';': SEMI,
1041  b'=': EQUAL,
1042  b'+': PLUS,
1043  }
1044 
1045  # literal table
1046  literals = {
1047  b'strict': STRICT,
1048  b'graph': GRAPH,
1049  b'digraph': DIGRAPH,
1050  b'node': NODE,
1051  b'edge': EDGE,
1052  b'subgraph': SUBGRAPH,
1053  }
1054 
1055  ignorecase = True
1056 
1057 
1059 
1060  scanner = DotScanner()
1061 
1062  def filter(self, type, text):
1063  # TODO: handle charset
1064  if type == STR_ID:
1065  text = text[1:-1]
1066 
1067  # line continuations
1068  text = text.replace(b'\\\r\n', b'')
1069  text = text.replace(b'\\\r', b'')
1070  text = text.replace(b'\\\n', b'')
1071 
1072  # quotes
1073  text = text.replace(b'\\"', b'"')
1074 
1075  # layout engines recognize other escape codes (many non-standard)
1076  # but we don't translate them here
1077 
1078  type = ID
1079 
1080  elif type == HTML_ID:
1081  text = text[1:-1]
1082  type = ID
1083 
1084  return type, text
1085 
1086 
1088 
1089  def __init__(self, lexer):
1090  Parser.__init__(self, lexer)
1091  self.graph_attrs = {}
1092  self.node_attrs = {}
1093  self.edge_attrs = {}
1094 
1095  def parse(self):
1096  self.parse_graph()
1097  self.match(EOF)
1098 
1099  def parse_graph(self):
1100  if self.lookahead.type == STRICT:
1101  self.consume()
1102  self.skip(LCURLY)
1103  self.consume()
1104  while self.lookahead.type != RCURLY:
1105  self.parse_stmt()
1106  self.consume()
1107 
1108  def parse_subgraph(self):
1109  id = None
1110  shapes_before = set(self.shapes)
1111  if self.lookahead.type == SUBGRAPH:
1112  self.consume()
1113  if self.lookahead.type == ID:
1114  id = self.lookahead.text
1115  self.consume()
1116  if self.lookahead.type == LCURLY:
1117  self.consume()
1118  while self.lookahead.type != RCURLY:
1119  self.parse_stmt()
1120  self.consume()
1121  new_shapes = set(self.shapes) - shapes_before
1122  self.subgraph_shapes[id] = [s for s in new_shapes if not any([s in ss for ss in list(self.subgraph_shapes.values())])]
1123  return id
1124 
1125  def parse_stmt(self):
1126  if self.lookahead.type == GRAPH:
1127  self.consume()
1128  attrs = self.parse_attrs()
1129  self.graph_attrs.update(attrs)
1130  self.handle_graph(attrs)
1131  elif self.lookahead.type == NODE:
1132  self.consume()
1133  self.node_attrs.update(self.parse_attrs())
1134  elif self.lookahead.type == EDGE:
1135  self.consume()
1136  self.edge_attrs.update(self.parse_attrs())
1137  elif self.lookahead.type in (SUBGRAPH, LCURLY):
1138  self.parse_subgraph()
1139  else:
1140  id = self.parse_node_id()
1141  if self.lookahead.type == EDGE_OP:
1142  self.consume()
1143  node_ids = [id, self.parse_node_id()]
1144  while self.lookahead.type == EDGE_OP:
1145  node_ids.append(self.parse_node_id())
1146  attrs = self.parse_attrs()
1147  for i in range(0, len(node_ids) - 1):
1148  self.handle_edge(node_ids[i], node_ids[i + 1], attrs)
1149  elif self.lookahead.type == EQUAL:
1150  self.consume()
1151  self.parse_id()
1152  else:
1153  attrs = self.parse_attrs()
1154  self.handle_node(id, attrs)
1155  if self.lookahead.type == SEMI:
1156  self.consume()
1157 
1158  def parse_attrs(self):
1159  attrs = {}
1160  while self.lookahead.type == LSQUARE:
1161  self.consume()
1162  while self.lookahead.type != RSQUARE:
1163  name, value = self.parse_attr()
1164  name = name.decode('utf-8')
1165  attrs[name] = value
1166  if self.lookahead.type == COMMA:
1167  self.consume()
1168  self.consume()
1169  return attrs
1170 
1171  def parse_attr(self):
1172  name = self.parse_id()
1173  if self.lookahead.type == EQUAL:
1174  self.consume()
1175  value = self.parse_id()
1176  else:
1177  value = b'true'
1178  return name, value
1179 
1180  def parse_node_id(self):
1181  node_id = self.parse_id()
1182  if self.lookahead.type == COLON:
1183  self.consume()
1184  port = self.parse_id()
1185  if self.lookahead.type == COLON:
1186  self.consume()
1187  compass_pt = self.parse_id()
1188  else:
1189  compass_pt = None
1190  else:
1191  port = None
1192  compass_pt = None
1193  # XXX: we don't really care about port and compass point values when parsing xdot
1194  return node_id
1195 
1196  def parse_id(self):
1197  self.match(ID)
1198  id = self.lookahead.text
1199  self.consume()
1200  return id
1201 
1202  def handle_graph(self, attrs):
1203  pass
1204 
1205  def handle_node(self, id, attrs):
1206  pass
1207 
1208  def handle_edge(self, src_id, dst_id, attrs):
1209  pass
1210 
1211 
1212 class XDotParser(DotParser):
1213 
1214  XDOTVERSION = '1.7'
1215 
1216  def __init__(self, xdotcode):
1217  lexer = DotLexer(buf = xdotcode)
1218  DotParser.__init__(self, lexer)
1219 
1220  self.nodes = []
1221  self.edges = []
1222  self.shapes = []
1223  self.node_by_name = {}
1224  self.top_graph = True
1225  self.width = 0
1226  self.height = 0
1228 
1229  def handle_graph(self, attrs):
1230  if self.top_graph:
1231  # Check xdot version
1232  try:
1233  xdotversion = attrs['xdotversion']
1234  except KeyError:
1235  pass
1236  else:
1237  if float(xdotversion) > float(self.XDOTVERSION):
1238  sys.stderr.write('warning: xdot version %s, but supported is %s\n' % (xdotversion, self.XDOTVERSION))
1239 
1240  # Parse bounding box
1241  try:
1242  bb = attrs['bb']
1243  except KeyError:
1244  return
1245 
1246  if bb:
1247  xmin, ymin, xmax, ymax = map(float, bb.split(b","))
1248 
1249  self.xoffset = -xmin
1250  self.yoffset = -ymax
1251  self.xscale = 1.0
1252  self.yscale = -1.0
1253  # FIXME: scale from points to pixels
1254 
1255  self.width = max(xmax - xmin, 1)
1256  self.height = max(ymax - ymin, 1)
1257 
1258  self.top_graph = False
1259 
1260  for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
1261  if attr in attrs:
1262  parser = XDotAttrParser(self, attrs[attr])
1263  self.shapes.extend(parser.parse())
1264 
1265  def handle_node(self, id, attrs):
1266  try:
1267  pos = attrs['pos']
1268  except KeyError:
1269  return
1270 
1271  x, y = self.parse_node_pos(pos)
1272  w = float(attrs.get('width', 0))*72
1273  h = float(attrs.get('height', 0))*72
1274  shapes = []
1275  for attr in ("_draw_", "_ldraw_"):
1276  if attr in attrs:
1277  parser = XDotAttrParser(self, attrs[attr])
1278  shapes.extend(parser.parse())
1279  url = attrs.get('URL', None)
1280  node = Node(id, x, y, w, h, shapes, url)
1281  self.node_by_name[id] = node
1282  if shapes:
1283  self.nodes.append(node)
1284 
1285  def handle_edge(self, src_id, dst_id, attrs):
1286  try:
1287  pos = attrs['pos']
1288  except KeyError:
1289  return
1290 
1291  points = self.parse_edge_pos(pos)
1292  shapes = []
1293  for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
1294  if attr in attrs:
1295  parser = XDotAttrParser(self, attrs[attr])
1296  shapes.extend(parser.parse())
1297  url = attrs.get('URL', None)
1298  if shapes:
1299  src = self.node_by_name[src_id]
1300  dst = self.node_by_name[dst_id]
1301  self.edges.append(Edge(src, dst, points, shapes, url))
1302 
1303  def parse(self):
1304  DotParser.parse(self)
1305  return Graph(self.width, self.height, self.shapes, self.nodes, self.edges, self.subgraph_shapes)
1306 
1307  def parse_node_pos(self, pos):
1308  x, y = pos.split(b",")
1309  return self.transform(float(x), float(y))
1310 
1311  def parse_edge_pos(self, pos):
1312  points = []
1313  for entry in pos.split(b' '):
1314  fields = entry.split(b',')
1315  try:
1316  x, y = fields
1317  except ValueError:
1318  # TODO: handle start/end points
1319  continue
1320  else:
1321  points.append(self.transform(float(x), float(y)))
1322  return points
1323 
1324  def transform(self, x, y):
1325  # XXX: this is not the right place for this code
1326  x = (x + self.xoffset)*self.xscale
1327  y = (y + self.yoffset)*self.yscale
1328  return x, y
1329 
1330 
1331 class Animation(object):
1332 
1333  step = 0.03 # seconds
1334 
1335  def __init__(self, dot_widget):
1336  self.dot_widget = dot_widget
1337  self.timeout_id = None
1338 
1339  def start(self):
1340  self.timeout_id = GLib.timeout_add(int(self.step * 1000), self.tick)
1341 
1342  def stop(self):
1343  self.dot_widget.animation = NoAnimation(self.dot_widget)
1344  if self.timeout_id is not None:
1345  GLib.source_remove(self.timeout_id)
1346  self.timeout_id = None
1347 
1348  def tick(self):
1349  self.stop()
1350 
1351 
1353 
1354  def start(self):
1355  pass
1356 
1357  def stop(self):
1358  pass
1359 
1360 
1361 class LinearAnimation(Animation):
1362 
1363  duration = 0.6
1364 
1365  def start(self):
1366  self.started = time.time()
1367  Animation.start(self)
1368 
1369  def tick(self):
1370  t = (time.time() - self.started) / self.duration
1371  self.animate(max(0, min(t, 1)))
1372  return (t < 1)
1373 
1374  def animate(self, t):
1375  pass
1376 
1377 
1378 class MoveToAnimation(LinearAnimation):
1379 
1380  def __init__(self, dot_widget, target_x, target_y):
1381  Animation.__init__(self, dot_widget)
1382  self.source_x = dot_widget.x
1383  self.source_y = dot_widget.y
1384  self.target_x = target_x
1385  self.target_y = target_y
1386 
1387  def animate(self, t):
1388  sx, sy = self.source_x, self.source_y
1389  tx, ty = self.target_x, self.target_y
1390  self.dot_widget.x = tx * t + sx * (1-t)
1391  self.dot_widget.y = ty * t + sy * (1-t)
1392  self.dot_widget.queue_draw()
1393 
1394 
1396 
1397  def __init__(self, dot_widget, target_x, target_y):
1398  MoveToAnimation.__init__(self, dot_widget, target_x, target_y)
1399  self.source_zoom = dot_widget.zoom_ratio
1401  self.extra_zoom = 0
1402 
1403  middle_zoom = 0.5 * (self.source_zoom + self.target_zoom)
1404 
1405  distance = math.hypot(self.source_x - self.target_x,
1406  self.source_y - self.target_y)
1407  rect = self.dot_widget.get_allocation()
1408  visible = min(rect.width, rect.height) / self.dot_widget.zoom_ratio
1409  visible *= 0.9
1410  if distance > 0:
1411  desired_middle_zoom = visible / distance
1412  self.extra_zoom = min(0, 4 * (desired_middle_zoom - middle_zoom))
1413 
1414  def animate(self, t):
1415  a, b, c = self.source_zoom, self.extra_zoom, self.target_zoom
1416  self.dot_widget.zoom_ratio = c*t + b*t*(1-t) + a*(1-t)
1417  self.dot_widget.zoom_to_fit_on_resize = False
1418  MoveToAnimation.animate(self, t)
1419 
1420 
1421 class DragAction(object):
1422 
1423  def __init__(self, dot_widget):
1424  self.dot_widget = dot_widget
1425 
1426  def on_button_press(self, event):
1427  self.startmousex = self.prevmousex = event.x
1428  self.startmousey = self.prevmousey = event.y
1429  self.start()
1430 
1431  def on_motion_notify(self, event):
1432  if event.is_hint:
1433  window, x, y, state = event.window.get_device_position(event.device)
1434  else:
1435  x, y, state = event.x, event.y, event.state
1436  deltax = self.prevmousex - x
1437  deltay = self.prevmousey - y
1438  self.drag(deltax, deltay)
1439  self.prevmousex = x
1440  self.prevmousey = y
1441 
1442  def on_button_release(self, event):
1443  self.stopmousex = event.x
1444  self.stopmousey = event.y
1445  self.stop()
1446 
1447  def draw(self, cr):
1448  pass
1449 
1450  def start(self):
1451  pass
1452 
1453  def drag(self, deltax, deltay):
1454  pass
1455 
1456  def stop(self):
1457  pass
1458 
1459  def abort(self):
1460  pass
1461 
1462 
1463 class NullAction(DragAction):
1464 
1465  def on_motion_notify(self, event):
1466  if event.is_hint:
1467  window, x, y, state = event.window.get_device_position(event.device)
1468  else:
1469  x, y, state = event.x, event.y, event.state
1470  dot_widget = self.dot_widget
1471  item = dot_widget.get_url(x, y)
1472  if item is None:
1473  item = dot_widget.get_jump(x, y)
1474  if item is not None:
1475  dot_widget.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
1476  dot_widget.set_highlight(item.highlight)
1477  else:
1478  dot_widget.get_window().set_cursor(None)
1479  dot_widget.set_highlight(None)
1480 
1481 
1483 
1484  def start(self):
1485  self.dot_widget.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.FLEUR))
1486 
1487  def drag(self, deltax, deltay):
1488  self.dot_widget.x += deltax / self.dot_widget.zoom_ratio
1489  self.dot_widget.y += deltay / self.dot_widget.zoom_ratio
1490  self.dot_widget.queue_draw()
1491 
1492  def stop(self):
1493  self.dot_widget.get_window().set_cursor(None)
1494 
1495  abort = stop
1496 
1497 
1499 
1500  def drag(self, deltax, deltay):
1501  self.dot_widget.zoom_ratio *= 1.005 ** (deltax + deltay)
1502  self.dot_widget.zoom_to_fit_on_resize = False
1503  self.dot_widget.queue_draw()
1504 
1505  def stop(self):
1506  self.dot_widget.queue_draw()
1507 
1508 
1510 
1511  def drag(self, deltax, deltay):
1512  self.dot_widget.queue_draw()
1513 
1514  def draw(self, cr):
1515  cr.save()
1516  cr.set_source_rgba(.5, .5, 1.0, 0.25)
1517  cr.rectangle(self.startmousex, self.startmousey,
1518  self.prevmousex - self.startmousex,
1519  self.prevmousey - self.startmousey)
1520  cr.fill()
1521  cr.set_source_rgba(.5, .5, 1.0, 1.0)
1522  cr.set_line_width(1)
1523  cr.rectangle(self.startmousex - .5, self.startmousey - .5,
1524  self.prevmousex - self.startmousex + 1,
1525  self.prevmousey - self.startmousey + 1)
1526  cr.stroke()
1527  cr.restore()
1528 
1529  def stop(self):
1530  x1, y1 = self.dot_widget.window2graph(self.startmousex,
1531  self.startmousey)
1532  x2, y2 = self.dot_widget.window2graph(self.stopmousex,
1533  self.stopmousey)
1534  self.dot_widget.zoom_to_area(x1, y1, x2, y2)
1535 
1536  def abort(self):
1537  self.dot_widget.queue_draw()
1538 
1539 
1540 class DotWidget(Gtk.DrawingArea):
1541  """GTK widget that draws dot graphs."""
1542 
1543  #TODO GTK3: Second argument has to be of type Gdk.EventButton instead of object.
1544  __gsignals__ = {
1545  'clicked' : (GObject.SIGNAL_RUN_LAST, None, (str, object))
1546  }
1547 
1548  filter = 'dot'
1549 
1550  def __init__(self):
1551  Gtk.DrawingArea.__init__(self)
1552 
1553  self.graph = Graph()
1554  self.openfilename = None
1555 
1556  self.set_can_focus(True)
1557 
1558  self.connect("draw", self.on_draw)
1559  self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK)
1560  self.connect("button-press-event", self.on_area_button_press)
1561  self.connect("button-release-event", self.on_area_button_release)
1562  self.add_events(Gdk.EventMask.POINTER_MOTION_MASK |
1563  Gdk.EventMask.POINTER_MOTION_HINT_MASK |
1564  Gdk.EventMask.BUTTON_RELEASE_MASK |
1565  Gdk.EventMask.SCROLL_MASK)
1566  self.connect("motion-notify-event", self.on_area_motion_notify)
1567  self.connect("scroll-event", self.on_area_scroll_event)
1568  self.connect("size-allocate", self.on_area_size_allocate)
1569 
1570  self.connect('key-press-event', self.on_key_press_event)
1571  self.last_mtime = None
1572 
1573  GLib.timeout_add(1000, self.update)
1574 
1575  self.x, self.y = 0.0, 0.0
1576  self.zoom_ratio = 1.0
1578  self.animation = NoAnimation(self)
1580  self.presstime = None
1581  self.highlight = None
1582 
1583  def set_filter(self, filter):
1584  self.filter = filter
1585 
1586  def run_filter(self, dotcode):
1587  if not self.filter:
1588  return dotcode
1589  try:
1590  p = subprocess.Popen(
1591  [self.filter, '-Txdot'],
1592  stdin=subprocess.PIPE,
1593  stdout=subprocess.PIPE,
1594  stderr=subprocess.PIPE,
1595  shell=False,
1596  universal_newlines=False
1597  )
1598  except OSError as exc:
1599  error = '%s: %s' % (self.filter, exc.strerror)
1600  p = subprocess.CalledProcessError(exc.errno, self.filter, exc.strerror)
1601  else:
1602  xdotcode, error = p.communicate(dotcode)
1603  error = error.rstrip()
1604  if error:
1605  error = error.decode()
1606  sys.stderr.write(error + '\n')
1607  if p.returncode != 0:
1608  self.error_dialog(error)
1609  return None
1610  return xdotcode
1611 
1612  def set_dotcode(self, dotcode, filename=None):
1613  self.openfilename = None
1614  if isinstance(dotcode, str):
1615  dotcode = dotcode.encode('utf-8')
1616  xdotcode = self.run_filter(dotcode)
1617  if xdotcode is None:
1618  return False
1619  try:
1620  self.set_xdotcode(xdotcode)
1621  except ParseError as ex:
1622  self.error_dialog(str(ex))
1623  return False
1624  else:
1625  if filename is None:
1626  self.last_mtime = None
1627  else:
1628  self.last_mtime = os.stat(filename).st_mtime
1629  self.openfilename = filename
1630  return True
1631 
1632  def set_xdotcode(self, xdotcode):
1633  assert isinstance(xdotcode, bytes)
1634  parser = XDotParser(xdotcode)
1635  self.graph = parser.parse()
1636  self.zoom_image(self.zoom_ratio, center=True)
1637 
1638  def reload(self):
1639  if self.openfilename is not None:
1640  try:
1641  fp = open(self.openfilename, 'rt')
1642  self.set_dotcode(fp.read(), self.openfilename)
1643  fp.close()
1644  except IOError:
1645  pass
1646 
1647  def update(self):
1648  if self.openfilename is not None:
1649  current_mtime = os.stat(self.openfilename).st_mtime
1650  if current_mtime != self.last_mtime:
1651  self.last_mtime = current_mtime
1652  self.reload()
1653  return True
1654 
1655  def on_draw(self, widget, cr):
1656  cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
1657  cr.paint()
1658 
1659  cr.save()
1660  rect = self.get_allocation()
1661  cr.translate(0.5*rect.width, 0.5*rect.height)
1662  cr.scale(self.zoom_ratio, self.zoom_ratio)
1663  cr.translate(-self.x, -self.y)
1664 
1665  self.graph.draw(cr, highlight_items=self.highlight)
1666  cr.restore()
1667 
1668  self.drag_action.draw(cr)
1669 
1670  return False
1671 
1672  def get_current_pos(self):
1673  return self.x, self.y
1674 
1675  def set_current_pos(self, x, y):
1676  self.x = x
1677  self.y = y
1678  self.queue_draw()
1679 
1680  def set_highlight(self, items):
1681  if self.highlight != items:
1682  self.highlight = items
1683  self.queue_draw()
1684 
1685  def zoom_image(self, zoom_ratio, center=False, pos=None):
1686  # Constrain zoom ratio to a sane range to prevent numeric instability.
1687  zoom_ratio = min(zoom_ratio, 1E4)
1688  zoom_ratio = max(zoom_ratio, 1E-6)
1689 
1690  if center:
1691  self.x = self.graph.width/2
1692  self.y = self.graph.height/2
1693  elif pos is not None:
1694  rect = self.get_allocation()
1695  x, y = pos
1696  x -= 0.5*rect.width
1697  y -= 0.5*rect.height
1698  self.x += x / self.zoom_ratio - x / zoom_ratio
1699  self.y += y / self.zoom_ratio - y / zoom_ratio
1700  self.zoom_ratio = zoom_ratio
1701  self.zoom_to_fit_on_resize = False
1702  self.queue_draw()
1703 
1704  def zoom_to_area(self, x1, y1, x2, y2):
1705  rect = self.get_allocation()
1706  width = abs(x1 - x2)
1707  height = abs(y1 - y2)
1708  if width == 0 and height == 0:
1709  self.zoom_ratio *= self.ZOOM_INCREMENT
1710  else:
1711  self.zoom_ratio = min(
1712  float(rect.width)/float(width),
1713  float(rect.height)/float(height)
1714  )
1715  self.zoom_to_fit_on_resize = False
1716  self.x = (x1 + x2) / 2
1717  self.y = (y1 + y2) / 2
1718  self.queue_draw()
1719 
1720  def zoom_to_fit(self):
1721  rect = self.get_allocation()
1722  rect.x += self.ZOOM_TO_FIT_MARGIN
1723  rect.y += self.ZOOM_TO_FIT_MARGIN
1724  rect.width -= 2 * self.ZOOM_TO_FIT_MARGIN
1725  rect.height -= 2 * self.ZOOM_TO_FIT_MARGIN
1726  zoom_ratio = min(
1727  float(rect.width)/float(self.graph.width),
1728  float(rect.height)/float(self.graph.height)
1729  )
1730  self.zoom_image(zoom_ratio, center=True)
1731  self.zoom_to_fit_on_resize = True
1732 
1733  ZOOM_INCREMENT = 1.25
1734  ZOOM_TO_FIT_MARGIN = 12
1735 
1736  def on_zoom_in(self, action):
1737  self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
1738 
1739  def on_zoom_out(self, action):
1740  self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
1741 
1742  def on_zoom_fit(self, action):
1743  self.zoom_to_fit()
1744 
1745  def on_zoom_100(self, action):
1746  self.zoom_image(1.0)
1747 
1748  POS_INCREMENT = 100
1749 
1750  def on_key_press_event(self, widget, event):
1751  if event.keyval == Gdk.KEY_Left:
1752  self.x -= self.POS_INCREMENT/self.zoom_ratio
1753  self.queue_draw()
1754  return True
1755  if event.keyval == Gdk.KEY_Right:
1756  self.x += self.POS_INCREMENT/self.zoom_ratio
1757  self.queue_draw()
1758  return True
1759  if event.keyval == Gdk.KEY_Up:
1760  self.y -= self.POS_INCREMENT/self.zoom_ratio
1761  self.queue_draw()
1762  return True
1763  if event.keyval == Gdk.KEY_Down:
1764  self.y += self.POS_INCREMENT/self.zoom_ratio
1765  self.queue_draw()
1766  return True
1767  if event.keyval in (Gdk.KEY_Page_Up,
1768  Gdk.KEY_plus,
1769  Gdk.KEY_equal,
1770  Gdk.KEY_KP_Add):
1771  self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
1772  self.queue_draw()
1773  return True
1774  if event.keyval in (Gdk.KEY_Page_Down,
1775  Gdk.KEY_minus,
1776  Gdk.KEY_KP_Subtract):
1777  self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
1778  self.queue_draw()
1779  return True
1780  if event.keyval == Gdk.KEY_Escape:
1781  self.drag_action.abort()
1782  self.drag_action = NullAction(self)
1783  return True
1784  if event.keyval == Gdk.KEY_r:
1785  self.reload()
1786  return True
1787  if event.keyval == Gdk.KEY_f:
1788  win = widget.get_toplevel()
1789  find_toolitem = win.uimanager.get_widget('/ToolBar/Find')
1790  textentry = find_toolitem.get_children()
1791  win.set_focus(textentry[0])
1792  return True
1793  if event.keyval == Gdk.KEY_q:
1794  Gtk.main_quit()
1795  return True
1796  if event.keyval == Gdk.KEY_p:
1797  self.on_print()
1798  return True
1799  return False
1800 
1801  print_settings = None
1802  def on_print(self, action=None):
1803  print_op = Gtk.PrintOperation()
1804 
1805  if self.print_settings != None:
1806  print_op.set_print_settings(self.print_settings)
1807 
1808  print_op.connect("begin_print", self.begin_print)
1809  print_op.connect("draw_page", self.draw_page)
1810 
1811  res = print_op.run(Gtk.PrintOperationAction.PRINT_DIALOG, self.get_toplevel())
1812  if res == Gtk.PrintOperationResult.APPLY:
1813  self.print_settings = print_op.get_print_settings()
1814 
1815  def begin_print(self, operation, context):
1816  operation.set_n_pages(1)
1817  return True
1818 
1819  def draw_page(self, operation, context, page_nr):
1820  cr = context.get_cairo_context()
1821 
1822  rect = self.get_allocation()
1823  cr.translate(0.5*rect.width, 0.5*rect.height)
1824  cr.scale(self.zoom_ratio, self.zoom_ratio)
1825  cr.translate(-self.x, -self.y)
1826 
1827  self.graph.draw(cr, highlight_items=self.highlight)
1828 
1829  def get_drag_action(self, event):
1830  state = event.state
1831  if event.button in (1, 2): # left or middle button
1832  modifiers = Gtk.accelerator_get_default_mod_mask()
1833  if state & modifiers == Gdk.ModifierType.CONTROL_MASK:
1834  return ZoomAction
1835  elif state & modifiers == Gdk.ModifierType.SHIFT_MASK:
1836  return ZoomAreaAction
1837  else:
1838  return PanAction
1839  return NullAction
1840 
1841  def on_area_button_press(self, area, event):
1842  self.animation.stop()
1843  self.drag_action.abort()
1844  action_type = self.get_drag_action(event)
1845  self.drag_action = action_type(self)
1846  self.drag_action.on_button_press(event)
1847  self.presstime = time.time()
1848  self.pressx = event.x
1849  self.pressy = event.y
1850  return False
1851 
1852  def is_click(self, event, click_fuzz=4, click_timeout=1.0):
1853  assert event.type == Gdk.EventType.BUTTON_RELEASE
1854  if self.presstime is None:
1855  # got a button release without seeing the press?
1856  return False
1857  # XXX instead of doing this complicated logic, shouldn't we listen
1858  # for gtk's clicked event instead?
1859  deltax = self.pressx - event.x
1860  deltay = self.pressy - event.y
1861  return (time.time() < self.presstime + click_timeout
1862  and math.hypot(deltax, deltay) < click_fuzz)
1863 
1864  def on_click(self, element, event):
1865  """Override this method in subclass to process
1866  click events. Note that element can be None
1867  (click on empty space)."""
1868  return False
1869 
1870  def on_area_button_release(self, area, event):
1871  self.drag_action.on_button_release(event)
1872  self.drag_action = NullAction(self)
1873  x, y = int(event.x), int(event.y)
1874  if self.is_click(event):
1875  el = self.get_element(x, y)
1876  if self.on_click(el, event):
1877  return True
1878 
1879  if event.button == 1:
1880  url = self.get_url(x, y)
1881  if url is not None:
1882  self.emit('clicked', url.url, event)
1883  else:
1884  jump = self.get_jump(x, y)
1885  if jump is not None:
1886  self.animate_to(jump.x, jump.y)
1887 
1888  return True
1889 
1890  if event.button == 1 or event.button == 2:
1891  return True
1892  return False
1893 
1894  def on_area_scroll_event(self, area, event):
1895  if event.direction == Gdk.ScrollDirection.UP:
1896  self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT,
1897  pos=(event.x, event.y))
1898  return True
1899  if event.direction == Gdk.ScrollDirection.DOWN:
1900  self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT,
1901  pos=(event.x, event.y))
1902  return True
1903  return False
1904 
1905  def on_area_motion_notify(self, area, event):
1906  self.drag_action.on_motion_notify(event)
1907  return True
1908 
1909  def on_area_size_allocate(self, area, allocation):
1910  if self.zoom_to_fit_on_resize:
1911  self.zoom_to_fit()
1912 
1913  def animate_to(self, x, y):
1914  self.animation = ZoomToAnimation(self, x, y)
1915  self.animation.start()
1916 
1917  def window2graph(self, x, y):
1918  rect = self.get_allocation()
1919  x -= 0.5*rect.width
1920  y -= 0.5*rect.height
1921  x /= self.zoom_ratio
1922  y /= self.zoom_ratio
1923  x += self.x
1924  y += self.y
1925  return x, y
1926 
1927  def get_element(self, x, y):
1928  x, y = self.window2graph(x, y)
1929  return self.graph.get_element(x, y)
1930 
1931  def get_url(self, x, y):
1932  x, y = self.window2graph(x, y)
1933  return self.graph.get_url(x, y)
1934 
1935  def get_jump(self, x, y):
1936  x, y = self.window2graph(x, y)
1937  return self.graph.get_jump(x, y)
1938 
1939 
1940 class FindMenuToolAction(Gtk.Action):
1941  __gtype_name__ = "FindMenuToolAction"
1942 
1944  return Gtk.ToolItem()
1945 
1946 
1947 class DotWindow(Gtk.Window):
1948 
1949  ui = '''
1950  <ui>
1951  <toolbar name="ToolBar">
1952  <toolitem action="Open"/>
1953  <toolitem action="Reload"/>
1954  <toolitem action="Print"/>
1955  <separator/>
1956  <toolitem action="ZoomIn"/>
1957  <toolitem action="ZoomOut"/>
1958  <toolitem action="ZoomFit"/>
1959  <toolitem action="Zoom100"/>
1960  <separator/>
1961  <toolitem name="Find" action="Find"/>
1962  </toolbar>
1963  </ui>
1964  '''
1965 
1966  base_title = 'Dot Viewer'
1967 
1968  def __init__(self, widget=None):
1969  Gtk.Window.__init__(self)
1970 
1971  self.graph = Graph()
1972 
1973  window = self
1974 
1975  window.set_title(self.base_title)
1976  window.set_default_size(512, 512)
1977  vbox = Gtk.VBox()
1978  window.add(vbox)
1979 
1980  self.dotwidget = widget or DotWidget()
1981 
1982  # Create a UIManager instance
1983  uimanager = self.uimanager = Gtk.UIManager()
1984 
1985  # Add the accelerator group to the toplevel window
1986  accelgroup = uimanager.get_accel_group()
1987  window.add_accel_group(accelgroup)
1988 
1989  # Create an ActionGroup
1990  actiongroup = Gtk.ActionGroup('Actions')
1991  self.actiongroup = actiongroup
1992 
1993  # Create actions
1994  actiongroup.add_actions((
1995  ('Open', Gtk.STOCK_OPEN, None, None, None, self.on_open),
1996  ('Reload', Gtk.STOCK_REFRESH, None, None, None, self.on_reload),
1997  ('Print', Gtk.STOCK_PRINT, None, None, "Prints the currently visible part of the graph", self.dotwidget.on_print),
1998  ('ZoomIn', Gtk.STOCK_ZOOM_IN, None, None, None, self.dotwidget.on_zoom_in),
1999  ('ZoomOut', Gtk.STOCK_ZOOM_OUT, None, None, None, self.dotwidget.on_zoom_out),
2000  ('ZoomFit', Gtk.STOCK_ZOOM_FIT, None, None, None, self.dotwidget.on_zoom_fit),
2001  ('Zoom100', Gtk.STOCK_ZOOM_100, None, None, None, self.dotwidget.on_zoom_100),
2002  ))
2003 
2004  find_action = FindMenuToolAction("Find", None,
2005  "Find a node by name", None)
2006  actiongroup.add_action(find_action)
2007 
2008  # Add the actiongroup to the uimanager
2009  uimanager.insert_action_group(actiongroup, 0)
2010 
2011  # Add a UI descrption
2012  uimanager.add_ui_from_string(self.ui)
2013 
2014  # Create a Toolbar
2015  toolbar = uimanager.get_widget('/ToolBar')
2016  vbox.pack_start(toolbar, False, False, 0)
2017 
2018  vbox.pack_start(self.dotwidget, True, True, 0)
2019 
2020  self.last_open_dir = "."
2021 
2022  self.set_focus(self.dotwidget)
2023 
2024  # Add Find text search
2025  find_toolitem = uimanager.get_widget('/ToolBar/Find')
2026  self.textentry = Gtk.Entry(max_length=20)
2027  self.textentry.set_icon_from_stock(0, Gtk.STOCK_FIND)
2028  find_toolitem.add(self.textentry)
2029 
2030  self.textentry.set_activates_default(True)
2031  self.textentry.connect ("activate", self.textentry_activate, self.textentry);
2032  self.textentry.connect ("changed", self.textentry_changed, self.textentry);
2033 
2034  self.show_all()
2035 
2036  def find_text(self, entry_text):
2037  found_items = []
2038  dot_widget = self.dotwidget
2039  regexp = re.compile(entry_text)
2040  for node in dot_widget.graph.nodes:
2041  if node.search_text(regexp):
2042  found_items.append(node)
2043  return found_items
2044 
2045  def textentry_changed(self, widget, entry):
2046  entry_text = entry.get_text()
2047  dot_widget = self.dotwidget
2048  if not entry_text:
2049  dot_widget.set_highlight(None)
2050  return
2051 
2052  found_items = self.find_text(entry_text)
2053  dot_widget.set_highlight(found_items)
2054 
2055  def textentry_activate(self, widget, entry):
2056  entry_text = entry.get_text()
2057  dot_widget = self.dotwidget
2058  if not entry_text:
2059  dot_widget.set_highlight(None)
2060  return;
2061 
2062  found_items = self.find_text(entry_text)
2063  dot_widget.set_highlight(found_items)
2064  if(len(found_items) == 1):
2065  dot_widget.animate_to(found_items[0].x, found_items[0].y)
2066 
2067  def set_filter(self, filter):
2068  self.dotwidget.set_filter(filter)
2069 
2070  def set_dotcode(self, dotcode, filename=None):
2071  if self.dotwidget.set_dotcode(dotcode, filename):
2072  self.update_title(filename)
2073  self.dotwidget.zoom_to_fit()
2074 
2075  def set_xdotcode(self, xdotcode, filename=None):
2076  if self.dotwidget.set_xdotcode(xdotcode):
2077  self.update_title(filename)
2078  self.dotwidget.zoom_to_fit()
2079 
2080  def update_title(self, filename=None):
2081  if filename is None:
2082  self.set_title(self.base_title)
2083  else:
2084  self.set_title(os.path.basename(filename) + ' - ' + self.base_title)
2085 
2086  def open_file(self, filename):
2087  try:
2088  fp = open(filename, 'rt')
2089  self.set_dotcode(fp.read(), filename)
2090  fp.close()
2091  except IOError as ex:
2092  self.error_dialog(str(ex))
2093 
2094  def on_open(self, action):
2095  chooser = Gtk.FileChooserDialog(title="Open dot File",
2096  action=Gtk.FileChooserAction.OPEN,
2097  buttons=(Gtk.STOCK_CANCEL,
2098  Gtk.ResponseType.CANCEL,
2099  Gtk.STOCK_OPEN,
2100  Gtk.ResponseType.OK))
2101  chooser.set_default_response(Gtk.ResponseType.OK)
2102  chooser.set_current_folder(self.last_open_dir)
2103  filter = Gtk.FileFilter()
2104  filter.set_name("Graphviz dot files")
2105  filter.add_pattern("*.dot")
2106  chooser.add_filter(filter)
2107  filter = Gtk.FileFilter()
2108  filter.set_name("All files")
2109  filter.add_pattern("*")
2110  chooser.add_filter(filter)
2111  if chooser.run() == Gtk.ResponseType.OK:
2112  filename = chooser.get_filename()
2113  self.last_open_dir = chooser.get_current_folder()
2114  chooser.destroy()
2115  self.open_file(filename)
2116  else:
2117  chooser.destroy()
2118 
2119  def on_reload(self, action):
2120  self.dotwidget.reload()
2121 
2122  def error_dialog(self, message):
2123  dlg = Gtk.MessageDialog(type=Gtk.MessageType.ERROR,
2124  message_format=message,
2125  buttons=Gtk.ButtonsType.OK)
2126  dlg.set_title(self.base_title)
2127  dlg.run()
2128  dlg.destroy()
2129 
2130 
2131 class OptionParser(optparse.OptionParser):
2132 
2133  def format_epilog(self, formatter):
2134  # Prevent stripping the newlines in epilog message
2135  # http://stackoverflow.com/questions/1857346/python-optparse-how-to-include-additional-info-in-usage-output
2136  return self.epilog
2137 
2138 
2139 def main():
2140 
2141  parser = OptionParser(
2142  usage='\n\t%prog [file]',
2143  epilog='''
2144 Shortcuts:
2145  Up, Down, Left, Right scroll
2146  PageUp, +, = zoom in
2147  PageDown, - zoom out
2148  R reload dot file
2149  F find
2150  Q quit
2151  P print
2152  Escape halt animation
2153  Ctrl-drag zoom in/out
2154  Shift-drag zooms an area
2155 '''
2156  )
2157  parser.add_option(
2158  '-f', '--filter',
2159  type='choice', choices=('dot', 'neato', 'twopi', 'circo', 'fdp'),
2160  dest='filter', default='dot',
2161  help='graphviz filter: dot, neato, twopi, circo, or fdp [default: %default]')
2162  parser.add_option(
2163  '-n', '--no-filter',
2164  action='store_const', const=None, dest='filter',
2165  help='assume input is already filtered into xdot format (use e.g. dot -Txdot)')
2166 
2167  (options, args) = parser.parse_args(sys.argv[1:])
2168  if len(args) > 1:
2169  parser.error('incorrect number of arguments')
2170 
2171  win = DotWindow()
2172  win.connect('delete-event', Gtk.main_quit)
2173  win.set_filter(options.filter)
2174  if len(args) >= 1:
2175  if args[0] == '-':
2176  win.set_dotcode(sys.stdin.read())
2177  else:
2178  win.open_file(args[0])
2179  Gtk.main()
2180 
2181 
2182 # Apache-Style Software License for ColorBrewer software and ColorBrewer Color
2183 # Schemes, Version 1.1
2184 #
2185 # Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State
2186 # University. All rights reserved.
2187 #
2188 # Redistribution and use in source and binary forms, with or without
2189 # modification, are permitted provided that the following conditions are met:
2190 #
2191 # 1. Redistributions as source code must retain the above copyright notice,
2192 # this list of conditions and the following disclaimer.
2193 #
2194 # 2. The end-user documentation included with the redistribution, if any,
2195 # must include the following acknowledgment:
2196 #
2197 # This product includes color specifications and designs developed by
2198 # Cynthia Brewer (http://colorbrewer.org/).
2199 #
2200 # Alternately, this acknowledgment may appear in the software itself, if and
2201 # wherever such third-party acknowledgments normally appear.
2202 #
2203 # 3. The name "ColorBrewer" must not be used to endorse or promote products
2204 # derived from this software without prior written permission. For written
2205 # permission, please contact Cynthia Brewer at cbrewer@psu.edu.
2206 #
2207 # 4. Products derived from this software may not be called "ColorBrewer",
2208 # nor may "ColorBrewer" appear in their name, without prior written
2209 # permission of Cynthia Brewer.
2210 #
2211 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
2212 # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
2213 # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CYNTHIA
2214 # BREWER, MARK HARROWER, OR THE PENNSYLVANIA STATE UNIVERSITY BE LIABLE FOR ANY
2215 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2216 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2217 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2218 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2219 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2220 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2221 brewer_colors = {
2222  'accent3': [(127, 201, 127), (190, 174, 212), (253, 192, 134)],
2223  'accent4': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153)],
2224  'accent5': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176)],
2225  'accent6': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127)],
2226  'accent7': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23)],
2227  'accent8': [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), (56, 108, 176), (240, 2, 127), (191, 91, 23), (102, 102, 102)],
2228  'blues3': [(222, 235, 247), (158, 202, 225), (49, 130, 189)],
2229  'blues4': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (33, 113, 181)],
2230  'blues5': [(239, 243, 255), (189, 215, 231), (107, 174, 214), (49, 130, 189), (8, 81, 156)],
2231  'blues6': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (49, 130, 189), (8, 81, 156)],
2232  'blues7': [(239, 243, 255), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)],
2233  'blues8': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 69, 148)],
2234  'blues9': [(247, 251, 255), (222, 235, 247), (198, 219, 239), (158, 202, 225), (107, 174, 214), (66, 146, 198), (33, 113, 181), (8, 81, 156), (8, 48, 107)],
2235  'brbg10': [(84, 48, 5), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)],
2236  'brbg11': [(84, 48, 5), (1, 102, 94), (0, 60, 48), (140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143)],
2237  'brbg3': [(216, 179, 101), (245, 245, 245), (90, 180, 172)],
2238  'brbg4': [(166, 97, 26), (223, 194, 125), (128, 205, 193), (1, 133, 113)],
2239  'brbg5': [(166, 97, 26), (223, 194, 125), (245, 245, 245), (128, 205, 193), (1, 133, 113)],
2240  'brbg6': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (199, 234, 229), (90, 180, 172), (1, 102, 94)],
2241  'brbg7': [(140, 81, 10), (216, 179, 101), (246, 232, 195), (245, 245, 245), (199, 234, 229), (90, 180, 172), (1, 102, 94)],
2242  'brbg8': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)],
2243  'brbg9': [(140, 81, 10), (191, 129, 45), (223, 194, 125), (246, 232, 195), (245, 245, 245), (199, 234, 229), (128, 205, 193), (53, 151, 143), (1, 102, 94)],
2244  'bugn3': [(229, 245, 249), (153, 216, 201), (44, 162, 95)],
2245  'bugn4': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (35, 139, 69)],
2246  'bugn5': [(237, 248, 251), (178, 226, 226), (102, 194, 164), (44, 162, 95), (0, 109, 44)],
2247  'bugn6': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (44, 162, 95), (0, 109, 44)],
2248  'bugn7': [(237, 248, 251), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)],
2249  'bugn8': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 88, 36)],
2250  'bugn9': [(247, 252, 253), (229, 245, 249), (204, 236, 230), (153, 216, 201), (102, 194, 164), (65, 174, 118), (35, 139, 69), (0, 109, 44), (0, 68, 27)],
2251  'bupu3': [(224, 236, 244), (158, 188, 218), (136, 86, 167)],
2252  'bupu4': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 65, 157)],
2253  'bupu5': [(237, 248, 251), (179, 205, 227), (140, 150, 198), (136, 86, 167), (129, 15, 124)],
2254  'bupu6': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (136, 86, 167), (129, 15, 124)],
2255  'bupu7': [(237, 248, 251), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)],
2256  'bupu8': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (110, 1, 107)],
2257  'bupu9': [(247, 252, 253), (224, 236, 244), (191, 211, 230), (158, 188, 218), (140, 150, 198), (140, 107, 177), (136, 65, 157), (129, 15, 124), (77, 0, 75)],
2258  'dark23': [(27, 158, 119), (217, 95, 2), (117, 112, 179)],
2259  'dark24': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138)],
2260  'dark25': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30)],
2261  'dark26': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2)],
2262  'dark27': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29)],
2263  'dark28': [(27, 158, 119), (217, 95, 2), (117, 112, 179), (231, 41, 138), (102, 166, 30), (230, 171, 2), (166, 118, 29), (102, 102, 102)],
2264  'gnbu3': [(224, 243, 219), (168, 221, 181), (67, 162, 202)],
2265  'gnbu4': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (43, 140, 190)],
2266  'gnbu5': [(240, 249, 232), (186, 228, 188), (123, 204, 196), (67, 162, 202), (8, 104, 172)],
2267  'gnbu6': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (67, 162, 202), (8, 104, 172)],
2268  'gnbu7': [(240, 249, 232), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)],
2269  'gnbu8': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 88, 158)],
2270  'gnbu9': [(247, 252, 240), (224, 243, 219), (204, 235, 197), (168, 221, 181), (123, 204, 196), (78, 179, 211), (43, 140, 190), (8, 104, 172), (8, 64, 129)],
2271  'greens3': [(229, 245, 224), (161, 217, 155), (49, 163, 84)],
2272  'greens4': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (35, 139, 69)],
2273  'greens5': [(237, 248, 233), (186, 228, 179), (116, 196, 118), (49, 163, 84), (0, 109, 44)],
2274  'greens6': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (49, 163, 84), (0, 109, 44)],
2275  'greens7': [(237, 248, 233), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)],
2276  'greens8': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 90, 50)],
2277  'greens9': [(247, 252, 245), (229, 245, 224), (199, 233, 192), (161, 217, 155), (116, 196, 118), (65, 171, 93), (35, 139, 69), (0, 109, 44), (0, 68, 27)],
2278  'greys3': [(240, 240, 240), (189, 189, 189), (99, 99, 99)],
2279  'greys4': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (82, 82, 82)],
2280  'greys5': [(247, 247, 247), (204, 204, 204), (150, 150, 150), (99, 99, 99), (37, 37, 37)],
2281  'greys6': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (99, 99, 99), (37, 37, 37)],
2282  'greys7': [(247, 247, 247), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)],
2283  'greys8': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37)],
2284  'greys9': [(255, 255, 255), (240, 240, 240), (217, 217, 217), (189, 189, 189), (150, 150, 150), (115, 115, 115), (82, 82, 82), (37, 37, 37), (0, 0, 0)],
2285  'oranges3': [(254, 230, 206), (253, 174, 107), (230, 85, 13)],
2286  'oranges4': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (217, 71, 1)],
2287  'oranges5': [(254, 237, 222), (253, 190, 133), (253, 141, 60), (230, 85, 13), (166, 54, 3)],
2288  'oranges6': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (230, 85, 13), (166, 54, 3)],
2289  'oranges7': [(254, 237, 222), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)],
2290  'oranges8': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (140, 45, 4)],
2291  'oranges9': [(255, 245, 235), (254, 230, 206), (253, 208, 162), (253, 174, 107), (253, 141, 60), (241, 105, 19), (217, 72, 1), (166, 54, 3), (127, 39, 4)],
2292  'orrd3': [(254, 232, 200), (253, 187, 132), (227, 74, 51)],
2293  'orrd4': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (215, 48, 31)],
2294  'orrd5': [(254, 240, 217), (253, 204, 138), (252, 141, 89), (227, 74, 51), (179, 0, 0)],
2295  'orrd6': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (227, 74, 51), (179, 0, 0)],
2296  'orrd7': [(254, 240, 217), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)],
2297  'orrd8': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (153, 0, 0)],
2298  'orrd9': [(255, 247, 236), (254, 232, 200), (253, 212, 158), (253, 187, 132), (252, 141, 89), (239, 101, 72), (215, 48, 31), (179, 0, 0), (127, 0, 0)],
2299  'paired10': [(166, 206, 227), (106, 61, 154), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
2300  'paired11': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
2301  'paired12': [(166, 206, 227), (106, 61, 154), (255, 255, 153), (177, 89, 40), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
2302  'paired3': [(166, 206, 227), (31, 120, 180), (178, 223, 138)],
2303  'paired4': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44)],
2304  'paired5': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153)],
2305  'paired6': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28)],
2306  'paired7': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111)],
2307  'paired8': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0)],
2308  'paired9': [(166, 206, 227), (31, 120, 180), (178, 223, 138), (51, 160, 44), (251, 154, 153), (227, 26, 28), (253, 191, 111), (255, 127, 0), (202, 178, 214)],
2309  'pastel13': [(251, 180, 174), (179, 205, 227), (204, 235, 197)],
2310  'pastel14': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228)],
2311  'pastel15': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166)],
2312  'pastel16': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204)],
2313  'pastel17': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189)],
2314  'pastel18': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236)],
2315  'pastel19': [(251, 180, 174), (179, 205, 227), (204, 235, 197), (222, 203, 228), (254, 217, 166), (255, 255, 204), (229, 216, 189), (253, 218, 236), (242, 242, 242)],
2316  'pastel23': [(179, 226, 205), (253, 205, 172), (203, 213, 232)],
2317  'pastel24': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228)],
2318  'pastel25': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201)],
2319  'pastel26': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174)],
2320  'pastel27': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204)],
2321  'pastel28': [(179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204), (204, 204, 204)],
2322  'piyg10': [(142, 1, 82), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)],
2323  'piyg11': [(142, 1, 82), (77, 146, 33), (39, 100, 25), (197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65)],
2324  'piyg3': [(233, 163, 201), (247, 247, 247), (161, 215, 106)],
2325  'piyg4': [(208, 28, 139), (241, 182, 218), (184, 225, 134), (77, 172, 38)],
2326  'piyg5': [(208, 28, 139), (241, 182, 218), (247, 247, 247), (184, 225, 134), (77, 172, 38)],
2327  'piyg6': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (230, 245, 208), (161, 215, 106), (77, 146, 33)],
2328  'piyg7': [(197, 27, 125), (233, 163, 201), (253, 224, 239), (247, 247, 247), (230, 245, 208), (161, 215, 106), (77, 146, 33)],
2329  'piyg8': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)],
2330  'piyg9': [(197, 27, 125), (222, 119, 174), (241, 182, 218), (253, 224, 239), (247, 247, 247), (230, 245, 208), (184, 225, 134), (127, 188, 65), (77, 146, 33)],
2331  'prgn10': [(64, 0, 75), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)],
2332  'prgn11': [(64, 0, 75), (27, 120, 55), (0, 68, 27), (118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97)],
2333  'prgn3': [(175, 141, 195), (247, 247, 247), (127, 191, 123)],
2334  'prgn4': [(123, 50, 148), (194, 165, 207), (166, 219, 160), (0, 136, 55)],
2335  'prgn5': [(123, 50, 148), (194, 165, 207), (247, 247, 247), (166, 219, 160), (0, 136, 55)],
2336  'prgn6': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (217, 240, 211), (127, 191, 123), (27, 120, 55)],
2337  'prgn7': [(118, 42, 131), (175, 141, 195), (231, 212, 232), (247, 247, 247), (217, 240, 211), (127, 191, 123), (27, 120, 55)],
2338  'prgn8': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)],
2339  'prgn9': [(118, 42, 131), (153, 112, 171), (194, 165, 207), (231, 212, 232), (247, 247, 247), (217, 240, 211), (166, 219, 160), (90, 174, 97), (27, 120, 55)],
2340  'pubu3': [(236, 231, 242), (166, 189, 219), (43, 140, 190)],
2341  'pubu4': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (5, 112, 176)],
2342  'pubu5': [(241, 238, 246), (189, 201, 225), (116, 169, 207), (43, 140, 190), (4, 90, 141)],
2343  'pubu6': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (43, 140, 190), (4, 90, 141)],
2344  'pubu7': [(241, 238, 246), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)],
2345  'pubu8': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (3, 78, 123)],
2346  'pubu9': [(255, 247, 251), (236, 231, 242), (208, 209, 230), (166, 189, 219), (116, 169, 207), (54, 144, 192), (5, 112, 176), (4, 90, 141), (2, 56, 88)],
2347  'pubugn3': [(236, 226, 240), (166, 189, 219), (28, 144, 153)],
2348  'pubugn4': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (2, 129, 138)],
2349  'pubugn5': [(246, 239, 247), (189, 201, 225), (103, 169, 207), (28, 144, 153), (1, 108, 89)],
2350  'pubugn6': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (28, 144, 153), (1, 108, 89)],
2351  'pubugn7': [(246, 239, 247), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)],
2352  'pubugn8': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 100, 80)],
2353  'pubugn9': [(255, 247, 251), (236, 226, 240), (208, 209, 230), (166, 189, 219), (103, 169, 207), (54, 144, 192), (2, 129, 138), (1, 108, 89), (1, 70, 54)],
2354  'puor10': [(127, 59, 8), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)],
2355  'puor11': [(127, 59, 8), (84, 39, 136), (45, 0, 75), (179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172)],
2356  'puor3': [(241, 163, 64), (247, 247, 247), (153, 142, 195)],
2357  'puor4': [(230, 97, 1), (253, 184, 99), (178, 171, 210), (94, 60, 153)],
2358  'puor5': [(230, 97, 1), (253, 184, 99), (247, 247, 247), (178, 171, 210), (94, 60, 153)],
2359  'puor6': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (216, 218, 235), (153, 142, 195), (84, 39, 136)],
2360  'puor7': [(179, 88, 6), (241, 163, 64), (254, 224, 182), (247, 247, 247), (216, 218, 235), (153, 142, 195), (84, 39, 136)],
2361  'puor8': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)],
2362  'puor9': [(179, 88, 6), (224, 130, 20), (253, 184, 99), (254, 224, 182), (247, 247, 247), (216, 218, 235), (178, 171, 210), (128, 115, 172), (84, 39, 136)],
2363  'purd3': [(231, 225, 239), (201, 148, 199), (221, 28, 119)],
2364  'purd4': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (206, 18, 86)],
2365  'purd5': [(241, 238, 246), (215, 181, 216), (223, 101, 176), (221, 28, 119), (152, 0, 67)],
2366  'purd6': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (221, 28, 119), (152, 0, 67)],
2367  'purd7': [(241, 238, 246), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)],
2368  'purd8': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (145, 0, 63)],
2369  'purd9': [(247, 244, 249), (231, 225, 239), (212, 185, 218), (201, 148, 199), (223, 101, 176), (231, 41, 138), (206, 18, 86), (152, 0, 67), (103, 0, 31)],
2370  'purples3': [(239, 237, 245), (188, 189, 220), (117, 107, 177)],
2371  'purples4': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (106, 81, 163)],
2372  'purples5': [(242, 240, 247), (203, 201, 226), (158, 154, 200), (117, 107, 177), (84, 39, 143)],
2373  'purples6': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (117, 107, 177), (84, 39, 143)],
2374  'purples7': [(242, 240, 247), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)],
2375  'purples8': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (74, 20, 134)],
2376  'purples9': [(252, 251, 253), (239, 237, 245), (218, 218, 235), (188, 189, 220), (158, 154, 200), (128, 125, 186), (106, 81, 163), (84, 39, 143), (63, 0, 125)],
2377  'rdbu10': [(103, 0, 31), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)],
2378  'rdbu11': [(103, 0, 31), (33, 102, 172), (5, 48, 97), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195)],
2379  'rdbu3': [(239, 138, 98), (247, 247, 247), (103, 169, 207)],
2380  'rdbu4': [(202, 0, 32), (244, 165, 130), (146, 197, 222), (5, 113, 176)],
2381  'rdbu5': [(202, 0, 32), (244, 165, 130), (247, 247, 247), (146, 197, 222), (5, 113, 176)],
2382  'rdbu6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (209, 229, 240), (103, 169, 207), (33, 102, 172)],
2383  'rdbu7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (247, 247, 247), (209, 229, 240), (103, 169, 207), (33, 102, 172)],
2384  'rdbu8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)],
2385  'rdbu9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (247, 247, 247), (209, 229, 240), (146, 197, 222), (67, 147, 195), (33, 102, 172)],
2386  'rdgy10': [(103, 0, 31), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)],
2387  'rdgy11': [(103, 0, 31), (77, 77, 77), (26, 26, 26), (178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135)],
2388  'rdgy3': [(239, 138, 98), (255, 255, 255), (153, 153, 153)],
2389  'rdgy4': [(202, 0, 32), (244, 165, 130), (186, 186, 186), (64, 64, 64)],
2390  'rdgy5': [(202, 0, 32), (244, 165, 130), (255, 255, 255), (186, 186, 186), (64, 64, 64)],
2391  'rdgy6': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (224, 224, 224), (153, 153, 153), (77, 77, 77)],
2392  'rdgy7': [(178, 24, 43), (239, 138, 98), (253, 219, 199), (255, 255, 255), (224, 224, 224), (153, 153, 153), (77, 77, 77)],
2393  'rdgy8': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)],
2394  'rdgy9': [(178, 24, 43), (214, 96, 77), (244, 165, 130), (253, 219, 199), (255, 255, 255), (224, 224, 224), (186, 186, 186), (135, 135, 135), (77, 77, 77)],
2395  'rdpu3': [(253, 224, 221), (250, 159, 181), (197, 27, 138)],
2396  'rdpu4': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (174, 1, 126)],
2397  'rdpu5': [(254, 235, 226), (251, 180, 185), (247, 104, 161), (197, 27, 138), (122, 1, 119)],
2398  'rdpu6': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (197, 27, 138), (122, 1, 119)],
2399  'rdpu7': [(254, 235, 226), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)],
2400  'rdpu8': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119)],
2401  'rdpu9': [(255, 247, 243), (253, 224, 221), (252, 197, 192), (250, 159, 181), (247, 104, 161), (221, 52, 151), (174, 1, 126), (122, 1, 119), (73, 0, 106)],
2402  'rdylbu10': [(165, 0, 38), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)],
2403  'rdylbu11': [(165, 0, 38), (69, 117, 180), (49, 54, 149), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209)],
2404  'rdylbu3': [(252, 141, 89), (255, 255, 191), (145, 191, 219)],
2405  'rdylbu4': [(215, 25, 28), (253, 174, 97), (171, 217, 233), (44, 123, 182)],
2406  'rdylbu5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 217, 233), (44, 123, 182)],
2407  'rdylbu6': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (224, 243, 248), (145, 191, 219), (69, 117, 180)],
2408  'rdylbu7': [(215, 48, 39), (252, 141, 89), (254, 224, 144), (255, 255, 191), (224, 243, 248), (145, 191, 219), (69, 117, 180)],
2409  'rdylbu8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)],
2410  'rdylbu9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 144), (255, 255, 191), (224, 243, 248), (171, 217, 233), (116, 173, 209), (69, 117, 180)],
2411  'rdylgn10': [(165, 0, 38), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)],
2412  'rdylgn11': [(165, 0, 38), (26, 152, 80), (0, 104, 55), (215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99)],
2413  'rdylgn3': [(252, 141, 89), (255, 255, 191), (145, 207, 96)],
2414  'rdylgn4': [(215, 25, 28), (253, 174, 97), (166, 217, 106), (26, 150, 65)],
2415  'rdylgn5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (166, 217, 106), (26, 150, 65)],
2416  'rdylgn6': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (217, 239, 139), (145, 207, 96), (26, 152, 80)],
2417  'rdylgn7': [(215, 48, 39), (252, 141, 89), (254, 224, 139), (255, 255, 191), (217, 239, 139), (145, 207, 96), (26, 152, 80)],
2418  'rdylgn8': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)],
2419  'rdylgn9': [(215, 48, 39), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (217, 239, 139), (166, 217, 106), (102, 189, 99), (26, 152, 80)],
2420  'reds3': [(254, 224, 210), (252, 146, 114), (222, 45, 38)],
2421  'reds4': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (203, 24, 29)],
2422  'reds5': [(254, 229, 217), (252, 174, 145), (251, 106, 74), (222, 45, 38), (165, 15, 21)],
2423  'reds6': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (222, 45, 38), (165, 15, 21)],
2424  'reds7': [(254, 229, 217), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)],
2425  'reds8': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (153, 0, 13)],
2426  'reds9': [(255, 245, 240), (254, 224, 210), (252, 187, 161), (252, 146, 114), (251, 106, 74), (239, 59, 44), (203, 24, 29), (165, 15, 21), (103, 0, 13)],
2427  'set13': [(228, 26, 28), (55, 126, 184), (77, 175, 74)],
2428  'set14': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163)],
2429  'set15': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0)],
2430  'set16': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51)],
2431  'set17': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40)],
2432  'set18': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191)],
2433  'set19': [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), (255, 255, 51), (166, 86, 40), (247, 129, 191), (153, 153, 153)],
2434  'set23': [(102, 194, 165), (252, 141, 98), (141, 160, 203)],
2435  'set24': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195)],
2436  'set25': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84)],
2437  'set26': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47)],
2438  'set27': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148)],
2439  'set28': [(102, 194, 165), (252, 141, 98), (141, 160, 203), (231, 138, 195), (166, 216, 84), (255, 217, 47), (229, 196, 148), (179, 179, 179)],
2440  'set310': [(141, 211, 199), (188, 128, 189), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
2441  'set311': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
2442  'set312': [(141, 211, 199), (188, 128, 189), (204, 235, 197), (255, 237, 111), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
2443  'set33': [(141, 211, 199), (255, 255, 179), (190, 186, 218)],
2444  'set34': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114)],
2445  'set35': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211)],
2446  'set36': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98)],
2447  'set37': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105)],
2448  'set38': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229)],
2449  'set39': [(141, 211, 199), (255, 255, 179), (190, 186, 218), (251, 128, 114), (128, 177, 211), (253, 180, 98), (179, 222, 105), (252, 205, 229), (217, 217, 217)],
2450  'spectral10': [(158, 1, 66), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)],
2451  'spectral11': [(158, 1, 66), (50, 136, 189), (94, 79, 162), (213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165)],
2452  'spectral3': [(252, 141, 89), (255, 255, 191), (153, 213, 148)],
2453  'spectral4': [(215, 25, 28), (253, 174, 97), (171, 221, 164), (43, 131, 186)],
2454  'spectral5': [(215, 25, 28), (253, 174, 97), (255, 255, 191), (171, 221, 164), (43, 131, 186)],
2455  'spectral6': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (230, 245, 152), (153, 213, 148), (50, 136, 189)],
2456  'spectral7': [(213, 62, 79), (252, 141, 89), (254, 224, 139), (255, 255, 191), (230, 245, 152), (153, 213, 148), (50, 136, 189)],
2457  'spectral8': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)],
2458  'spectral9': [(213, 62, 79), (244, 109, 67), (253, 174, 97), (254, 224, 139), (255, 255, 191), (230, 245, 152), (171, 221, 164), (102, 194, 165), (50, 136, 189)],
2459  'ylgn3': [(247, 252, 185), (173, 221, 142), (49, 163, 84)],
2460  'ylgn4': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (35, 132, 67)],
2461  'ylgn5': [(255, 255, 204), (194, 230, 153), (120, 198, 121), (49, 163, 84), (0, 104, 55)],
2462  'ylgn6': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (49, 163, 84), (0, 104, 55)],
2463  'ylgn7': [(255, 255, 204), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)],
2464  'ylgn8': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 90, 50)],
2465  'ylgn9': [(255, 255, 229), (247, 252, 185), (217, 240, 163), (173, 221, 142), (120, 198, 121), (65, 171, 93), (35, 132, 67), (0, 104, 55), (0, 69, 41)],
2466  'ylgnbu3': [(237, 248, 177), (127, 205, 187), (44, 127, 184)],
2467  'ylgnbu4': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (34, 94, 168)],
2468  'ylgnbu5': [(255, 255, 204), (161, 218, 180), (65, 182, 196), (44, 127, 184), (37, 52, 148)],
2469  'ylgnbu6': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (44, 127, 184), (37, 52, 148)],
2470  'ylgnbu7': [(255, 255, 204), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)],
2471  'ylgnbu8': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (12, 44, 132)],
2472  'ylgnbu9': [(255, 255, 217), (237, 248, 177), (199, 233, 180), (127, 205, 187), (65, 182, 196), (29, 145, 192), (34, 94, 168), (37, 52, 148), (8, 29, 88)],
2473  'ylorbr3': [(255, 247, 188), (254, 196, 79), (217, 95, 14)],
2474  'ylorbr4': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (204, 76, 2)],
2475  'ylorbr5': [(255, 255, 212), (254, 217, 142), (254, 153, 41), (217, 95, 14), (153, 52, 4)],
2476  'ylorbr6': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (217, 95, 14), (153, 52, 4)],
2477  'ylorbr7': [(255, 255, 212), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)],
2478  'ylorbr8': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (140, 45, 4)],
2479  'ylorbr9': [(255, 255, 229), (255, 247, 188), (254, 227, 145), (254, 196, 79), (254, 153, 41), (236, 112, 20), (204, 76, 2), (153, 52, 4), (102, 37, 6)],
2480  'ylorrd3': [(255, 237, 160), (254, 178, 76), (240, 59, 32)],
2481  'ylorrd4': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (227, 26, 28)],
2482  'ylorrd5': [(255, 255, 178), (254, 204, 92), (253, 141, 60), (240, 59, 32), (189, 0, 38)],
2483  'ylorrd6': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (240, 59, 32), (189, 0, 38)],
2484  'ylorrd7': [(255, 255, 178), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)],
2485  'ylorrd8': [(255, 255, 204), (255, 237, 160), (254, 217, 118), (254, 178, 76), (253, 141, 60), (252, 78, 42), (227, 26, 28), (177, 0, 38)],
2486 }
2487 
2488 
2489 if __name__ == '__main__':
2490  main()
smach_viewer.xdot.xdot.Shape.__init__
def __init__(self)
Definition: xdot.py:92
smach_viewer.xdot.xdot.DotParser.parse_id
def parse_id(self)
Definition: xdot.py:1196
smach_viewer.xdot.xdot.Node.id
id
Definition: xdot.py:421
smach_viewer.xdot.xdot.Animation.timeout_id
timeout_id
Definition: xdot.py:1337
smach_viewer.xdot.xdot.XDotAttrParser.handle_bezier
def handle_bezier(self, points, filled=False)
Definition: xdot.py:797
smach_viewer.xdot.xdot.XDotAttrParser.read_float
def read_float(self)
Definition: xdot.py:595
smach_viewer.xdot.xdot.Element.get_jump
def get_jump(self, x, y)
Definition: xdot.py:412
smach_viewer.xdot.xdot.DotWidget.filter
string filter
Definition: xdot.py:1548
smach_viewer.xdot.xdot.DotWidget.animation
animation
Definition: xdot.py:1578
smach_viewer.xdot.xdot.Animation.step
float step
Definition: xdot.py:1333
smach_viewer.xdot.xdot.DragAction.dot_widget
dot_widget
Definition: xdot.py:1424
smach_viewer.xdot.xdot.Token
Definition: xdot.py:860
smach_viewer.xdot.xdot.XDotParser.XDOTVERSION
string XDOTVERSION
Definition: xdot.py:1214
smach_viewer.xdot.xdot.Pen.copy
def copy(self)
Definition: xdot.py:76
smach_viewer.xdot.xdot.Token.col
col
Definition: xdot.py:866
smach_viewer.xdot.xdot.PolygonShape.pen
pen
Definition: xdot.py:292
smach_viewer.xdot.xdot.Node.x1
x1
Definition: xdot.py:425
smach_viewer.xdot.xdot.FindMenuToolAction.do_create_tool_item
def do_create_tool_item(self)
Definition: xdot.py:1943
smach_viewer.xdot.xdot.BezierShape.points
points
Definition: xdot.py:338
smach_viewer.xdot.xdot.Parser.__init__
def __init__(self, lexer)
Definition: xdot.py:953
smach_viewer.xdot.xdot.Node.__init__
def __init__(self, id, x, y, w, h, shapes, url)
Definition: xdot.py:418
smach_viewer.xdot.xdot.DotWidget
Definition: xdot.py:1540
smach_viewer.xdot.xdot.XDotParser.parse_node_pos
def parse_node_pos(self, pos)
Definition: xdot.py:1307
smach_viewer.xdot.xdot.XDotParser.xoffset
xoffset
Definition: xdot.py:1249
smach_viewer.xdot.xdot.DotWidget.on_zoom_fit
def on_zoom_fit(self, action)
Definition: xdot.py:1742
smach_viewer.xdot.xdot.DragAction.start
def start(self)
Definition: xdot.py:1450
smach_viewer.xdot.xdot.ParseError.line
line
Definition: xdot.py:819
smach_viewer.xdot.xdot.LineShape.pen
pen
Definition: xdot.py:318
smach_viewer.xdot.xdot.XDotParser.xscale
xscale
Definition: xdot.py:1251
smach_viewer.xdot.xdot.XDotParser.subgraph_shapes
subgraph_shapes
Definition: xdot.py:1227
smach_viewer.xdot.xdot.ZoomAction.drag
def drag(self, deltax, deltay)
Definition: xdot.py:1500
smach_viewer.xdot.xdot.main
def main()
Definition: xdot.py:2139
smach_viewer.xdot.xdot.DotParser.__init__
def __init__(self, lexer)
Definition: xdot.py:1089
smach_viewer.xdot.xdot.Lexer.line
line
Definition: xdot.py:905
smach_viewer.xdot.xdot.Token.line
line
Definition: xdot.py:865
smach_viewer.xdot.xdot.Url.url
url
Definition: xdot.py:382
smach_viewer.xdot.xdot.DotParser.parse
def parse(self)
Definition: xdot.py:1095
smach_viewer.xdot.xdot.ImageShape.y0
y0
Definition: xdot.py:242
smach_viewer.xdot.xdot.EllipseShape.h
h
Definition: xdot.py:267
smach_viewer.xdot.xdot.DotWidget.set_highlight
def set_highlight(self, items)
Definition: xdot.py:1680
smach_viewer.xdot.xdot.NoAnimation
Definition: xdot.py:1352
smach_viewer.xdot.xdot.DotWidget.ZOOM_TO_FIT_MARGIN
int ZOOM_TO_FIT_MARGIN
Definition: xdot.py:1734
smach_viewer.xdot.xdot.Graph.width
width
Definition: xdot.py:497
smach_viewer.xdot.xdot.DotWindow.last_open_dir
last_open_dir
Definition: xdot.py:2020
smach_viewer.xdot.xdot.Lexer
Definition: xdot.py:869
smach_viewer.xdot.xdot.NoAnimation.start
def start(self)
Definition: xdot.py:1354
smach_viewer.xdot.xdot.LineShape.points
points
Definition: xdot.py:319
smach_viewer.xdot.xdot.DotWidget.presstime
presstime
Definition: xdot.py:1580
smach_viewer.xdot.xdot.DotWidget.set_filter
def set_filter(self, filter)
Definition: xdot.py:1583
smach_viewer.xdot.xdot.Graph.height
height
Definition: xdot.py:498
smach_viewer.xdot.xdot.Lexer.tabsize
int tabsize
Definition: xdot.py:873
smach_viewer.xdot.xdot.ZoomAreaAction
Definition: xdot.py:1509
smach_viewer.xdot.xdot.Graph.get_size
def get_size(self)
Definition: xdot.py:504
smach_viewer.xdot.xdot.Jump.item
item
Definition: xdot.py:391
smach_viewer.xdot.xdot.Graph.edges
edges
Definition: xdot.py:501
smach_viewer.xdot.xdot.Shape.select_pen
def select_pen(self, highlight)
Definition: xdot.py:99
smach_viewer.xdot.xdot.BezierShape.filled
filled
Definition: xdot.py:339
smach_viewer.xdot.xdot.Lexer.buf
buf
Definition: xdot.py:903
smach_viewer.xdot.xdot.Element.get_url
def get_url(self, x, y)
Definition: xdot.py:409
smach_viewer.xdot.xdot.DotParser.handle_edge
def handle_edge(self, src_id, dst_id, attrs)
Definition: xdot.py:1208
smach_viewer.xdot.xdot.Jump.url
url
Definition: xdot.py:397
smach_viewer.xdot.xdot.Edge.is_inside_begin
def is_inside_begin(self, x, y)
Definition: xdot.py:468
smach_viewer.xdot.xdot.XDotAttrParser.read_text
def read_text(self)
Definition: xdot.py:603
smach_viewer.xdot.xdot.Node.x
x
Definition: xdot.py:422
smach_viewer.xdot.xdot.Pen.subscript
subscript
Definition: xdot.py:70
smach_viewer.xdot.xdot.Scanner.tokens_re
tokens_re
Definition: xdot.py:839
smach_viewer.xdot.xdot.ZoomAreaAction.abort
def abort(self)
Definition: xdot.py:1536
smach_viewer.xdot.xdot.ZoomToAnimation
Definition: xdot.py:1395
smach_viewer.xdot.xdot.Parser.match
def match(self, type)
Definition: xdot.py:957
smach_viewer.xdot.xdot.DotWindow.actiongroup
actiongroup
Definition: xdot.py:1991
smach_viewer.xdot.xdot.DotWindow.set_xdotcode
def set_xdotcode(self, xdotcode, filename=None)
Definition: xdot.py:2075
smach_viewer.xdot.xdot.Pen.fillcolor
fillcolor
Definition: xdot.py:62
smach_viewer.xdot.xdot.Pen.italic
italic
Definition: xdot.py:67
smach_viewer.xdot.xdot.Lexer.newline_re
newline_re
Definition: xdot.py:875
smach_viewer.xdot.xdot.Edge.is_inside_end
def is_inside_end(self, x, y)
Definition: xdot.py:471
smach_viewer.xdot.xdot.DotWidget.on_area_scroll_event
def on_area_scroll_event(self, area, event)
Definition: xdot.py:1894
smach_viewer.xdot.xdot.ZoomAreaAction.stop
def stop(self)
Definition: xdot.py:1529
smach_viewer.xdot.xdot.ZoomToAnimation.animate
def animate(self, t)
Definition: xdot.py:1414
smach_viewer.xdot.xdot.LinearAnimation.tick
def tick(self)
Definition: xdot.py:1369
smach_viewer.xdot.xdot.XDotAttrParser.handle_image
def handle_image(self, x0, y0, w, h, path)
Definition: xdot.py:791
smach_viewer.xdot.xdot.Token.type
type
Definition: xdot.py:863
smach_viewer.xdot.xdot.DotWindow.graph
graph
Definition: xdot.py:1971
smach_viewer.xdot.xdot.Node.x2
x2
Definition: xdot.py:427
smach_viewer.xdot.xdot.ParseError
Definition: xdot.py:814
smach_viewer.xdot.xdot.DotParser.graph_attrs
graph_attrs
Definition: xdot.py:1091
smach_viewer.xdot.xdot.DotWidget.get_element
def get_element(self, x, y)
Definition: xdot.py:1927
smach_viewer.xdot.xdot.XDotAttrParser.__nonzero__
def __nonzero__(self)
Definition: xdot.py:572
smach_viewer.xdot.xdot.Node.y
y
Definition: xdot.py:423
smach_viewer.xdot.xdot.XDotParser.node_by_name
node_by_name
Definition: xdot.py:1223
smach_viewer.xdot.xdot.ZoomAction
Definition: xdot.py:1498
smach_viewer.xdot.xdot.XDotParser.parse_edge_pos
def parse_edge_pos(self, pos)
Definition: xdot.py:1311
smach_viewer.xdot.xdot.CompoundShape
Definition: xdot.py:361
smach_viewer.xdot.xdot.Parser
Definition: xdot.py:951
smach_viewer.xdot.xdot.DotWidget.on_print
def on_print(self, action=None)
Definition: xdot.py:1802
smach_viewer.xdot.xdot.Edge.is_inside
def is_inside(self, x, y)
Definition: xdot.py:474
smach_viewer.xdot.xdot.CompoundShape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:367
smach_viewer.xdot.xdot.LinearAnimation.start
def start(self)
Definition: xdot.py:1365
smach_viewer.xdot.xdot.XDotParser.handle_node
def handle_node(self, id, attrs)
Definition: xdot.py:1265
smach_viewer.xdot.xdot.DotWidget.get_jump
def get_jump(self, x, y)
Definition: xdot.py:1935
smach_viewer.xdot.xdot.Pen.bold
bold
Definition: xdot.py:66
smach_viewer.xdot.xdot.Scanner.next
def next(self, buf, pos)
Definition: xdot.py:844
smach_viewer.xdot.xdot.Graph.get_url
def get_url(self, x, y)
Definition: xdot.py:530
smach_viewer.xdot.xdot.DotWidget.on_draw
def on_draw(self, widget, cr)
Definition: xdot.py:1655
smach_viewer.xdot.xdot.DotParser.parse_attrs
def parse_attrs(self)
Definition: xdot.py:1158
smach_viewer.xdot.xdot.TextShape.CENTER
CENTER
Definition: xdot.py:113
smach_viewer.xdot.xdot.XDotParser.edges
edges
Definition: xdot.py:1221
smach_viewer.xdot.xdot.EllipseShape.x0
x0
Definition: xdot.py:264
smach_viewer.xdot.xdot.Edge.url
url
Definition: xdot.py:464
smach_viewer.xdot.xdot.Edge
Definition: xdot.py:457
smach_viewer.xdot.xdot.XDotAttrParser.handle_font_characteristics
def handle_font_characteristics(self, flags)
Definition: xdot.py:771
smach_viewer.xdot.xdot.Parser.lookahead
lookahead
Definition: xdot.py:955
smach_viewer.xdot.xdot.Graph.get_jump
def get_jump(self, x, y)
Definition: xdot.py:537
smach_viewer.xdot.xdot.EllipseShape.pen
pen
Definition: xdot.py:263
smach_viewer.xdot.xdot.Lexer.consume
def consume(self, text)
Definition: xdot.py:932
smach_viewer.xdot.xdot.DotWindow.dotwidget
dotwidget
Definition: xdot.py:1980
smach_viewer.xdot.xdot.DotWidget.on_click
def on_click(self, element, event)
Definition: xdot.py:1864
smach_viewer.xdot.xdot.ZoomToAnimation.extra_zoom
extra_zoom
Definition: xdot.py:1401
smach_viewer.xdot.xdot.Edge.points
points
Definition: xdot.py:463
smach_viewer.xdot.xdot.Node.y2
y2
Definition: xdot.py:428
smach_viewer.xdot.xdot.PanAction.start
def start(self)
Definition: xdot.py:1484
smach_viewer.xdot.xdot.Graph
Definition: xdot.py:492
smach_viewer.xdot.xdot.Node.get_url
def get_url(self, x, y)
Definition: xdot.py:435
smach_viewer.xdot.xdot.Parser.skip
def skip(self, type)
Definition: xdot.py:965
smach_viewer.xdot.xdot.ZoomAction.stop
def stop(self)
Definition: xdot.py:1505
smach_viewer.xdot.xdot.Pen.linewidth
linewidth
Definition: xdot.py:63
smach_viewer.xdot.xdot.TextShape.search_text
def search_text(self, regexp)
Definition: xdot.py:232
smach_viewer.xdot.xdot.MoveToAnimation.__init__
def __init__(self, dot_widget, target_x, target_y)
Definition: xdot.py:1380
smach_viewer.xdot.xdot.Jump.x
x
Definition: xdot.py:392
smach_viewer.xdot.xdot.EllipseShape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:270
smach_viewer.xdot.xdot.DragAction.on_button_release
def on_button_release(self, event)
Definition: xdot.py:1442
smach_viewer.xdot.xdot.TextShape
Definition: xdot.py:111
smach_viewer.xdot.xdot.PolygonShape.filled
filled
Definition: xdot.py:294
smach_viewer.xdot.xdot.DragAction.prevmousex
prevmousex
Definition: xdot.py:1427
smach_viewer.xdot.xdot.DotWidget.set_xdotcode
def set_xdotcode(self, xdotcode)
Definition: xdot.py:1632
smach_viewer.xdot.xdot.Pen.strikethrough
strikethrough
Definition: xdot.py:71
smach_viewer.xdot.xdot.XDotParser.shapes
shapes
Definition: xdot.py:1222
smach_viewer.xdot.xdot.MoveToAnimation.target_y
target_y
Definition: xdot.py:1385
smach_viewer.xdot.xdot.DotWindow.update_title
def update_title(self, filename=None)
Definition: xdot.py:2080
smach_viewer.xdot.xdot.XDotAttrParser.read_polygon
def read_polygon(self)
Definition: xdot.py:612
smach_viewer.xdot.xdot.Jump.y
y
Definition: xdot.py:393
smach_viewer.xdot.xdot.Node.is_inside
def is_inside(self, x, y)
Definition: xdot.py:432
smach_viewer.xdot.xdot.DotWindow.uimanager
uimanager
Definition: xdot.py:1983
smach_viewer.xdot.xdot.ImageShape.__init__
def __init__(self, pen, x0, y0, w, h, path)
Definition: xdot.py:238
smach_viewer.xdot.xdot.DotWindow.set_dotcode
def set_dotcode(self, dotcode, filename=None)
Definition: xdot.py:2070
smach_viewer.xdot.xdot.TextShape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:124
smach_viewer.xdot.xdot.DotWidget.ZOOM_INCREMENT
float ZOOM_INCREMENT
Definition: xdot.py:1733
smach_viewer.xdot.xdot.DragAction.on_button_press
def on_button_press(self, event)
Definition: xdot.py:1426
smach_viewer.xdot.xdot.DotWidget.on_zoom_100
def on_zoom_100(self, action)
Definition: xdot.py:1745
smach_viewer.xdot.xdot.DotWindow.find_text
def find_text(self, entry_text)
Definition: xdot.py:2036
smach_viewer.xdot.xdot.Lexer.scanner
scanner
Definition: xdot.py:872
smach_viewer.xdot.xdot.DragAction.abort
def abort(self)
Definition: xdot.py:1459
smach_viewer.xdot.xdot.XDotAttrParser.__init__
def __init__(self, parser, buf)
Definition: xdot.py:564
smach_viewer.xdot.xdot.Scanner.tokens
list tokens
Definition: xdot.py:830
smach_viewer.xdot.xdot.DotWindow.on_open
def on_open(self, action)
Definition: xdot.py:2094
smach_viewer.xdot.xdot.EllipseShape
Definition: xdot.py:259
smach_viewer.xdot.xdot.ZoomToAnimation.target_zoom
target_zoom
Definition: xdot.py:1400
smach_viewer.xdot.xdot.PolygonShape.points
points
Definition: xdot.py:293
smach_viewer.xdot.xdot.PanAction
Definition: xdot.py:1482
smach_viewer.xdot.xdot.Lexer.next
def next(self)
Definition: xdot.py:909
smach_viewer.xdot.xdot.Token.__init__
def __init__(self, type, text, line, col)
Definition: xdot.py:862
smach_viewer.xdot.xdot.NullAction
Definition: xdot.py:1463
smach_viewer.xdot.xdot.Element
Definition: xdot.py:400
smach_viewer.xdot.xdot.DotLexer.filter
def filter(self, type, text)
Definition: xdot.py:1062
smach_viewer.xdot.xdot.DotParser
Definition: xdot.py:1087
smach_viewer.xdot.xdot.DotWidget.print_settings
print_settings
Definition: xdot.py:1801
smach_viewer.xdot.xdot.DragAction.draw
def draw(self, cr)
Definition: xdot.py:1447
smach_viewer.xdot.xdot.DotWindow.textentry_activate
def textentry_activate(self, widget, entry)
Definition: xdot.py:2055
smach_viewer.xdot.xdot.DotWidget.on_zoom_out
def on_zoom_out(self, action)
Definition: xdot.py:1739
smach_viewer.xdot.xdot.XDotAttrParser.lookup_color
def lookup_color(self, c)
Definition: xdot.py:646
smach_viewer.xdot.xdot.EllipseShape.filled
filled
Definition: xdot.py:268
smach_viewer.xdot.xdot.DotWidget.animate_to
def animate_to(self, x, y)
Definition: xdot.py:1913
smach_viewer.xdot.xdot.TextShape.j
j
Definition: xdot.py:120
smach_viewer.xdot.xdot.DotWidget.zoom_ratio
zoom_ratio
Definition: xdot.py:1576
smach_viewer.xdot.xdot.DotWidget.is_click
def is_click(self, event, click_fuzz=4, click_timeout=1.0)
Definition: xdot.py:1852
smach_viewer.xdot.xdot.Url.__init__
def __init__(self, item, url, highlight=None)
Definition: xdot.py:380
smach_viewer.xdot.xdot.OptionParser.format_epilog
def format_epilog(self, formatter)
Definition: xdot.py:2133
smach_viewer.xdot.xdot.DotLexer
Definition: xdot.py:1058
smach_viewer.xdot.xdot.XDotAttrParser.handle_font
def handle_font(self, size, name)
Definition: xdot.py:767
smach_viewer.xdot.xdot.Animation.dot_widget
dot_widget
Definition: xdot.py:1336
smach_viewer.xdot.xdot.DotWidget.on_area_size_allocate
def on_area_size_allocate(self, area, allocation)
Definition: xdot.py:1909
smach_viewer.xdot.xdot.CompoundShape.search_text
def search_text(self, regexp)
Definition: xdot.py:371
smach_viewer.xdot.xdot.TextShape.layout
layout
Definition: xdot.py:179
smach_viewer.xdot.xdot.XDotAttrParser.read_code
def read_code(self)
Definition: xdot.py:580
smach_viewer.xdot.xdot.XDotAttrParser.handle_color
def handle_color(self, color, filled=False)
Definition: xdot.py:750
smach_viewer.xdot.xdot.Edge.RADIUS
int RADIUS
Definition: xdot.py:466
smach_viewer.xdot.xdot.DotWidget.pressy
pressy
Definition: xdot.py:1849
smach_viewer.xdot.xdot.ImageShape.h
h
Definition: xdot.py:244
smach_viewer.xdot.xdot.DragAction.prevmousey
prevmousey
Definition: xdot.py:1428
smach_viewer.xdot.xdot.CompoundShape.shapes
shapes
Definition: xdot.py:365
smach_viewer.xdot.xdot.XDotAttrParser.read_color
def read_color(self)
Definition: xdot.py:620
smach_viewer.xdot.xdot.DotWidget.reload
def reload(self)
Definition: xdot.py:1638
smach_viewer.xdot.xdot.Graph.shapes
shapes
Definition: xdot.py:499
smach_viewer.xdot.xdot.XDotAttrParser.pos
pos
Definition: xdot.py:567
smach_viewer.xdot.xdot.Edge.__repr__
def __repr__(self)
Definition: xdot.py:488
smach_viewer.xdot.xdot.DragAction.startmousey
startmousey
Definition: xdot.py:1428
smach_viewer.xdot.xdot.LineShape.__init__
def __init__(self, pen, points)
Definition: xdot.py:316
smach_viewer.xdot.xdot.DotScanner
Definition: xdot.py:1004
smach_viewer.xdot.xdot.ZoomAreaAction.draw
def draw(self, cr)
Definition: xdot.py:1514
smach_viewer.xdot.xdot.EllipseShape.w
w
Definition: xdot.py:266
smach_viewer.xdot.xdot.DotWindow.textentry
textentry
Definition: xdot.py:2026
smach_viewer.xdot.xdot.XDotParser
Definition: xdot.py:1212
smach_viewer.xdot.xdot.XDotParser.handle_graph
def handle_graph(self, attrs)
Definition: xdot.py:1229
smach_viewer.xdot.xdot.Edge.src
src
Definition: xdot.py:461
smach_viewer.xdot.xdot.BezierShape.__init__
def __init__(self, pen, points, filled=False)
Definition: xdot.py:335
smach_viewer.xdot.xdot.Jump
Definition: xdot.py:388
smach_viewer.xdot.xdot.TextShape.pen
pen
Definition: xdot.py:117
smach_viewer.xdot.xdot.Node.get_jump
def get_jump(self, x, y)
Definition: xdot.py:442
smach_viewer.xdot.xdot_qt.stop
def stop(self)
Definition: xdot_qt.py:1305
smach_viewer.xdot.xdot.ImageShape.x0
x0
Definition: xdot.py:241
smach_viewer.xdot.xdot.DotWidget.get_url
def get_url(self, x, y)
Definition: xdot.py:1931
smach_viewer.xdot.xdot.XDotAttrParser.parser
parser
Definition: xdot.py:565
smach_viewer.xdot.xdot.DotParser.node_attrs
node_attrs
Definition: xdot.py:1092
smach_viewer.xdot.xdot.DragAction.on_motion_notify
def on_motion_notify(self, event)
Definition: xdot.py:1431
smach_viewer.xdot.xdot.PolygonShape.__init__
def __init__(self, pen, points, filled=False)
Definition: xdot.py:290
smach_viewer.xdot.xdot.ImageShape.w
w
Definition: xdot.py:243
smach_viewer.xdot.xdot.DotWidget.highlight
highlight
Definition: xdot.py:1581
smach_viewer.xdot.xdot.NullAction.on_motion_notify
def on_motion_notify(self, event)
Definition: xdot.py:1465
smach_viewer.xdot.xdot.DragAction.drag
def drag(self, deltax, deltay)
Definition: xdot.py:1453
smach_viewer.xdot.xdot.XDotParser.yscale
yscale
Definition: xdot.py:1252
smach_viewer.xdot.xdot.DotWidget.window2graph
def window2graph(self, x, y)
Definition: xdot.py:1917
smach_viewer.xdot.xdot.ParseError.msg
msg
Definition: xdot.py:817
smach_viewer.xdot.xdot.XDotAttrParser.buf
buf
Definition: xdot.py:566
smach_viewer.xdot.xdot.XDotParser.parse
def parse(self)
Definition: xdot.py:1303
smach_viewer.xdot.xdot.DotWidget.y
y
Definition: xdot.py:1575
smach_viewer.xdot.xdot.Scanner.literals
dictionary literals
Definition: xdot.py:832
smach_viewer.xdot.xdot.DotParser.handle_node
def handle_node(self, id, attrs)
Definition: xdot.py:1205
smach_viewer.xdot.xdot.Animation.tick
def tick(self)
Definition: xdot.py:1348
smach_viewer.xdot.xdot.XDotAttrParser.handle_linestyle
def handle_linestyle(self, style)
Definition: xdot.py:759
smach_viewer.xdot.xdot.PolygonShape
Definition: xdot.py:288
smach_viewer.xdot.xdot.DotWindow.textentry_changed
def textentry_changed(self, widget, entry)
Definition: xdot.py:2045
smach_viewer.xdot.xdot.XDotParser.__init__
def __init__(self, xdotcode)
Definition: xdot.py:1216
smach_viewer.xdot.xdot.DotWindow.on_reload
def on_reload(self, action)
Definition: xdot.py:2119
smach_viewer.xdot.xdot.ParseError.col
col
Definition: xdot.py:820
smach_viewer.xdot.xdot.TextShape.x
x
Definition: xdot.py:118
smach_viewer.xdot.xdot.Node.url
url
Definition: xdot.py:430
smach_viewer.xdot.xdot.DotParser.edge_attrs
edge_attrs
Definition: xdot.py:1093
smach_viewer.xdot.xdot.ParseError.filename
filename
Definition: xdot.py:818
smach_viewer.xdot.xdot.Pen
Definition: xdot.py:56
smach_viewer.xdot.xdot.Element.is_inside
def is_inside(self, x, y)
Definition: xdot.py:406
smach_viewer.xdot.xdot.TextShape.RIGHT
RIGHT
Definition: xdot.py:113
smach_viewer.xdot.xdot.XDotAttrParser.read_point
def read_point(self)
Definition: xdot.py:598
smach_viewer.xdot.xdot.XDotAttrParser.handle_polygon
def handle_polygon(self, points, filled=False)
Definition: xdot.py:803
smach_viewer.xdot.xdot.Url.item
item
Definition: xdot.py:381
smach_viewer.xdot.xdot.DotParser.parse_attr
def parse_attr(self)
Definition: xdot.py:1171
smach_viewer.xdot.xdot.Pen.fontsize
fontsize
Definition: xdot.py:64
smach_viewer.xdot.xdot.Scanner
Definition: xdot.py:826
smach_viewer.xdot.xdot.DotWidget.zoom_to_fit
def zoom_to_fit(self)
Definition: xdot.py:1720
smach_viewer.xdot.xdot.DotWidget.on_area_motion_notify
def on_area_motion_notify(self, area, event)
Definition: xdot.py:1905
smach_viewer.xdot.xdot.Animation
Definition: xdot.py:1331
smach_viewer.xdot.xdot.Edge.__init__
def __init__(self, src, dst, points, shapes, url)
Definition: xdot.py:459
smach_viewer.xdot.xdot.DotWidget.zoom_to_fit_on_resize
zoom_to_fit_on_resize
Definition: xdot.py:1577
smach_viewer.xdot.xdot.XDotParser.transform
def transform(self, x, y)
Definition: xdot.py:1324
smach_viewer.xdot.xdot.Lexer.col
col
Definition: xdot.py:906
smach_viewer.xdot.xdot.Shape
Definition: xdot.py:89
smach_viewer.xdot.xdot.ZoomToAnimation.__init__
def __init__(self, dot_widget, target_x, target_y)
Definition: xdot.py:1397
smach_viewer.xdot.xdot.DotParser.handle_graph
def handle_graph(self, attrs)
Definition: xdot.py:1202
smach_viewer.xdot.xdot.Animation.__init__
def __init__(self, dot_widget)
Definition: xdot.py:1335
smach_viewer.xdot.xdot.DotWidget.POS_INCREMENT
int POS_INCREMENT
Definition: xdot.py:1748
smach_viewer.xdot.xdot.LinearAnimation.duration
float duration
Definition: xdot.py:1363
smach_viewer.xdot.xdot.XDotParser.width
width
Definition: xdot.py:1225
smach_viewer.xdot.xdot.MoveToAnimation.source_y
source_y
Definition: xdot.py:1383
smach_viewer.xdot.xdot.LinearAnimation.animate
def animate(self, t)
Definition: xdot.py:1374
smach_viewer.xdot.xdot.XDotAttrParser.skip_space
def skip_space(self)
Definition: xdot.py:588
smach_viewer.xdot.xdot.DotWidget.drag_action
drag_action
Definition: xdot.py:1579
smach_viewer.xdot.xdot.Graph.nodes
nodes
Definition: xdot.py:500
smach_viewer.xdot.xdot.ImageShape.pen
pen
Definition: xdot.py:240
smach_viewer.xdot.xdot.XDotAttrParser.read_int
def read_int(self)
Definition: xdot.py:592
smach_viewer.xdot.xdot.DotWidget.openfilename
openfilename
Definition: xdot.py:1554
smach_viewer.xdot.xdot.DotWindow.error_dialog
def error_dialog(self, message)
Definition: xdot.py:2122
smach_viewer.xdot.xdot.ParseError.__init__
def __init__(self, msg=None, filename=None, line=None, col=None)
Definition: xdot.py:816
smach_viewer.xdot.xdot.DotWidget.get_drag_action
def get_drag_action(self, event)
Definition: xdot.py:1829
smach_viewer.xdot.xdot.Node.y1
y1
Definition: xdot.py:426
smach_viewer.xdot.xdot.DragAction.stopmousex
stopmousex
Definition: xdot.py:1443
smach_viewer.xdot.xdot.square_distance
def square_distance(x1, y1, x2, y2)
Definition: xdot.py:451
smach_viewer.xdot.xdot.DotWidget.on_zoom_in
def on_zoom_in(self, action)
Definition: xdot.py:1736
smach_viewer.xdot.xdot.Node
Definition: xdot.py:416
smach_viewer.xdot.xdot.Scanner.ignorecase
bool ignorecase
Definition: xdot.py:833
smach_viewer.xdot.xdot.Parser.lexer
lexer
Definition: xdot.py:954
smach_viewer.xdot.xdot.DotWidget.update
def update(self)
Definition: xdot.py:1647
smach_viewer.xdot.xdot.Url
Definition: xdot.py:378
smach_viewer.xdot.xdot.LineShape
Definition: xdot.py:314
smach_viewer.xdot.xdot.Parser.consume
def consume(self)
Definition: xdot.py:975
smach_viewer.xdot.xdot.XDotAttrParser.handle_text
def handle_text(self, x, y, j, w, t)
Definition: xdot.py:782
smach_viewer.xdot.xdot.TextShape.w
w
Definition: xdot.py:121
smach_viewer.xdot.xdot.DotWindow.open_file
def open_file(self, filename)
Definition: xdot.py:2086
smach_viewer.xdot.xdot.DotParser.parse_graph
def parse_graph(self)
Definition: xdot.py:1099
smach_viewer.xdot.xdot.XDotAttrParser.transform
def transform(self, x, y)
Definition: xdot.py:747
smach_viewer.xdot.xdot.BezierShape.pen
pen
Definition: xdot.py:337
smach_viewer.xdot.xdot.DotWindow.set_filter
def set_filter(self, filter)
Definition: xdot.py:2067
smach_viewer.xdot.xdot.DotWindow.ui
string ui
Definition: xdot.py:1949
smach_viewer.xdot.xdot.DragAction.startmousex
startmousex
Definition: xdot.py:1427
smach_viewer.xdot.xdot.DotWidget.zoom_image
def zoom_image(self, zoom_ratio, center=False, pos=None)
Definition: xdot.py:1685
smach_viewer.xdot.xdot.OptionParser
Definition: xdot.py:2131
smach_viewer.xdot.xdot.EllipseShape.y0
y0
Definition: xdot.py:265
smach_viewer.xdot.xdot.DotWidget.set_current_pos
def set_current_pos(self, x, y)
Definition: xdot.py:1675
smach_viewer.xdot.xdot.Lexer.__init__
def __init__(self, buf=None, pos=0, filename=None, fp=None)
Definition: xdot.py:877
smach_viewer.xdot.xdot.DotWidget.begin_print
def begin_print(self, operation, context)
Definition: xdot.py:1815
smach_viewer.xdot.xdot.ParseError.__str__
def __str__(self)
Definition: xdot.py:822
smach_viewer.xdot.xdot.LineShape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:321
smach_viewer.xdot.xdot.Shape.highlight_pen
highlight_pen
Definition: xdot.py:102
smach_viewer.xdot.xdot.PanAction.drag
def drag(self, deltax, deltay)
Definition: xdot.py:1487
smach_viewer.xdot.xdot.DotParser.parse_stmt
def parse_stmt(self)
Definition: xdot.py:1125
smach_viewer.xdot.xdot.XDotAttrParser.handle_ellipse
def handle_ellipse(self, x0, y0, w, h, filled=False)
Definition: xdot.py:785
smach_viewer.xdot.xdot.DragAction.stop
def stop(self)
Definition: xdot.py:1456
smach_viewer.xdot.xdot.Pen.fontname
fontname
Definition: xdot.py:65
smach_viewer.xdot.xdot.XDotParser.height
height
Definition: xdot.py:1226
smach_viewer.xdot.xdot.ZoomAreaAction.drag
def drag(self, deltax, deltay)
Definition: xdot.py:1511
smach_viewer.xdot.xdot.Jump.__init__
def __init__(self, item, x, y, highlight=None, url=None)
Definition: xdot.py:390
smach_viewer.xdot.xdot.Scanner.symbols
dictionary symbols
Definition: xdot.py:831
smach_viewer.xdot.xdot.DotWidget.graph
graph
Definition: xdot.py:1553
smach_viewer.xdot.xdot.Graph.subgraph_shapes
subgraph_shapes
Definition: xdot.py:502
smach_viewer.xdot.xdot.ImageShape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:247
smach_viewer.xdot.xdot.MoveToAnimation.animate
def animate(self, t)
Definition: xdot.py:1387
smach_viewer.xdot.xdot.Graph.get_element
def get_element(self, x, y)
Definition: xdot.py:522
smach_viewer.xdot.xdot.Edge.dst
dst
Definition: xdot.py:462
smach_viewer.xdot.xdot.XDotAttrParser.handle_linewidth
def handle_linewidth(self, linewidth)
Definition: xdot.py:756
smach_viewer.xdot.xdot.TextShape.t
t
Definition: xdot.py:122
smach_viewer.xdot.xdot.DotWidget.run_filter
def run_filter(self, dotcode)
Definition: xdot.py:1586
smach_viewer.xdot.xdot.EllipseShape.__init__
def __init__(self, pen, x0, y0, w, h, filled=False)
Definition: xdot.py:261
smach_viewer.xdot.xdot.Url.highlight
highlight
Definition: xdot.py:385
smach_viewer.xdot.xdot.Pen.dash
dash
Definition: xdot.py:74
smach_viewer.xdot.xdot.Pen.superscript
superscript
Definition: xdot.py:69
smach_viewer.xdot.xdot.DotWidget.on_area_button_release
def on_area_button_release(self, area, event)
Definition: xdot.py:1870
smach_viewer.xdot.xdot.XDotParser.top_graph
top_graph
Definition: xdot.py:1224
smach_viewer.xdot.xdot.MoveToAnimation.source_x
source_x
Definition: xdot.py:1382
smach_viewer.xdot.xdot.Pen.underline
underline
Definition: xdot.py:68
smach_viewer.xdot.xdot.DotWidget.on_area_button_press
def on_area_button_press(self, area, event)
Definition: xdot.py:1841
smach_viewer.xdot.xdot.BezierShape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:341
smach_viewer.xdot.xdot.XDotParser.yoffset
yoffset
Definition: xdot.py:1250
smach_viewer.xdot.xdot.XDotParser.nodes
nodes
Definition: xdot.py:1220
smach_viewer.xdot.xdot.DotParser.parse_node_id
def parse_node_id(self)
Definition: xdot.py:1180
smach_viewer.xdot.xdot.DragAction
Definition: xdot.py:1421
smach_viewer.xdot.xdot.XDotAttrParser.parse
def parse(self)
Definition: xdot.py:675
smach_viewer.xdot.xdot.Graph.draw
def draw(self, cr, highlight_items=None)
Definition: xdot.py:507
smach_viewer.xdot.xdot.BezierShape
Definition: xdot.py:333
smach_viewer.xdot.xdot.XDotAttrParser.handle_line
def handle_line(self, points)
Definition: xdot.py:794
smach_viewer.xdot.xdot.TextShape.y
y
Definition: xdot.py:119
smach_viewer.xdot.xdot.TextShape.__init__
def __init__(self, pen, x, y, j, w, t)
Definition: xdot.py:115
smach_viewer.xdot.xdot.DragAction.stopmousey
stopmousey
Definition: xdot.py:1444
smach_viewer.xdot.xdot.Token.text
text
Definition: xdot.py:864
smach_viewer.xdot.xdot.DotWidget.zoom_to_area
def zoom_to_area(self, x1, y1, x2, y2)
Definition: xdot.py:1704
smach_viewer.xdot.xdot.DotWindow
Definition: xdot.py:1947
smach_viewer.xdot.xdot.Animation.stop
def stop(self)
Definition: xdot.py:1342
smach_viewer.xdot.xdot.Node.__repr__
def __repr__(self)
Definition: xdot.py:447
smach_viewer.xdot.xdot.Pen.color
color
Definition: xdot.py:61
smach_viewer.xdot.xdot.ImageShape
Definition: xdot.py:236
smach_viewer.xdot.xdot.CompoundShape.__init__
def __init__(self, shapes)
Definition: xdot.py:363
smach_viewer.xdot.xdot.ZoomToAnimation.source_zoom
source_zoom
Definition: xdot.py:1399
smach_viewer.xdot.xdot.MoveToAnimation.target_x
target_x
Definition: xdot.py:1384
smach_viewer.xdot.xdot.Edge.get_jump
def get_jump(self, x, y)
Definition: xdot.py:481
smach_viewer.xdot.xdot.XDotParser.handle_edge
def handle_edge(self, src_id, dst_id, attrs)
Definition: xdot.py:1285
smach_viewer.xdot.xdot.MoveToAnimation
Definition: xdot.py:1378
smach_viewer.xdot.xdot.Scanner.__init__
def __init__(self)
Definition: xdot.py:835
smach_viewer.xdot.xdot.TextShape.LEFT
LEFT
Definition: xdot.py:113
smach_viewer.xdot.xdot.Lexer.filename
filename
Definition: xdot.py:907
smach_viewer.xdot.xdot.Jump.highlight
highlight
Definition: xdot.py:396
smach_viewer.xdot.xdot.XDotAttrParser.pen
pen
Definition: xdot.py:569
smach_viewer.xdot.xdot.Pen.overline
overline
Definition: xdot.py:72
smach_viewer.xdot.xdot.DotWidget.x
x
Definition: xdot.py:1676
smach_viewer.xdot.xdot.DotWidget.get_current_pos
def get_current_pos(self)
Definition: xdot.py:1672
smach_viewer.xdot.xdot.DotWidget.__init__
def __init__(self)
Definition: xdot.py:1550
smach_viewer.xdot.xdot.NoAnimation.stop
def stop(self)
Definition: xdot.py:1357
smach_viewer.xdot.xdot.Pen.__init__
def __init__(self)
Definition: xdot.py:59
smach_viewer.xdot.xdot.XDotAttrParser.shapes
shapes
Definition: xdot.py:570
smach_viewer.xdot.xdot.DotWidget.last_mtime
last_mtime
Definition: xdot.py:1571
smach_viewer.xdot.xdot.DragAction.__init__
def __init__(self, dot_widget)
Definition: xdot.py:1423
smach_viewer.xdot.xdot.DotWidget.draw_page
def draw_page(self, operation, context, page_nr)
Definition: xdot.py:1819
smach_viewer.xdot.xdot.Lexer.pos
pos
Definition: xdot.py:904
smach_viewer.xdot.xdot.PolygonShape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:296
smach_viewer.xdot.xdot.DotWidget.on_key_press_event
def on_key_press_event(self, widget, event)
Definition: xdot.py:1750
smach_viewer.xdot.xdot.DotWidget.pressx
pressx
Definition: xdot.py:1848
smach_viewer.xdot.xdot.ImageShape.path
path
Definition: xdot.py:245
smach_viewer.xdot.xdot.DotParser.parse_subgraph
def parse_subgraph(self)
Definition: xdot.py:1108
smach_viewer.xdot.xdot.Graph.__init__
def __init__(self, width=1, height=1, shapes=(), nodes=(), edges=(), subgraph_shapes={})
Definition: xdot.py:494
smach_viewer.xdot.xdot.Pen.highlighted
def highlighted(self)
Definition: xdot.py:82
smach_viewer.xdot.xdot.Shape.draw
def draw(self, cr, highlight=False)
Definition: xdot.py:95
smach_viewer.xdot.xdot.XDotAttrParser
Definition: xdot.py:558
smach_viewer.xdot.xdot.LinearAnimation.started
started
Definition: xdot.py:1366
smach_viewer.xdot.xdot.FindMenuToolAction
Definition: xdot.py:1940
smach_viewer.xdot.xdot.Element.__init__
def __init__(self, shapes)
Definition: xdot.py:403
smach_viewer.xdot.xdot.PanAction.stop
def stop(self)
Definition: xdot.py:1492
smach_viewer.xdot.xdot.DotWidget.set_dotcode
def set_dotcode(self, dotcode, filename=None)
Definition: xdot.py:1612
smach_viewer.xdot.xdot.XDotAttrParser.unescape
def unescape(self, buf)
Definition: xdot.py:575
smach_viewer.xdot.xdot.Animation.start
def start(self)
Definition: xdot.py:1339
smach_viewer.xdot.xdot.DotWindow.__init__
def __init__(self, widget=None)
Definition: xdot.py:1968
smach_viewer.xdot.xdot.DotWindow.base_title
string base_title
Definition: xdot.py:1966
smach_viewer.xdot.xdot.Shape.search_text
def search_text(self, regexp)
Definition: xdot.py:107


smach_viewer
Author(s): Jonathan Bohren
autogenerated on Thu Feb 20 2025 03:09:09