pythoncanvastkinterstipple

What is the simplest way to stipple a Tkinter canvas widget?


I have a Tkinter canvas widget and I'd like to create a stipple pattern on it. I know I could do it manually using the create_line method. However, I'd think there would seem to be a better way. Any help would be much appreciated.

What I'm currently working with. (press Alt-F4 to exit the program)

import Tkinter, re

class StippleCanvas(Tkinter.Tk):
    def __init__(self, parent):
        Tkinter.Tk.__init__(self, parent)


        self.overrideredirect(1)
        self.resizable(False,False)
        self.wm_attributes("-topmost", 1)
        self.attributes("-alpha", 0.9)

        w = 90
        h = 90

        self.Ca = Tkinter.Canvas(self, width=w, height=h, highlightthickness=0, bd=0, bg='grey25')
        self.Ca.grid(column=0, row=0)
        self.bind('<Button-1>', self.relative_mouse_position)
        self.bind('<ButtonRelease-1>', self.wid_unbind)

        i0 = 0
        while i0 < w:
            i1 = 0
            while i1 < h:
                self.Ca.create_line(i1, i0, i1+1, i0, fill='grey20', width=1)
                i1 = i1 + 2
            i0 = i0 + 2

    def geom_grab (self):
        Cursorgfltr = re.compile(r"(\d+), (\d+)")
        CursorPos = self.winfo_pointerxy()
        CursorPosGr = Cursorgfltr.search(str(CursorPos))
        self.CursorX = int(CursorPosGr.group(1))
        self.CursorY = int(CursorPosGr.group(2))

        gfltr = re.compile(r"(\d+)?x?(\d+)?([+-])(\d+)([+-])(\d+)")

        gf = gfltr.search(self.wm_geometry())
        self.X = int(gf.group(4))
        self.Y = int(gf.group(6))

    def relative_mouse_position (self, event):
        self.geom_grab()

        RelX = self.CursorX - self.X
        RelY = self.CursorY - self.Y

        self.Ca.bind( "<Motion>", lambda event: self.wid_drag(event, RelX, RelY) )

    def wid_drag (self, event, RelX, RelY):


        self.geom_grab()

        X = self.CursorX - RelX
        Y = self.CursorY - RelY

        if X < 0:
            X = 0

        if Y < 0:
            Y = 0

        self.wm_geometry('+' + str(X) + '+' + str(Y))


    def wid_unbind (self, event):
        self.Ca.unbind("<Motion>")

def run():
    StippleCanvas(None).mainloop()
if __name__ == '__main__':
    run()

Solution

  • The simplest? That depends on your definition of "simple". One way is to use a drawing program to create the image you want for the background, then place that in the canvas. This presumes you know the size of the canvas ahead of time. There's nothing much simpler than inserting a single image to the canvas.

    A variation on this theme is to create a small tile, and use tkinter's image processing to create a new image of the appropriate size with the original image tiled in the new image. This can be done every time the canvas is resized. Some examples of this can be seen at the following link, though they are in Tcl rather than Python (converting it to python is very straight-forward so don't let that spook you):

    http://wiki.tcl.tk/4389

    Though, frankly, for a simple stipple what you've done is fine. I'd move the stipple-drawing to a function so it can be re-run whenever the canvas changes size (ie: when it gets a <Configure> event).