It's actually a pretty good demonstration of the Placer Geometry Manager which allows widgets to be precisely placed relative to the top, left corner of their containing, or ancestor, frame.
# File: puzzle.py
# References:
# 'Python and Tkinter Programming' by John Grayson, p90
# http://www.tcl.tk/man/tcl8.5/TkCmd/place.htm
# http://www.tcl.tk/man/tcl8.5/TkCmd/colors.htm
from tkinter import *
from tkinter import ttk
from demopanels import MsgPanel, SeeDismissPanel
class PuzzleDemo(Frame):
def __init__(self, isapp=True, name='puzzledemo'):
Frame.__init__(self, name=name)
self.pack(expand=Y, fill=BOTH)
self.master.title('15 Puzzle Demo')
self.isapp = isapp
self._create_widgets()
def _create_widgets(self):
if self.isapp:
MsgPanel(self, ["A 15-puzzle appears below as a collection of buttons. ",
"Click on any of the pieces next to the space and that ",
"piece will slide over the space.\n\n",
"Continue this until the pieces are arranged in numerical ",
"order from upper-left to lower-right."])
SeeDismissPanel(self)
self._create_demo_panel()
def _create_demo_panel(self):
bgColor = 'gray80' # colour for panel background and empty space
# if width and height are not specifically set buttons are positioned
# in a 0 size window and do not show up
demoPanel = Frame(self, borderwidth=2, relief=SUNKEN, background=bgColor,
width=120, height=120)
demoPanel.pack(side=TOP, pady=1, padx=1)
# buttons are placed relative to the top, left corner of demoPanel
# with relations expressed as a value between 0.0 and 1.0
# top, left corner = (x,y) = (0,0)
# bottom, right corner = (x,y) = (1,1)
self.xypos = {}
self.xypos['space'] = (.75, .75)
order = [3, 1, 6, 2, 5, 7, 15, 13, 4, 11, 8, 9, 14, 10, 12]
for i in range(15):
num = order[i]
self.xypos[num] = ( i%4 * .25, i//4 * .25)
b = ttk.Button(text=num, style='Puzzle.TButton')
b['command'] =lambda b=b: self._puzzle_switch(b)
b.place(in_=demoPanel, relx=self.xypos[num][0], rely=self.xypos[num][1],
relwidth=.25, relheight=.25)
# set button background to demoPanel background
ttk.Style().configure('Puzzle.TButton', background=bgColor)
def _puzzle_switch(self, button):
num = button['text']
sx = self.xypos['space'][0] # position of 'space'
sy = self.xypos['space'][1]
x = self.xypos[num][0] # position of selected button
y = self.xypos[num][1]
# is the selected button next to the space?
if( sy-.01 <= y <= sy+.01 and sx-.26 <= x <= sx+.26
or sx-.01 <= x <= sx+.01 and sy-.26 <= y <= sy+.26):
# swap button with space
self.xypos['space'], self.xypos[num] = self.xypos[num], self.xypos['space']
# re-position button
button.place(relx=self.xypos[num][0], rely=self.xypos[num][1])
if __name__ == '__main__':
PuzzleDemo().mainloop()