# File: canvassimple.py
# http://infohost.nmt.edu/tcc/help/pubs/tkinter//canvas.html#create_rectangle
# http://www.tcl.tk/man/tcl8.5/TkCmd/canvas.htm
#
# Ref: canvasscroll.py from
# http://tkinter.unpythonic.net/wiki/A_tour_of_Tkinter_widgets
from tkinter import *
from tkinter import ttk
from demopanels import MsgPanel, SeeDismissPanel
# ===========================================================================
# Constants
# ===========================================================================
SQUARE = 60 # width and height of rect
PAD = 20 # padding around rectangles
SIDE = SQUARE + PAD
ROWS = 10 # num of rows
COLS = 20 # num of rect in a row
BG_FILL = 'gray90' # background fill colour
CUR_FILL = 'cyan' # selected rectangle fill colour
# ===========================================================================
# Class
# ===========================================================================
class SimpleCanvasDemo(ttk.Frame):
def __init__(self, isapp=True, name='simplecanvasdemo'):
ttk.Frame.__init__(self, name=name)
self.pack(expand=Y, fill=BOTH)
self.master.title('Simple Canvas Demo')
self.isapp = isapp
self._create_widgets()
def _create_widgets(self):
if self.isapp:
MsgPanel(self,
["This window displays a canvas widget that can be ",
"scrolled either by using the scrollbars or by right ",
"clicking in a rectangle and dragging the mouse ",
"horizontally and/or vertically.\n\n",
"Click on a rectangle and its indices will be displayed ",
"in the label area below the canvas."])
SeeDismissPanel(self)
self._create_demo_panel()
def _create_demo_panel(self):
demoPanel = Frame(self)
demoPanel.pack(side=TOP, fill=BOTH, expand=Y)
# create text label
lbl = ttk.Label(text='Selected Rectangle: ',
background='LightGoldenrod1',
font=('Helv', '10', 'bold'))
lbl.pack(in_=demoPanel, side=BOTTOM, fill=X,
expand=Y, pady=10, padx=5)
# create a canvas with rectangles
canvas = self._create_grid(demoPanel)
self._create_rects(canvas)
self._create_bindings(canvas, lbl)
# ===========================================================================
# Canvas
# ===========================================================================
def _create_grid(self, parent):
# frame to hold canvas
grid = ttk.Frame(parent)
grid.pack(expand=Y, fill=BOTH, padx=1, pady=1)
# scrollable canvas (must set scrollregion)
c = Canvas(relief=SUNKEN, borderwidth=2,
background=BG_FILL,
scrollregion=(0,0,
COLS*SIDE+PAD,
ROWS*SIDE+PAD)) # (W,N,E,S)
hscroll = ttk.Scrollbar(orient=HORIZONTAL, command=c.xview)
vscroll = ttk.Scrollbar(orient=VERTICAL, command=c.yview)
c['xscrollcommand'] = hscroll.set
c['yscrollcommand'] = vscroll.set
# set canvas and scrollbar positions and
# resize behaviours
grid.rowconfigure(0, weight=1, minsize=0)
grid.columnconfigure(0, weight=1, minsize=0)
c.grid(in_=grid, padx=1, pady=1, row=0, column=0,
rowspan=1, columnspan=1, sticky='news')
vscroll.grid(in_=grid, padx=1, pady=1, row=0,
column=1, rowspan=1, columnspan=1,
sticky='news')
hscroll.grid(in_=grid, padx=1, pady=1, row=1,
column=0, rowspan=1, columnspan=1,
sticky='news')
return c
def _create_rects(self, c):
# create 10 rows of rectangles,
# with 20 rectangles per row
# initial colour matches canvas bg
# each rectangle is 'tagged' with its name
# (same as text displayed in the rectangle)
for i in range(COLS): # columns
x = PAD + ( i * SIDE)
for j in range(ROWS): # rows
y = PAD + (j * SIDE)
name = '{},{}'.format(i,j)
c.create_rectangle(x, y, x+SQUARE, y+SQUARE,
outline='black',
fill=BG_FILL,
tags=('rect', name))
c.create_text(x+(SQUARE/2), y+(SQUARE/2),
text=name,
anchor=CENTER,
tags=('text', name))
# ===========================================================================
# Canvas Bindings
# ===========================================================================
def _create_bindings(self, canvas, lbl):
# highlight rectangle under mouse
canvas.tag_bind('all', '<Any-Enter>', self._enter_rect )
canvas.tag_bind('all', '<Any-Leave>', self._leave_rect )
# display rectangle indices on 'select'
canvas.tag_bind('all', '<1>',
lambda evt, l=lbl, c=canvas: self._sel_rect(c, l))
# drag canvas (scroll using right mouse button)
canvas.tag_bind('all', '<3>',
lambda evt, c=canvas: self._begin_drag(evt, c))
canvas.tag_bind('all', '<B3-Motion>',
lambda evt, c=canvas: self._drag_canvas(evt, c))
# ===========================================================================
# Canvas bound methods (callbacks)
# ===========================================================================
def _sel_rect(self, canvas, label):
item = self._get_current_rect(canvas) # selected object
tags = canvas.gettags('current') # associated tags
for t in tags:
if t not in ('rect', 'current', 'text'):
# tag must be the object name
label['text'] = 'Selected Rectangle: ({})'.format(t)
def _enter_rect(self, evt):
w = self.nametowidget(evt.widget) # get canvas
item = self._get_current_rect(w) # get selected rect
w.itemconfigure(item, fill=CUR_FILL) # change fill colour
def _leave_rect(self, evt):
w = self.nametowidget(evt.widget) # get canvas
item = self._get_current_rect(w) # get selected rect
w.itemconfigure(item, fill=BG_FILL) # change fill colour
def _get_current_rect(self, c):
item = c.find_withtag('current') # id of current item
tags = c.gettags('current') # associated tags
if 'text' in tags: # text item?
item = c.find_below(item) # id of containing rect
return item
# drag canvas (scroll)
def _begin_drag(self, evt, canvas):
canvas.scan_mark(evt.x, evt.y) # initial right-mouse click
def _drag_canvas(self, evt, canvas):
canvas.scan_dragto(evt.x, evt.y) # capture dragging
# ===========================================================================
# Main
# ===========================================================================
if __name__ == '__main__':
SimpleCanvasDemo().mainloop()
Wednesday, August 8, 2012
Tkinter Simple Canvas Demo
This code is based on the Tcl cscroll.tcl demo with reference to the Tkinter Tour canvasscroll.py demo. It demonstrates a scrollable canvas containing a number of rectangles displayed in a grid.
Labels:
Tkinter Canvas,
Tkinter Demos
