diff options
Diffstat (limited to 'src/toys/toyframework.py')
-rw-r--r-- | src/toys/toyframework.py | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/src/toys/toyframework.py b/src/toys/toyframework.py new file mode 100644 index 0000000..c580c1e --- /dev/null +++ b/src/toys/toyframework.py @@ -0,0 +1,435 @@ +#!/usr/bin/python + +import gtk,math +import pangocairo,cairo +import gobject + +# def draw_text(cr, pos, txt, bottom = False): +# def draw_number(cr, pos, num): + +def draw_circ(cr, (x, y)): + cr.new_sub_path() + cr.arc(x, y, 3, 0, math.pi*2) + cr.stroke() +def draw_cross(cr, (x, y)): + cr.move_to(x-3, y-3) + cr.line_to(x+3, y+3) + cr.move_to(x-3, y+3) + cr.line_to(x+3, y-3) + cr.stroke() + +class Handle: + def __init__(self): + pass + def draw(self, cr, annotes): + pass + def hit(self, (x, y)): + return None + def move_to(self, hit, om, m): + pass + def scroll(self, pos, dir): + pass + +class PointHandle(Handle): + def __init__(self, x, y, name=""): + Handle.__init__(self) + self.pos = (x,y) + self.name = name + def draw(self, cr, annotes): + draw_circ(cr, self.pos) + if annotes: + draw_text(cr, self.pos, str(self.name)) + def hit(self, mouse): + if math.hypot(mouse[0] - self.pos[0], mouse[1] - self.pos[1]) < 5: + return 0 + return None + def move_to(self, hit, om, m): + self.pos = m + +class PointSetHandle(Handle): + def __init__(self, pts=None, name=""): + Handle.__init__(self) + self.pts = pts or [] + self.name = name + def draw(self, cr, annotes): + for p in self.pts: + draw_circ(cr, p) + if annotes: + draw_text(cr, p, str(self.name)) + def hit(self, mouse): + for i,p in enumerate(self.pts): + if math.hypot(mouse[0] - p[0], mouse[1] - p[1]) < 5: + return i + return None + def move_to(self, hit, om, m): + self.pts[hit[1]] = m + def append(self, x,y): + self.pts.append((x,y)) + +class Toy: + def __init__(self): + self.handles = [] + self.mouse_down = False + self.old_mouse = None + self.selected = None + self.notify = "notify" + self.origin = [0,0] + self.interactive_level = 0 + self.transform = None + + def draw(self, cr, (width, height), save): + bounds = self.should_draw_bounds() + self.transform = cr.get_matrix() + self.transform.invert() + if bounds == 1: + cr.set_source_rgba(0., 0., 0, 0.8) + cr.set_line_width (0.5) + for i in [1,3]: + cr.move_to(0, i*width/4) + cr.line_to(width, i*width/4) + cr.move_to(i*width/4, 0) + cr.line_to(i*width/4, height) + elif bounds == 2: + cr.set_source_rgba (0., 0., 0, 0.8) + cr.set_line_width (0.5) + cr.move_to(0, width/2) + cr.line_to(width, width/2) + cr.move_to(width/2, 0) + cr.line_to(width/2, height) + + cr.set_source_rgba (0., 0.5, 0, 1) + cr.set_line_width (1) + annotes = self.should_draw_numbers() + for i,h in enumerate(self.handles): + cr.save() + if self.selected and i == self.selected[0]: + cr.set_source_rgba (0.5, 0, 0, 1) + h.draw(cr, annotes) + cr.restore() + + cr.set_source_rgba (0., 0.5, 0, 0.8) + if self.notify: + cr.save() + cr.identity_matrix() + bnds = draw_text(cr, (0, height), self.notify, True) + l,t,w,h = bnds[1] + cr.set_source_rgba(1,1,1,0.9) + cr.rectangle(l,t+height-h, w, h) + cr.fill() + cr.set_source_rgba(0,0,0,1) + draw_text(cr, (0, height), self.notify, True) + cr.restore() + + def mouse_moved(self, e): + mouse = (e.x, e.y) + if self.transform != None: + mouse = self.transform.transform_point(e.x, e.y) + + if e.state & (gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON3_MASK): + self.interactive_level = 2 + if self.selected: + self.handles[self.selected[0]].move_to(self.selected, self.old_mouse, mouse) + self.old_mouse = mouse + self.canvas.queue_draw() + #self.redraw() + def mouse_pressed(self, e): + self.interactive_level = 1 + mouse = (e.x, e.y) + if self.transform != None: + mouse = self.transform.transform_point(e.x, e.y) + if e.button == 1: + for i,h in enumerate(self.handles): + hit = h.hit(mouse) + if hit != None: + self.selected = (i, hit) + self.mouse_down = True + self.old_mouse = mouse + self.canvas.queue_draw() + #self.redraw() + + def mouse_released(self, e): + self.interactive_level = 1 + self.selected = None + if e.button == 1: + self.mouse_down = False + self.canvas.queue_draw() + #self.redraw() + + def scroll_event(self, da, ev): + self.interactive_level = 1 + #print 'scroll:'+'\n'.join([str((x, getattr(ev, x))) for x in dir(ev)]) + #print 'end' + da.queue_draw() + hit = None + for i,h in enumerate(self.handles): + hit = h.scroll((ev.x,ev.y), ev.direction) + if hit != None: + break + return hit != None + + def key_hit(self, e): + pass + + def should_draw_numbers(self): + return True + def should_draw_bounds(self): + return 0 + + def first_time(self, argv): + pass + + def gtk_ready(self): + pass + + def resize_canvas(self, s): + pass + def redraw(self): + self.window.queue_draw() + def get_save_size(self): + return (0,0)+self.window.window.get_size() + def delete_event(self, window, e): + gtk.main_quit() + return False + + def expose_event(self, widget, event): + cr = widget.window.cairo_create() + + width, height = widget.window.get_size() + global resized + + if not resized: + alloc_size = ((0, width), (0, height)) + self.resize_canvas(alloc_size) + resized = True + cr.translate(self.origin[0], self.origin[1]) + self.draw(cr, (width, height), False) + + return True + + def mouse_motion_event(self, widget, e): + e.x -= self.origin[0] + e.y -= self.origin[1] + self.mouse_moved(e) + + return False + + def mouse_event(self, widget, e): + e.x -= self.origin[0] + e.y -= self.origin[1] + self.mouse_pressed(e) + + return False + + def mouse_release_event(self, widget, e): + e.x -= self.origin[0] + e.y -= self.origin[1] + self.mouse_released(e) + + return False + + def key_release_event(self, widget, e): + self.key_hit(e) + + return False + + def size_allocate_event(self, widget, allocation): + alloc_size = ((allocation.x, allocation.x + allocation.width), + (allocation.y, allocation.y+allocation.height)) + self.resize_canvas(alloc_size) + + return False + + def relax_interaction_timeout(self): + if self.interactive_level > 0: + self.interactive_level -= 1 + self.canvas.queue_draw() + return True + + +class Toggle: + def __init__(self): + self.bounds = (0,0,0,0) + self.text = "" + self.on = False + def draw(self, cr): + cr.set_source_rgba(0,0,0,1) + cr.rectangle(bounds.left(), bounds.top(), + bounds.width(), bounds.height()) + if(on): + cr.fill() + cr.set_source_rgba(1,1,1,1) + else: + cr.stroke() + draw_text(cr, bounds.corner(0) + (5,2), text) + + def toggle(self): + self.on = not self.on + def set(self, state): + self.on = state + def handle_click(self, e): + if bounds.contains((e.x, e,y)) and e.button == 1: + toggle() + +def toggle_events(toggles, e): + for t in toggles: + t.handle_click(e) + +def draw_toggles(cr, toggles): + for t in toggles: + t.draw(cr) + + +current_toys = [] + +def draw_text(cr, loc, txt, bottom = False, font="Sans 12"): + import pango + layout = pangocairo.CairoContext.create_layout (cr) + layout.set_font_description (pango.FontDescription(font)) + + layout.set_text(txt) + bounds = layout.get_pixel_extents() + cr.move_to(loc[0], loc[1]) + if bottom: + if bottom == "topright": + cr.move_to(loc[0] - bounds[1][2], + loc[1]) + elif bottom == "bottomright": + cr.move_to(loc[0] - bounds[1][2], + loc[1] - bounds[1][3]) + elif bottom == "topleft": + cr.move_to(loc[0], + loc[1]) + else: + cr.move_to(loc[0], loc[1] - bounds[1][3]) + cr.show_layout(layout) + return bounds + +# Framework Accessors + +# Gui Event Callbacks + +def make_about(evt): + about_window = gtk.Window(gtk.WINDOW_TOPLEVEL) + about_window.set_title("About") + about_window.set_resizable(True) + + about_text = gtk.TextView() + buf = about_text.get_buffer() + buf.set_text("Toy lib2geom application", -1) + about_window.add(about_text) + + about_window.show_all() + +def save_cairo(evt): + d = gtk.FileChooserDialog("Save file as svg, png or pdf", + None, + gtk.FILE_CHOOSER_ACTION_SAVE, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)) + + if(d.run() == gtk.RESPONSE_ACCEPT): + filename = d.get_filename() + cr_s = None + left, top, width, height = current_toys[0].get_save_size() + + if filename[-4:] == ".pdf": + cr_s = cairo.PDFSurface(filename, width, height) + elif filename[-4:] == ".png": + cr_s = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height ) + else: + cr_s = cairo.SVGSurface(filename, width, height) + cr = cairo.Context(cr_s) + cr = pangocairo.CairoContext(cairo.Context(cr_s)) + cr.translate(-left, -top) + current_toys[0].draw(cr, (width, height), True) + + cr.show_page() + del cr + del cr_s + d.destroy() + +resized = False + + +def FileMenuAction(): + pass + +ui = """ +<ui> + <menubar> + <menu name="FileMenu" action="FileMenuAction"> + <menuitem name="Save" action="save_cairo" /> + <menuitem name="Quit" action="gtk.main_quit" /> + <placeholder name="FileMenuAdditions" /> + </menu> + <menu name="HelpMenu" action="HelpMenuAction"> + <menuitem name="About" action="make_about" /> + </menu> + </menubar> +</ui> +""" + +def init(argv, t, width, height): + global current_toys + current_toys.append(t) + + t.first_time(argv) + + t.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + t.window.set_title("title") + +# Creates the menu from the menu data above + uim = gtk.UIManager() + ag = gtk.ActionGroup('menu_actiongroup') + ag.add_actions([('FileMenuAction', None, 'File', None, None, None), + ('save_cairo', None, 'Save screenshot', None, None, save_cairo), + ('gtk.main_quit', None, 'Quit', None, None, gtk.main_quit), + ('HelpMenuAction', None, 'Help', None, None, None), + ('make_about', None, 'About', None, None, make_about) + ]) + uim.insert_action_group(ag, 0) + uim.add_ui_from_string(ui) + menu = uim.get_widget("/ui/menubar") + # Creates the menu from the menu data above + #uim = gtk.UIManager() + #uim.add_ui_from_string(ui) + #menu = uim.get_widget("ui/menubar") + + t.window.connect("delete_event", t.delete_event) + + t.canvas = gtk.DrawingArea() + + t.canvas.add_events((gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.KEY_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.SCROLL_MASK)) + + t.canvas.connect("expose_event", t.expose_event) + t.canvas.connect("button_press_event", t.mouse_event) + t.canvas.connect("button_release_event", t.mouse_release_event) + t.canvas.connect("motion_notify_event", t.mouse_motion_event) + t.canvas.connect("key_press_event", t.key_release_event) + t.canvas.connect("size-allocate", t.size_allocate_event) + t.canvas.connect("scroll_event", t.scroll_event) + + t.vbox = gtk.VBox(False, 0) + t.window.add(t.vbox) + + t.vbox.pack_start (menu, False, False, 0) + + pain = gtk.VPaned() + t.vbox.pack_start(pain, True, True, 0) + pain.add1(t.canvas) + + t.canvas.set_size_request(width, height) + t.window.show_all() + + # Make sure the canvas can receive key press events. + t.canvas.set_flags(gtk.CAN_FOCUS) + t.canvas.grab_focus() + assert(t.canvas.is_focus()) + + t.gtk_ready() + gobject.timeout_add(1000, t.relax_interaction_timeout) + + gtk.main() + +def get_vbox(): + return current_toys[0].vbox |