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 [email protected] http://www.daa.com.au/mailman/listinfo/pygtk Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
