今天我们利用canvas绘制、删除图片的的函数,以及鼠标事件的绑定来制作一个简单的九宫格拼图游戏。

首先从网上下九张图,它们是把一张图分割成了九宫图,打乱后显示在canvas画布上。

接下来我们只要实现图片的选中与拖动即可,用到了鼠标左键、按下并拖动左键、鼠标右键和鼠标移动四个事件的绑定。

其他的并不难,直接看完整代码和注释吧:

from tkinter import *
import tkinter as tk
from PIL import Image, ImageTk
import random

a=0  #用来记录绘制的图片编号,其中1-9为初始的9张
List = [1,2,3,4,5,6,7,8,9]
random.shuffle(List)  #打乱图片顺序
root = Tk()
root.title("拼图游戏")
root.geometry("1400x690")
canvas = Canvas(root,width=1400, height=690)
canvas.pack()

#获得鼠标位置
def callback(event):
    global x,y
    x = event.x
    y = event.y
#删除刚绘制的图片
def delete(event):
    try:
        if a>14:  #前九张和五条边界线不能删除
            canvas.delete(a)
    except:
        return
#根据鼠标位置,确定当前区域,即选中的图片编号
def pick(event):
    global a
    if x<=230 and y<=230:
        a=1
    elif 230<x<=460 and y<=230:
        a=2
    elif 460<x<=690 and y<=230:
        a=3
    elif x<=230 and 230<y<=460:
        a=4
    elif 230<x<=460 and 230<y<=460:
        a=5
    elif 460<x<=690 and 230<y<=460:
        a=6
    elif x<=230 and 460<y<=690:
        a=7
    elif 230<x<=460 and 460<y<=690:
        a=8
    elif 460<x<=690 and 460<y<=690:
        a=9

#删除并重新绘制选中的图片
def call(event):
    global a,aa
    if a>14:  #原图和边界线不删除
        canvas.delete(a)
    exec('canvas.create_image(x,y,image=var{})'.format(i))
    a=aa+1  #记录新的图片编号,aa的初始值为14
    aa+=1

#九张图片的位置,以图片中心点为基准
lc=[[115,115],[230+115,115],[460+115,115],[115,230+115],[230+115,230+115],[460+115,230+115],[115,460+115],[230+115,460+115],[460+115,460+115]]
#绘制初始九张图
for j in range(1,10):
    load = Image.open("pt/%s.png"%List[j-1])
    exec('var{}= ImageTk.PhotoImage(load)'.format(j))  #批量创建变量
    exec('canvas.create_image(lc[j-1][0],lc[j-1][1],image=var{})'.format(j))
#绘制初始五条边界线
canvas.create_line(690,0,690,690)
canvas.create_line(690,230,1400,230,fill="white")
canvas.create_line(690,460,1400,460,fill="white")
canvas.create_line(920,0,920,690,fill="white")
aa=canvas.create_line(1150,0,1150,690,fill="white")
#绑定鼠标事件
canvas.bind("<B1-Motion>",call)  #拖动左键,移动图片
canvas.bind("<Button-1>",pick)  #点击左键,确定图片编号
canvas.bind("<Button-3>",delete)  #右键删除
root.bind("<Motion>",callback)  #移动鼠标获取坐标
root.mainloop()

注意事项:

代码中的难点是如何动态创建变量。

exec('canvas.create_image(x,y,image=var{})'.format(i))

之所以必须动态创建,是因为用上面这句赋给image值的时候,传进来的变量不能在后续发生改变,否则就会显示不出图片。因此显示九张图,就得创建九个变量var1、var2……var9,而不能用同一个变量给image传值。为了简便,我们只能动态地创建变量和调用。

另外,注意call函数中,之所以通过aa来给a赋值,而不是直接用:

exec('a=canvas.create_image(x,y,image=var{})'.format(i))

  原因在于,exec里面赋值语句是不生效的(我也不知道为啥),a不会在里面被赋值。