simple graphics

Anton Vredegoor anton at vredegoor.doge.nl
Tue Oct 7 09:55:30 EDT 2003


"db" <donald.braman at yale.edu> wrote:

>I've mostly used python for database programming & file scripting. As such I
>haven't had much need for graphics. Now I need to create a simple grid of
>colored squares and change the colors in response to simple scripts I've
>written. I'm wondering what your experiences have been with graphics in
>Python & if there are any learning resources you know of that I can access
>on the web or in a book.

Here's an example vector graphics script using tkinter. Maybe you can
adapt it to your needs or extract some techniques out of it.

Anton.

# polyedit.py by Anton vredegoor <anton at vredegoor.doge.nl> Oct 7, 2003
""" 
    This is a test version of a polygon editor. The script
    displays a window with a grid of colored squares. Individual
    squares can be selected and deselected with a mouseclick. 
    
    After *three or more* squares are selected a click with the
    other mouse button makes a polygon visible. Another click
    with this button hides the polygon again. Hiding the polygon
    is sometimes necessary to access squares "under" it. 
    
    Functionality:
    
    requirements: Python 2.3 with Tkinter (standard installation)
    usage: just run the script with no arguments
    
    button1: (left mouse button) selects/deselects squares
    button3: (right mouse button) shows/hides the polygon
                (at least 3 squares need to be selected to be able
                to draw a polygon)
    
    key "c": clear all entered data
    key "p": print self.n and the list of selected polygons 
    key "ESC": exit application
    
    Notes:
    
    - by changing the number in function "main": 
    a = PolyEdit(root,10)  it is possible to get more or less squares
    displayed. (this number is also the first number that is printed
    after pressing "p")
    - since this is a vector graphic application, resizing the
    application window will also resize and center the squares inside
    the window
    - the exact order in with the squares are selected is important,
    deselecting the polygons in reverse order can be usefull
    sometimes
    
    Example polyedit output:
    
    10 [(4, 5), (3, 3), (5, 4), (3, 5), (4, 3), (5, 5), (3, 4),
    (5, 3)]
    10 [(2, 2), (2, 6), (7, 6), (7, 5), (3, 5), (3, 3), (5, 3),
    (5, 7), (6, 7), (6, 2)]
"""

from __future__ import division
from Tkinter import *

class AB(tuple):
    """ groups values by two, access is by property a or b"""
    def __new__(cls, *args):
        return tuple.__new__(cls, args)
    def geta(self): return self[0]
    def getb(self): return self[1]
    a,b = map(property,[geta,getb])

class Scaler(AB):
    """ maps values from one coordinate system to another"""
    
    def __init__(self, *args):
        a,b = self.a,self.b
        fx = (b.b.a-b.a.a)/(a.b.a-a.a.a)
        fy = (b.b.b-b.a.b)/(a.b.b-a.a.b)
        f = min(fx,fy)
        wxc = (a.a.a+a.b.a)/2
        wyc = (a.a.b+a.b.b)/2
        vxc = (b.a.a+b.b.a)/2
        vyc = (b.a.b+b.b.b)/2
        xc = vxc-f*wxc
        yc = vyc-f*wyc
        self.f,self.xc,self.yc = f,xc,yc

    def scalepoint(self, P):
        f,xc,yc = self.f,self.xc,self.yc
        return AB(f*P.a+xc, f*P.b+yc)

    def scalerect(self, R):
        f,xc,yc = self.f,self.xc,self.yc
        p1 = AB(f*R.a.a+xc, f*R.a.b+yc)
        p2 = AB(f*R.b.a+xc, f*R.b.b+yc)
        return AB(p1,p2)
        
class PolyEdit:
    """ tkinter application to edit polygons"""

    def __init__(self, master,n):
        self.n=n
        self.canvas = Canvas(master,width=500,height=500)
        self.canvas.pack(fill= BOTH, expand=YES)
        master.bind("<Escape>", lambda event='ignored',
            m=master: m.destroy())
        master.bind("<Configure>", self.configure)
        master.bind("<Key-p>", self.output)
        master.bind("<Key-c>", self.clear)
        self.canvas.bind("<Button-1>", self.toggleselect)
        self.canvas.bind("<Button-3>", self.togglepoly)
        self.rects = [self.canvas.create_rectangle(0,0,0,0)
            for i in range(n) for j in range(n)]
        self.polypoints = []
        self.polygons = []
        self.polyshown = False
    
    def output(self,event=None):
        print self.n,self.polypoints

    def togglepoly(self,event=None):
        if self.polyshown: self.hidepoly()
        else: self.showpoly()
    
    def hidepoly(self,event=None):
        for x in self.polygons:
            self.canvas.delete(x)
        self.polyshown = False

    def clear(self,event=None):
        c = self.canvas
        self.hidepoly()
        self.polypoints = []
        self.polygons = []
        rects = c.find_withtag("selected")
        for r in rects:
                c.dtag(r,"selected")
                c.itemconfig(r,fill = "yellow")

    def toggleselect(self,event):
        c,x,y = self.canvas,event.x, event.y 
        cvid = c.find_closest(x,y)
        if cvid[0] in self.rects:
            i,j = divmod(cvid[0]-1,self.n)
            if "selected" in c.gettags(cvid):
                c.dtag(cvid,"selected")
                c.itemconfig(cvid,fill = "yellow")
                self.polypoints.remove((i,j))
            else:
                c.itemconfig(cvid,tags = ("selected"),
                    fill = "red")
                self.polypoints.append((i,j))
            if self.polyshown:
                self.hidepoly()
                self.showpoly()

    def configure(self, event):
        self.draw()

    def draw(self):
        polyshown =  self.polyshown
        self.hidepoly()
        c = self.canvas
        scale = Scaler(self.b,self.a)
        for rect in self.rects:
            i,j = divmod(rect-1,self.n)
            r = scale.scalerect(AB(AB(i,j),AB(i+1,j+1)))
            c.coords(rect,r.a.a,r.a.b,r.b.a,r.b.b)
            if "selected" in c.gettags(rect):
                c.itemconfig(rect,fill = "red")
            else:
                c.itemconfig(rect,fill = "yellow")
        if polyshown:  self.showpoly()

    def showpoly(self,event=None):
        c = self.canvas
        scale = Scaler(self.b,self.a)
        pts = []
        if len(self.polypoints)>2:
            for i,j in self.polypoints:
                pts.append(scale.scalepoint(AB(i+.5,j+.5)))
            self.polygons.append(c.create_polygon(pts,
                                            fill="black"))
        self.polyshown = True
            
    def geta(self):
        c = self.canvas
        p1 = AB(0,0)
        p2 = AB(c.winfo_width(), c.winfo_height())
        return AB(p1,p2)
    
    def getb(self):
        p1 = AB(0,0)
        p2 = AB(self.n,self.n)
        return AB(p1,p2)

    a,b = map(property,[geta,getb])

def main():
    root = Tk()
    root.title('Polygon Editor')
    a = PolyEdit(root,10)
    root.mainloop()
    
if __name__=='__main__':
    main()





More information about the Python-list mailing list