def set_drawing_style(self, sloppiness=0.0): """Set the drawing style for the diagram. 0.0 is straight, 2.0 is very sloppy. If the sloppiness is set to be anything greater than 0.0, the FreeHandPainter instances will be used for both the item painter and the box painter. Otherwise, by default, the ItemPainter is used for the item and BoundingBoxPainter for the box.""" view = self.view if sloppiness: item_painter = FreeHandPainter(ItemPainter(), sloppiness=sloppiness) box_painter = FreeHandPainter(BoundingBoxPainter(), sloppiness=sloppiness) else: item_painter = ItemPainter() box_painter = BoundingBoxPainter() view.painter = ( PainterChain() .append(item_painter) .append(HandlePainter()) .append(FocusedItemPainter()) .append(ToolPainter()) ) view.bounding_box_painter = box_painter view.queue_draw_refresh()
def update_painters(self, view): sloppiness = self.properties.get("diagram.sloppiness", 0) if sloppiness: view.painter = FreeHandPainter(ItemPainter(), sloppiness) view.bounding_box_painter = FreeHandPainter( BoundingBoxPainter(), sloppiness ) else: view.painter = ItemPainter()
def update_painters(self, view): self.logger.info('Updating painters') self.logger.debug('View is %s' % view) sloppiness = self.properties('diagram.sloppiness', 0) self.logger.debug('Sloppiness is %s' % sloppiness) if sloppiness: view.painter = FreeHandPainter(ItemPainter(), sloppiness) view.bounding_box_painter = FreeHandPainter(BoundingBoxPainter(), sloppiness) else: view.painter = ItemPainter()
def update_painters(self, view): logger.info("Updating painters") logger.debug("View is %s" % view) sloppiness = self.properties("diagram.sloppiness", 0) logger.debug("Sloppiness is %s" % sloppiness) if sloppiness: view.painter = FreeHandPainter(ItemPainter(), sloppiness) view.bounding_box_painter = FreeHandPainter( BoundingBoxPainter(), sloppiness) else: view.painter = ItemPainter()
def apply_painters(view): painter = FreeHandPainter(ItemPainter(view.selection)) view.painter = (PainterChain().append(painter).append( HandlePainter(view)).append(LineSegmentPainter(view.selection)).append( GuidePainter(view)).append( RubberbandPainter(rubberband_state(view)))) view.bounding_box_painter = painter
def __init__(self, model: Optional[Model] = None, selection: Selection = None): """Create a new view. Args: model (Model): optional model to be set on construction time. selection (Selection): optional selection object, in case the default selection object (hover/select/focus) is not enough. """ Gtk.DrawingArea.__init__(self) self._dirty_items: Set[Item] = set() self._back_buffer: Optional[cairo.Surface] = None self._back_buffer_needs_resizing = True self._controllers: Set[Gtk.EventController] = set() self.set_can_focus(True) if Gtk.get_major_version() == 3: self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK | Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.STRUCTURE_MASK) self.set_app_paintable(True) else: self.set_draw_func(GtkView.do_draw) self.connect_after("resize", GtkView.on_resize) def alignment_updated(matrix: Matrix) -> None: if self._model: self._matrix *= matrix # type: ignore[misc] self._scrolling = Scrolling(alignment_updated) self._selection = selection or Selection() self._matrix = Matrix() self._painter: Painter = DefaultPainter(self) self._bounding_box_painter: ItemPainterType = ItemPainter( self._selection) self._matrix_changed = False self._qtree: Quadtree[Item, Tuple[float, float, float, float]] = Quadtree() self._model: Optional[Model] = None if model: self.model = model self._selection.add_handler(self.on_selection_update) self._matrix.add_handler(self.on_matrix_update)
def on_write_demo_svg_clicked(button): assert view.model painter = ItemPainter() # Update bounding boxes with a temporaly CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) bounding_box = BoundingBoxPainter(painter).bounding_box( canvas.get_all_items(), tmpcr) tmpcr.show_page() tmpsurface.flush() surface = cairo.SVGSurface("demo.svg", int(bounding_box.width), int(bounding_box.height)) cr = cairo.Context(surface) cr.translate(-bounding_box.x, -bounding_box.y) painter.paint(items=list(view.model.get_all_items()), cairo=cr) cr.show_page() surface.flush() surface.finish()
def __init__(self, canvas=None): self._matrix = cairo.Matrix() self._painter = DefaultPainter(self) self._bounding_box_painter = BoundingBoxPainter(ItemPainter(self), self) # Handling selections. # TODO: Move this to a context? self._selected_items = set() self._focused_item = None self._hovered_item = None self._dropzone_item = None self._qtree = Quadtree() self._bounds = Rectangle(0, 0, 0, 0) self._canvas = None if canvas: self._set_canvas(canvas)
def on_clicked(button): svgview = View(view.canvas) svgview.painter = ItemPainter() # Update bounding boxes with a temporaly CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) svgview.update_bounding_box(tmpcr) tmpcr.show_page() tmpsurface.flush() w, h = svgview.bounding_box.width, svgview.bounding_box.height surface = cairo.SVGSurface("demo.svg", w, h) cr = cairo.Context(surface) svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y) svgview.paint(cr) cr.show_page() surface.flush() surface.finish()
def create_window(canvas, title, zoom=1.0): view = GtkView() view.painter = ( PainterChain() .append(FreeHandPainter(ItemPainter())) .append(HandlePainter()) .append(FocusedItemPainter()) .append(ToolPainter()) ) view.bounding_box_painter = FreeHandPainter(BoundingBoxPainter(ItemPainter())) w = Gtk.Window() w.set_title(title) w.set_default_size(400, 120) h = Gtk.HBox() w.add(h) # VBox contains buttons that can be used to manipulate the canvas: v = Gtk.VBox() v.set_property("border-width", 3) v.set_property("spacing", 2) f = Gtk.Frame() f.set_property("border-width", 1) f.add(v) h.pack_start(f, False, True, 0) v.add(Gtk.Label.new("Item placement:")) b = Gtk.Button.new_with_label("Add box") def on_clicked(button, view): # view.window.set_cursor(Gdk.Cursor.new(Gdk.CursorType.CROSSHAIR)) view.tool.grab(PlacementTool(view, factory(view, MyBox), HandleTool(), 2)) b.connect("clicked", on_clicked, view) v.add(b) b = Gtk.Button.new_with_label("Add line") def on_clicked(button): view.tool.grab(PlacementTool(view, factory(view, MyLine), HandleTool(), 1)) b.connect("clicked", on_clicked) v.add(b) v.add(Gtk.Label.new("Zooming:")) b = Gtk.Button.new_with_label("Zoom in") def on_clicked(button): view.zoom(1.2) b.connect("clicked", on_clicked) v.add(b) b = Gtk.Button.new_with_label("Zoom out") def on_clicked(button): view.zoom(1 / 1.2) b.connect("clicked", on_clicked) v.add(b) v.add(Gtk.Label.new("Misc:")) b = Gtk.Button.new_with_label("Split line") def on_clicked(button): if isinstance(view.focused_item, Line): segment = Segment(view.focused_item, view) segment.split_segment(0) view.queue_draw_item(view.focused_item) b.connect("clicked", on_clicked) v.add(b) b = Gtk.Button.new_with_label("Delete focused") def on_clicked(button): if view.focused_item: canvas.remove(view.focused_item) b.connect("clicked", on_clicked) v.add(b) v.add(Gtk.Label.new("State:")) b = Gtk.ToggleButton.new_with_label("Record") def on_toggled(button): global undo_list if button.get_active(): print("start recording") del undo_list[:] state.subscribers.add(undo_handler) else: print("stop recording") state.subscribers.remove(undo_handler) b.connect("toggled", on_toggled) v.add(b) b = Gtk.Button.new_with_label("Play back") def on_clicked(self): global undo_list apply_me = list(undo_list) del undo_list[:] print("Actions on the undo stack:", len(apply_me)) apply_me.reverse() saveapply = state.saveapply for event in apply_me: print("Undo: invoking", event) saveapply(*event) print("New undo stack size:", len(undo_list)) # Visualize each event: # while Gtk.events_pending(): # Gtk.main_iteration() b.connect("clicked", on_clicked) v.add(b) v.add(Gtk.Label.new("Export:")) b = Gtk.Button.new_with_label("Write demo.png") def on_clicked(button): svgview = View(view.canvas) svgview.painter = ItemPainter() # Update bounding boxes with a temporary CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) svgview.update_bounding_box(tmpcr) tmpcr.show_page() tmpsurface.flush() w, h = svgview.bounding_box.width, svgview.bounding_box.height surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h)) cr = cairo.Context(surface) svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y) cr.save() svgview.paint(cr) cr.restore() cr.show_page() surface.write_to_png("demo.png") b.connect("clicked", on_clicked) v.add(b) b = Gtk.Button.new_with_label("Write demo.svg") def on_clicked(button): svgview = View(view.canvas) svgview.painter = ItemPainter() # Update bounding boxes with a temporaly CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) svgview.update_bounding_box(tmpcr) tmpcr.show_page() tmpsurface.flush() w, h = svgview.bounding_box.width, svgview.bounding_box.height surface = cairo.SVGSurface("demo.svg", w, h) cr = cairo.Context(surface) svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y) svgview.paint(cr) cr.show_page() surface.flush() surface.finish() b.connect("clicked", on_clicked) v.add(b) b = Gtk.Button.new_with_label("Dump QTree") def on_clicked(button, li): view._qtree.dump() b.connect("clicked", on_clicked, [0]) v.add(b) b = Gtk.Button.new_with_label("Pickle (save)") def on_clicked(button, li): f = open("demo.pickled", "wb") try: import pickle pickle.dump(view.canvas, f) finally: f.close() b.connect("clicked", on_clicked, [0]) v.add(b) b = Gtk.Button.new_with_label("Unpickle (load)") def on_clicked(button, li): f = open("demo.pickled", "rb") try: import pickle canvas = pickle.load(f) canvas.update_now() finally: f.close() create_window(canvas, "Unpickled diagram") b.connect("clicked", on_clicked, [0]) v.add(b) b = Gtk.Button.new_with_label("Unpickle (in place)") def on_clicked(button, li): f = open("demo.pickled", "rb") try: import pickle canvas = pickle.load(f) finally: f.close() # [i.request_update() for i in canvas.get_all_items()] canvas.update_now() view.canvas = canvas b.connect("clicked", on_clicked, [0]) v.add(b) b = Gtk.Button.new_with_label("Reattach (in place)") def on_clicked(button, li): view.canvas = None view.canvas = canvas b.connect("clicked", on_clicked, [0]) v.add(b) # Add the actual View: view.canvas = canvas view.zoom(zoom) view.set_size_request(150, 120) s = Gtk.ScrolledWindow.new() s.set_hexpand(True) s.add(view) h.add(s) w.show_all() w.connect("destroy", Gtk.main_quit) def handle_changed(view, item, what): print(what, "changed: ", item) view.connect("focus-changed", handle_changed, "focus") view.connect("hover-changed", handle_changed, "hover") view.connect("selection-changed", handle_changed, "selection")
message('skipping %s' % pname) continue if options.dir: odir = '%s/%s' % (options.dir, odir) outfilename = '%s/%s.%s' % (odir, dname, options.format) if not os.path.exists(odir): message('creating dir %s' % odir) os.makedirs(odir) message('rendering: %s -> %s...' % (pname, outfilename)) view = View(diagram.canvas) view.painter = ItemPainter() tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) view.update_bounding_box(tmpcr) tmpcr.show_page() tmpsurface.flush() w, h = view.bounding_box.width, view.bounding_box.height if options.format == 'pdf': surface = cairo.PDFSurface(outfilename, w, h) elif options.format == 'svg': surface = cairo.SVGSurface(outfilename, w, h) elif options.format == 'png': surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w + 1), int(h + 1))
def main(argv=sys.argv[1:]): def message(msg): """ Print message if user set verbose mode. """ if options.verbose: print(msg, file=sys.stderr) usage = "usage: %prog [options] file1 file2..." parser = optparse.OptionParser(usage=usage) parser.add_option("-v", "--verbose", dest="verbose", action="store_true", help="verbose output") parser.add_option( "-u", "--use-underscores", dest="underscores", action="store_true", help="use underscores instead of spaces for output filenames", ) parser.add_option("-d", "--dir", dest="dir", metavar="directory", help="output to directory") parser.add_option( "-f", "--format", dest="format", metavar="format", help="output file format, default pdf", default="pdf", choices=["pdf", "svg", "png"], ) parser.add_option( "-r", "--regex", dest="regex", metavar="regex", help="process diagrams which name matches given regular expresion;" " name includes package name; regular expressions are case insensitive", ) (options, args) = parser.parse_args(argv) if not args: parser.print_help() session = Session( services=["event_manager", "component_registry", "element_factory"]) factory = session.get_service("element_factory") name_re = None if options.regex: name_re = re.compile(options.regex, re.I) # we should have some gaphor files to be processed at this point for model in args: message(f"loading model {model}") storage.load(model, factory) message("ready for rendering") for diagram in factory.select(lambda e: e.isKindOf(UML.Diagram)): odir = pkg2dir(diagram.package) # just diagram name dname = diagram.name # full diagram name including package path pname = f"{odir}/{dname}" if options.underscores: odir = odir.replace(" ", "_") dname = dname.replace(" ", "_") if name_re and not name_re.search(pname): message(f"skipping {pname}") continue if options.dir: odir = f"{options.dir}/{odir}" outfilename = f"{odir}/{dname}.{options.format}" if not os.path.exists(odir): message(f"creating dir {odir}") os.makedirs(odir) message(f"rendering: {pname} -> {outfilename}...") view = View(diagram.canvas) view.painter = ItemPainter() tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) view.update_bounding_box(tmpcr) tmpcr.show_page() tmpsurface.flush() w, h = view.bounding_box.width, view.bounding_box.height if options.format == "pdf": surface = cairo.PDFSurface(outfilename, w, h) elif options.format == "svg": surface = cairo.SVGSurface(outfilename, w, h) elif options.format == "png": surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w + 1), int(h + 1)) else: assert False, f"unknown format {options.format}" cr = cairo.Context(surface) view.matrix.translate(-view.bounding_box.x, -view.bounding_box.y) paint(view, cr) cr.show_page() if options.format == "png": surface.write_to_png(outfilename) surface.flush() surface.finish()
def create_window(canvas, title, zoom=1.0): view = GtkView() view.painter = PainterChain(). \ append(FreeHandPainter(ItemPainter())). \ append(HandlePainter()). \ append(FocusedItemPainter()). \ append(ToolPainter()) view.bounding_box_painter = FreeHandPainter(BoundingBoxPainter()) w = gtk.Window() w.set_title(title) h = gtk.HBox() w.add(h) # VBox contains buttons that can be used to manipulate the canvas: v = gtk.VBox() v.set_property('border-width', 3) v.set_property('spacing', 2) f = gtk.Frame() f.set_property('border-width', 1) f.add(v) h.pack_start(f, expand=False) v.add(gtk.Label('Item placement:')) b = gtk.Button('Add box') def on_clicked(button, view): #view.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSSHAIR)) view.tool.grab( PlacementTool(view, factory(view, MyBox), HandleTool(), 2)) b.connect('clicked', on_clicked, view) v.add(b) b = gtk.Button('Add line') def on_clicked(button): view.tool.grab( PlacementTool(view, factory(view, MyLine), HandleTool(), 1)) b.connect('clicked', on_clicked) v.add(b) v.add(gtk.Label('Zooming:')) b = gtk.Button('Zoom in') def on_clicked(button): view.zoom(1.2) b.connect('clicked', on_clicked) v.add(b) b = gtk.Button('Zoom out') def on_clicked(button): view.zoom(1 / 1.2) b.connect('clicked', on_clicked) v.add(b) v.add(gtk.Label('Misc:')) b = gtk.Button('Split line') def on_clicked(button): if isinstance(view.focused_item, Line): segment = Segment(view.focused_item, view) segment.split_segment(0) view.queue_draw_item(view.focused_item) b.connect('clicked', on_clicked) v.add(b) b = gtk.Button('Delete focused') def on_clicked(button): if view.focused_item: canvas.remove(view.focused_item) #print 'items:', canvas.get_all_items() b.connect('clicked', on_clicked) v.add(b) v.add(gtk.Label('State:')) b = gtk.ToggleButton('Record') def on_toggled(button): global undo_list if button.get_active(): print 'start recording' del undo_list[:] state.subscribers.add(undo_handler) else: print 'stop recording' state.subscribers.remove(undo_handler) b.connect('toggled', on_toggled) v.add(b) b = gtk.Button('Play back') def on_clicked(self): global undo_list apply_me = list(undo_list) del undo_list[:] print 'Actions on the undo stack:', len(apply_me) apply_me.reverse() saveapply = state.saveapply for event in apply_me: print 'Undo: invoking', event saveapply(*event) print 'New undo stack size:', len(undo_list) # Visualize each event: #while gtk.events_pending(): # gtk.main_iteration() b.connect('clicked', on_clicked) v.add(b) v.add(gtk.Label('Export:')) b = gtk.Button('Write demo.png') def on_clicked(button): svgview = View(view.canvas) svgview.painter = ItemPainter() # Update bounding boxes with a temporaly CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) svgview.update_bounding_box(tmpcr) tmpcr.show_page() tmpsurface.flush() w, h = svgview.bounding_box.width, svgview.bounding_box.height surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h)) cr = cairo.Context(surface) svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y) cr.save() svgview.paint(cr) cr.restore() cr.show_page() surface.write_to_png('demo.png') b.connect('clicked', on_clicked) v.add(b) b = gtk.Button('Write demo.svg') def on_clicked(button): svgview = View(view.canvas) svgview.painter = ItemPainter() # Update bounding boxes with a temporaly CairoContext # (used for stuff like calculating font metrics) tmpsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0) tmpcr = cairo.Context(tmpsurface) svgview.update_bounding_box(tmpcr) tmpcr.show_page() tmpsurface.flush() w, h = svgview.bounding_box.width, svgview.bounding_box.height surface = cairo.SVGSurface('demo.svg', w, h) cr = cairo.Context(surface) svgview.matrix.translate(-svgview.bounding_box.x, -svgview.bounding_box.y) svgview.paint(cr) cr.show_page() surface.flush() surface.finish() b.connect('clicked', on_clicked) v.add(b) b = gtk.Button('Dump QTree') def on_clicked(button, li): view._qtree.dump() b.connect('clicked', on_clicked, [0]) v.add(b) b = gtk.Button('Pickle (save)') def on_clicked(button, li): f = open('demo.pickled', 'w') try: import cPickle as pickle pickle.dump(view.canvas, f) finally: f.close() b.connect('clicked', on_clicked, [0]) v.add(b) b = gtk.Button('Unpickle (load)') def on_clicked(button, li): f = open('demo.pickled', 'r') try: import cPickle as pickle canvas = pickle.load(f) canvas.update_now() finally: f.close() create_window(canvas, 'Unpickled diagram') b.connect('clicked', on_clicked, [0]) v.add(b) b = gtk.Button('Unpickle (in place)') def on_clicked(button, li): f = open('demo.pickled', 'r') try: import cPickle as pickle canvas = pickle.load(f) finally: f.close() #[i.request_update() for i in canvas.get_all_items()] canvas.update_now() view.canvas = canvas b.connect('clicked', on_clicked, [0]) v.add(b) b = gtk.Button('Reattach (in place)') def on_clicked(button, li): view.canvas = None view.canvas = canvas b.connect('clicked', on_clicked, [0]) v.add(b) # Add the actual View: view.canvas = canvas view.zoom(zoom) view.set_size_request(150, 120) s = gtk.ScrolledWindow() s.add(view) h.add(s) w.show_all() w.connect('destroy', gtk.main_quit) def handle_changed(view, item, what): print what, 'changed: ', item view.connect('focus-changed', handle_changed, 'focus') view.connect('hover-changed', handle_changed, 'hover') view.connect('selection-changed', handle_changed, 'selection')