Hello :)I'm trying to create a note-taking application, very much "inspired" by the KDE-app Basket (BasKet, I don't know). I've made a gritty little video of Basket in use, if you don't know it: <URL:www.student.uni-oldenburg.de/andreas.waldenburger/Basket.mpeg> (it's DivX, about 40 seconds and roughly 1MB).
I have also attached a screenshot and the code of my experimental version.
I have several questions now:How would I draw the boxes efficiently? When moving the Boxes around, the different borders will unhinge a little (so you can see cracks between them) and I don't want that. I have considered drawing the border as a whole and then overlaying the left and right sides with eventboxes to add the current functionality. Is that preferable? What /is/ preferable? How can I shape these boxes efficiently? I know that this is answered in the FAQ; I would like to know how to reuse what I have already drawn, as in: "Make this thing transparent, *except* for where I've drawn."
Any other comments on my code are of course extremely welcome as well. regards, /W
<<inline: Screenshot-minimal.py.png>>
import pygtk pygtk.require('2.0') import gtk import gobject import cairo import math # temp text for textboxes TEXT = open("gui/bla.txt").read()[:-1] #*{{{ ContentBox class ContentBox(gtk.Table): """A Box containing a note This is a 3x3 Table, with the inner cell holding the actual content, while the outer cells contain the borders. """ #**{{{ __init__() def __init__(self, content, autonomous=True): gtk.Table.__init__(self, 3, 3) #***{{{ Components def make_content_border(size): """Set up a generic Border""" border = gtk.DrawingArea() border.add_events(gtk.gdk.BUTTON_PRESS_MASK |gtk.gdk.BUTTON_RELEASE_MASK |gtk.gdk.POINTER_MOTION_MASK ) border.connect("expose_event", self._handler_expose) border.set_size_request(size, size) # so that it has a width or height return border self.mover = make_content_border(12) self.resizer = make_content_border(12) self.top = make_content_border(8) self.bottom = make_content_border(8) self.add_events(gtk.gdk.EXPOSURE_MASK) # self.connect("expose_event", self._handler_expose) self.content = content self.attach(self.mover, 0, 1, 1, 2, gtk.FILL) self.attach(self.resizer, 2, 3, 1, 2, gtk.FILL) self.attach(self.top, 0, 3, 0, 1, gtk.FILL) self.attach(self.bottom, 0, 3, 2, 3, gtk.FILL) self.attach(self.content, 1, 2, 1, 2, gtk.EXPAND|gtk.FILL|gtk.SHRINK, xpadding=0, ypadding=0) #***}}} (Components) #***{{{ Events for child in self.get_children(): child.connect("button_press_event", self.bring_to_front) self.mover.connect("motion-notify-event", self._handler_motion) self.mover.connect("button-press-event", self._handler_motion_start) self.mover.connect("button-release-event", self._handler_motion_end) self.resizer.connect("motion-notify-event", self._handler_resize) self.resizer.connect("button-press-event", self._handler_resize_start) self.resizer.connect("button-release-event", self._handler_resize_end) #***}}} (Events) self.moving = False self.move_offset = None self.resizing = False self.resize_offset = None #**}}} (__init__()) #**{{{ Handlers #***{{{ Resize Handlers def _handler_resize_start(self, widget, event): self.resizing = True # distance from top /right/ of the ContentBox self.resize_offset = (int(widget.allocation.width-event.x), int(event.y)) print self.resize_offset self.grab_add() def _handler_resize_end(self, widget, event): self.resizing = False self.resize_offset = None self.grab_remove() def _handler_resize(self, widget, event): if self.resizing: x_off, y_off = self.resize_offset x, y = self.resizer.translate_coordinates(self, int(event.x), int(event.y)) x, y = x + x_off , y + y_off self.set_property('width-request', x) #***}}} (Resize Handlers) #***{{{ Movement Handlers def _handler_motion_start(self, widget, event): self.moving = True self.move_offset = (event.x, event.y) self.grab_add() def _handler_motion_end(self, widget, event): self.moving = False self.move_offset = None self.grab_remove() def _handler_motion(self, widget, event): if self.moving: x_off, y_off = self.move_offset x, y = self.translate_coordinates(self.parent, int(event.x), int(event.y)) x, y = x - x_off, y - y_off self.parent.move(self, int(x), int(y)) #***}}} (Movement Handlers) #***{{{ Expose Handlers def _handler_expose(self, widget, event): width, height = widget.allocation.width, widget.allocation.height cr = event.window.cairo_create() # Restrict Cairo to the exposed area; avoid extra work cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() if widget is self.top: self._draw_top(cr, width, height) elif widget is self.bottom: self._draw_bottom(cr, width, height) elif widget is self.mover: self._draw_mover(cr, width, height) elif widget is self.resizer: self._draw_resizer(cr, width, height) return False #****{{{ Border Drawing Methods def _draw_mover(self, cr, width, height): cr.set_source_rgb(0.0, 0.0, 1.0) cr.move_to(0, 0) cr.rel_line_to(0, height) cr.paint() def _draw_resizer(self, cr, width, height): cr.set_source_rgb(0.0, 0.0, 1.0) cr.move_to(width, 0) cr.rel_line_to(0, height) cr.paint() def _draw_top(self, cr, width, height): cr.set_source_rgb(0.0, 1.0, 0.0) radius = height cr.arc(radius, radius, radius, math.pi, 1.5*math.pi) cr.line_to(width-radius, 0) cr.arc(width-radius, radius, radius, 1.5*math.pi, 2*math.pi) cr.fill() def _draw_bottom(self, cr, width, height): cr.set_source_rgb(0.0, 1.0, 0.0) radius = height cr.arc_negative(radius, 0, radius, 1*math.pi, 0.5*math.pi) cr.line_to(width-radius, height) cr.arc_negative(width-radius, 0, radius, 0.5*math.pi, 0) cr.fill() #****}}} (Border Drawing Methods) #***}}} (Expose Handlers) #**}}} (Handlers) #**{{{ Public methods #***{{{ bring_to_front() def bring_to_front(self, widget, event): for child in self.get_children(): try: child.bring_to_front() except AttributeError: child.window.raise_() #***}}} (bring_to_front()) #**}}} (Public methods) #**{{{ TextContent class TextContent(gtk.TextView): def __init__(self): gtk.TextView.__init__(self) self.set_wrap_mode(gtk.WRAP_WORD_CHAR) self.get_buffer().set_text(TEXT) self.set_property('width-request', 200) #**}}} (TextContent) #*}}} (ContentBox) #*{{{ ContentPane class ContentPane(gtk.ScrolledWindow): def __init__(self): gtk.ScrolledWindow.__init__(self) self.layout = gtk.Layout() self.add(self.layout) self.layout.set_size(2000, 2000) def open_card(self, content): box = ContentBox(content) n = len(self.layout.get_children()) self.layout.put(box, 150*n, 150*n) box.show() #*}}} (ContentPane) #*{{{ Window Setup window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.connect("destroy", gtk.main_quit) # control_pane = gtk.TreeView() content_pane = ContentPane() content_pane.open_card(TextContent()) content_pane.open_card(TextContent()) window.add(content_pane) window.set_default_size(800, 600) window.show_all() gtk.main() #*}}} (Window Setup)
_______________________________________________ pygtk mailing list pygtk@daa.com.au http://www.daa.com.au/mailman/listinfo/pygtk Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/