数字华容道-将显示数字、单击数字事件绑定及事件处理函数封装在python类中以简化编程

在博文“数字华容道游戏_用Python tkinter Canvas实现”一文中,游戏用Canvas的text实例显示数字。text实例显示数字不同,其属性tag值也不同,根据text实例属性tag值,就能找到相应数字(text实例)。为了使鼠标单击数字事件处理函数知道要移动那个数字,必须用参数将被点击数字(text实例)tag值传递给该事件处理函数。采用的方法是在Canvas中每创建一个text实例,都令其有不同tag值,同时为该text实例创建一个单击数字事件函数,该函数有参数nTag其默认值是这个text实例的tag值。单击某数字,这个text实例(数字)用自己的事件函数响应,该事件函数找到其参数nTag的默认值指定的text实例(数字),完成对被点击数字的移动和输赢判断。这样每一个text实例都有一个事件函数,占用较多资源。
本文所用方法是:创建一个新类能显示数字华容道中数字并能操作数字,新类在canvas上创建text实例显示数字。text实例显示数字不同,其属性tag值也不同,根据text实例属性tag值,就能找到显示相应数字的text实例。类要保存必要数据,包括用实例变量self.tagN保存显示不同数字的text实例的tag值。在类中绑定单击数字(text实例)事件处理函数为类实例方法,使事件处理函数能直接访问类中数据。单击某数字(text实例),text实例用事件处理函数响应,事件处理函数根据实例变量self.tagN,找到被点击数字(text实例),完成对被点击数字的移动和输赢判断,从而简化编程。这样就不必为了将text实例tag值传递给鼠标单击数字事件处理函数,象上节那样为每一个text实例创建一个事件处理函数,使程序变得复杂,占用较多资源。实现的框架如下(不能运行只是用来说明问题):

class numSquare():
    X0=Y0=0 		#类变量,空位所在位置的坐标(X0,Y0),所有类实例共用
    def __init__(self,x,y,n):    #构造函数
        self.tagN="A"+n       #保存下句在Canvas中创建的text实例的tag值
   		#在Canvas中创建text实例显示数字,text实例显示数字不同,其属性tag值也不同
   		w.create_text(x,y,text=n,fill="red",tag=self.tagN,font=("Arial",40))
   		#绑定单击数字(text实例其tag=self.tagN)事件函数为类实例方法self.leftClick
        w.tag_bind(self.tagN,'<Button-1>',self.leftClick)
    def leftClick(self,event):	#类实例方法
        x,y=w.coords(self.tagN)#得到被点击的数字所在位置坐标
        w.move(self.tagN,numSquare.X0-x,numSquare.Y0-y)   #移动数字到空位
w=tk.Canvas(root,width=300,height=260,background="white")#建立Canvas对象
w.pack()
numSquare(0,0,5)     #创建numSquare类实例,参数对应类构造函数的参数x,y,n

创建numSquare实例将首先调用该类构造函数,并将numSquare类的实例ID传递给self。在构造函数中,首先用实例变量self.tagN保存将要建立的text实例的tag值,然后在Canvas中建立text实例用来显示数字,最后使用self.tag将相应的text实例和鼠标左键单击事件绑定,事件处理函数leftClick(self,event)是类的实例方法,可访问numSquare类的self.tag,通过self.tag可以访问这个text实例的所有数据。我们不能错误地认为,每创建一个numSquare实例,就要创建一个事件处理函数leftClick。实际情况是无论有多少个numSquare实例,leftClick方法只有一个,当然不同类实例的实例数据是不同的。本例所有numSquare实例中text实例的事件处理函数都是这个唯一的leftClick实例方法。当单击某数字(text实例)后,这个text实例响应鼠标左键单击事件,其事件处理函数leftClick从包含该数字(text实例)的numSquare实例中得到self.tagN,根据self.tagN找到被点击数字(text实例),从而完成对被点击数字(text实例)的移动和输赢判断。完整程序如下:

import tkinter as tk
import random
class numSquare():
    X0=Y0=0                       #类变量,空位所在位置的坐标(X0,Y0),所有类实例共用
    gameOver=False
    canvasID=0
    labelID=0
    def __init__(self,x,y,n):     #构造函数
        self.tagN="A"+n           #保存下句在Canvas中创建的text实例的tag值
        numSquare.canvasID.create_text(x,y,text=n,fill="red",tag=self.tagN,font=("Arial",40))
        numSquare.canvasID.tag_bind(self.tagN,'<Button-1>',self.leftClick)        #绑定左键单击事件
    def leftClick(self,event):    #类实例方法
        x,y=numSquare.canvasID.coords(self.tagN)          #得到被点击的数字所在位置的坐标
        #判断被单击数字是否和空白处相邻并且游戏未结束
        if abs(x-numSquare.X0)+abs(y-numSquare.Y0)==60 and numSquare.gameOver==False: 
            numSquare.canvasID.move(self.tagN,numSquare.X0-x,numSquare.Y0-y)      #移动数字到空白处
            numSquare.X0,numSquare.Y0=x,y                 #记录新空白处位置坐标
            numbers=list('087654321')
            for y in range(90,270,60):           #y为Canvas上数字的y坐标,90,150,210,即行坐标
                for x in range(90,270,60):       #x为Canvas上数字的x坐标,90,150,210,即列坐标
                    m=numbers.pop()
                    if m!= '0':                 #如m!= '0',8个数字还未检查完
                        #tag='A1'对象显示1,在(90,90)位置正确,tag='A2'字符显示2,在(150,90)处正确等等
                        if [x,y]!=numSquare.canvasID.coords("A"+m):#如相应字符对象位置不正确,玩家未赢
                            return
                    else:                       #如m=='0',8个数字所在位置正确,玩家赢了
                        numSquare.gameOver=True
                        numSquare.labelID['text']="你赢了"
root = tk.Tk()                                #初始化窗口
root.title('数字华容道_用类实现')             #窗口标题
root.geometry("300x260+200+20")     #窗口宽300,高=260,窗口左上点离屏幕左边界200,离屏幕上边界距离20。
root.resizable(width=False,height=False)    #设置窗口是否可变,这里宽不可变,高不可变,默认为True
w = tk.Canvas(root, width = 300, height = 260, background = "white")        #建立Canvas对象
w.pack()
numSquare.canvasID=w
numbers=list('123456780')
random.shuffle(numbers)             #使列表数字和空白随机排列
numSquare.labelID=tk.Label(numSquare.canvasID,text='单击数字移动方块',fg='red',font=("Arial",15))
numSquare.labelID.place(x=20,y=10,width=250,height=40)
for x in range(60,300,60):          #画棋盘4条竖线,x为Canvas的x坐标
    w.create_line(x,60,x,240,fill = "black",width = 3)
for y in range(60,300,60):          #画棋盘4条横线,y为Canvas的y坐标
    w.create_line(60,y,240,y,fill = "black",width = 3)
for y in range(90,270,60):           #y为Canvas上数字的y坐标,90,150,210,即行坐标
    for x in range(90,270,60):       #x为Canvas上数字的x坐标,90,150,210,即列坐标
        n=numbers.pop()
        if n!='0':                  #如n!='0',建立8个数字对象
            numSquare(x,y,n)
        else:                           #n=0,代表空白,记住无数字的空位的Canvas坐标(x,y)
            numSquare.X0,numSquare.Y0=x,y            
root.mainloop()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值