Skip to content

Instantly share code, notes, and snippets.

@jdmansour
Created April 18, 2013 16:38
Show Gist options
  • Save jdmansour/5414200 to your computer and use it in GitHub Desktop.
Save jdmansour/5414200 to your computer and use it in GitHub Desktop.
A little program to show rubberbanding with pygtk. Click and drag the rubberband to select points.
"""
A little program to show rubberbanding with pygtk.
Click and drag the rubberband to select points.
"""
import gtk
import random
from math import pi
def main():
w = gtk.Window()
w.add(RubberTest())
w.show_all()
gtk.main()
class RubberTest(gtk.DrawingArea):
def __init__(self, model=None):
gtk.DrawingArea.__init__(self)
self.set_size_request(300, 300)
self.connect("expose-event", self._expose)
self.connect("button-press-event", self._button_pressed)
self.connect("button-release-event", self._button_released)
self.connect("motion-notify-event", self._motion)
self.button_x = 0
self.button_y = 0
self.button_down = 0
self.sel_rect = gtk.gdk.Rectangle(-10, -10, 0, 0)
self.add_events(gtk.gdk.EXPOSURE_MASK
| gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.BUTTON_RELEASE_MASK
| gtk.gdk.POINTER_MOTION_MASK)
self.points = []
for i in range(200):
x = random.randint(0, 300)
y = random.randint(0, 300)
self.points.append((x, y))
def _button_pressed(self, widget, event):
# left mouse button
if not event.button == 1:
return False
self.button_x, self.button_y = event.x, event.y
self.button_down = True
return True
def _button_released(self, widget, event):
self.button_down = False
self.get_window().invalidate_rect(self.sel_rect, False)
def _motion(self, widget, event):
if not self.button_down:
return False
# calculate the rectangle, give it the right orientation
x1,y1 = self.button_x, self.button_y
x2,y2 = event.x, event.y
x = int(min(x1,x2))
y = int(min(y1,y2))
w = int(abs(x1-x2))
h = int(abs(y1-y2))
old = self.sel_rect
self.sel_rect = gtk.gdk.Rectangle(x,y,w,h)
win = self.get_window()
for rect in (self.sel_rect, old):
win.invalidate_rect(rect, False)
# check if the status of a point has changed
# if it has, invalidate the rect around it
for x,y in self.points:
pointrect = gtk.gdk.Rectangle(x-5, y-5, 10, 10)
if (pointrect.intersect(self.sel_rect) !=
pointrect.intersect(old)):
win.invalidate_rect(pointrect, False)
def _expose(self, widget, event):
cr = event.window.cairo_create()
cr.set_line_width(1)
cr.set_source_rgb(0, 0, 0)
rx, ry, rw, rh = self.sel_rect
for x,y in self.points:
cr.arc(x, y, 5, 0, 2*pi)
# just check if the middle of the circle is in the rubber band
if (rx <= x < rx+rw and ry <= y < ry+rh):
cr.fill_preserve()
cr.stroke()
if self.button_down:
# this is to get a non-antialiased rubber band rectangle.
# stroke needs half-integer coordinates,
# and fill/clip integer coordinates to be sharp.
#cr.translate(0.5,0.5)
# draw the selection:
x,y,w,h = self.sel_rect
if w <= 0 or h <= 0:
return True
cr.rectangle(x+1,y+1,w-2,h-2)
cr.set_source_rgba(0.2, 0.6, 0.8, 0.2)
cr.fill()
cr.rectangle(x+0.5,y+0.5,w-1,h-1)
cr.set_source_rgba(0.2, 0.6, 0.8, 0.35)
cr.set_line_width(1)
cr.stroke()
# suppress default expose handler
return True
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment