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/

Reply via email to