python应用学习(六)——tkinter制作连连看小游戏


前言

  最近想做一个练练的小游戏给家里的小朋友玩儿,但是苦于选取素材,然后在一个巨佬的博客里找了灵感,就借用一下粉丝的头像试试爬取网页里的图片吧!(感谢各位啦!)

完成总目标:
  爬取粉丝头像作为素材,完成一个连连看的小游戏
故本文分为两部分内容:
1、爬取素材部分(见python应用学习(五)——requests爬取网页图片);
2、利用素材完成连连看小游戏部分


(二)游戏制作部分

实现目标:

  利用上次爬取的粉丝头像集作为素材完成连连看游戏的制作,游戏界面展示:

在这里插入图片描述

一、准备

1、python环境

2、涉及到的python库需要 pip install 包名 安装

二、游戏简单介绍

1、游戏规则

(1)两个选中的方块是相同的。
(2)两个选中的方块之间连接线的折点不超过两个(连接线由X轴和Y轴的平行线组成。
本游戏增加了智能查找功能,当玩家自己无法找到可以消去的两个方块时右击画面系统则会提示可以消去的两个方块。

2、游戏设计所需的图片库:

需要十张图片作为游戏素材,素材选取我在第一部分内容中通过爬虫爬取了粉丝的图片作为我的设计素材。

三、游戏设计

I、创建Point点类

class Point:   
    def __init__(self,x,y):   #构造函数,用于类实例化为对象时的初始化,具体解释见
        self.x = x
        self.y = y

II、定义函数

1、产生map地图矩阵create_map()

def create_map():
    global map
    tmpMap = []
    m=(Width)*(Height)//10
    print('m=',m)

    #给map矩阵的每一个位置放上打乱顺序之后的图片索引号(每个索引号对应着固定的图片)
    for x in range(0,m):
       for i in range(0,10):# 每种方块有10个
          tmpMap.append(x)  #在tmpMap中放入0-9十种数字,每种数字十个,数字的大小对应的是imgs列表中图像对象的索引号
    
    random.shuffle(tmpMap)  #将上面顺序放入的数字打乱顺序
    for x in range(0,Width):#0--14 
       for y in range(0,Height):#0--14
           map[x][y]=tmpMap[x*Width+y]  #将tmpmap中的数据按顺序放入map形成一个10*10的矩阵
    print('-----------------map[][]----------------\n',map)

2、按照地图矩阵显示图片print_map()

def print_map( ):
    global image_map
    #将map矩阵中的索引号显示为对应的图片
    for x in range(0,Width): 
       for y in range(0,Height):
           if(map[x][y]!=' '):
               img1= imgs[int(map[x][y])]
               id=cv.create_image((x*(windowwidth/10)+(windowwidth/20),y*(windowheight/10)+(windowheight/20)),image=img1)#图像中心在窗口中摆放的水平初始偏移量为40,竖直初始偏移量为40,每个图片相对偏移为80(初始偏移量的二倍)
               image_map[x][y]=id  #把创建的图片的id放入image——map列表中与当前索引号相同的位置上,以便后续删除该id对应的图片
    cv.pack()
    # 按照图片显示位置将图片对应的imgs中的索引号显示成相应矩阵形式
    for y in range(0,Height):
        for x in range(0,Width):
            print (map[x][y],end=' ')#end = ‘ ’有了end就不是自动换行了,而是自动加空格
        print('')#自动换行

3、判断连通条件IsLink()

def IsLink (p1,p2):
    if lineLink(p1,p2):
        return True
    elif oneCornerLink(p1,p2):
        return True
    elif twoCornerLink(p1,p2):
        return True
    else:
        return False

 (1)直接连通lineLink()

def lineLink(p1,p2):
    absDistance = 0 #绝对距离(中间隔着的格子数)
    spaceCount = 0  #中间空着的格子数
    #p1和p2同行或者同列
    if (p1.x == p2.x or p1.y == p2.y):
        print('同行或者同列情况!')
        #同列情况
        if (p1.x == p2.x and p1.y != p2.y):
            print('同列')
            absDistance = abs(p1.y -p2.y) - 1 
            if p1.y > p2.y:
                zf = -1
            elif p1.y < p2.y:
                zf = 1
            for i in range(1,absDistance + 1):
                if (map[p1.x][p1.y + i * zf] == ' '):
                    spaceCount = spaceCount + 1
                else:
                    break
        #同行情况
        elif (p1.y == p2.y and p1.x != p2.x):
            print('同行')
            absDistance = abs(p1.x -p2.x) - 1 
            if p1.x > p2.x:
                zf = -1
            elif p1.x < p2.x:
                zf = 1
            for i in range(1,absDistance + 1):
                if (map[p1.x + i * zf][p1.y] == ' '):
                    spaceCount = spaceCount + 1
                else:
                    break

        if (spaceCount == absDistance):
            print("行或者列直接连通!")
            return True
        else:
            print('行/列不能消除!')
            return False
    else:
        return False  #不是同行同列直接返回False

 (2)一个折点连通oneCornerLink()

def oneCornerLink(p1,p2):
    checkP1 = Point(p1.x,p2.y)   #第一个直角点
    checkP2 = Point(p2.x,p1.y)   #第二个直角点
    #第一个直角点检测
    if (map[checkP1.x][checkP1.y] == ' '):
        if (lineLink(checkP1,p1) and lineLink(checkP1,p2)):
            print("直角消除OK",checkP1.x,checkP1.y)
            LinePointStack.append(checkP1)
            return True
    #第二个直角点检测
    if (map[checkP2.x][checkP2.y] == ' '):
        if (lineLink(checkP2,p1) and lineLink(checkP2,p2)):
            print("直角消除OK",checkP2.x,checkP2.y)
            LinePointStack.append(checkP2)
            return True
    else:
        print("不能直角消除")
        return False

 (3)两个折点连通twoCornerLink()

def twoCornerLink(p1,p2):
    #从四个方向探测
    for i in range(0,4):
        checkP = Point(p1.x,p1.y)
        #向下
        if(i == 0):
            checkP.y+=1
            while ((checkP.y < Height) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('下探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.y+=1
        #向上
        elif(i == 1):
            checkP.y-=1
            while ((checkP.y > 0) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('上探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.y-=1
        #向左
        elif(i == 2):
            checkP.x-=1
            while ((checkP.x > 0) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('左探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.x-=1
        #向右
        elif(i == 3):
            checkP.x+=1
            while ((checkP.x < Height) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('右探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.x+=1
    print("不能通过两个折点连接")
    return False

4、判断两个图片是非为同一图像IsSame()

def IsSame(p1,p2):
    if (map[p1.x][p1.y] == map[p2.x][p2.y]):
        print("两个图片相同")
        return True
    else:
        return False

5、在窗口中画网格drawgrid()

def drawgrid():
    for i in range(0,11):
        cv.create_line(0,(windowwidth/10)*i,windowwidth,(windowwidth/10)*i,width = 2)
    for i in range(0,11):
        cv.create_line((windowwidth/10)*i,0,(windowwidth/10)*i,windowwidth,width = 2)
    cv.pack()

6、画两图片的连接线drawLinkLine()

def drawLinkLine(p1,p2):
    print(LinePointStack,len(LinePointStack))
    if (len(LinePointStack)==0):
        print("直接连通")
        Line_id.append(drawLine(p1,p2))  #连接p1,p2并将连好的线的id存储在Line_id列表中
    elif (len(LinePointStack)==1):
        z=LinePointStack.pop()
        print("一折连通点z",z.x,z.y)
        Line_id.append(drawLine(p1,z))
        Line_id.append(drawLine(p2,z))
    elif (len(LinePointStack)==2):
        z1=LinePointStack.pop()
        print("2折连通点z1",z1.x,z1.y)
        Line_id.append(drawLine(p1,z1))
        z2=LinePointStack.pop()
        print("2折连通点z2",z2.x,z2.y)
        Line_id.append(drawLine(z1,z2)) 
        Line_id.append(drawLine(p2,z2))

7、清除连接线和方块clearTwoBlock()

def clearTwoBlock():
    global firstSelectRectId,SecondSelectRectId
    print('clearTwoBlock__firstSelectRectId',firstSelectRectId)
    print('clearTwoBlock__SecondSelectRectId',SecondSelectRectId)
    #清除第一个选定框线 
    cv.delete(firstSelectRectId)
    #清除第2个选定框线 
    print('here---------------------')
    cv.delete(SecondSelectRectId) 
    #清空记录方块的值
    map[p1.x][p1.y] = " "
    cv.delete(image_map[p1.x][p1.y])
    map[p2.x][p2.y] = " "
    cv.delete(image_map[p2.x][p2.y])
    Select_first = False
    delConnectLine()#清除选中方块之间连接线 

8、定义鼠标左键事件代码leftbuttonevent(event)

def leftbuttonevent(event):
    timer_interval = 0.1   # 这个间隔时间太长,那么在点击太快的话会出现误消除现象
    global Select_first,p1,p2
    global firstSelectRectId,SecondSelectRectId
    
    print ("clicked at", event.x, event.y)
    x=(event.x)//(windowwidth//10)  #换算棋盘坐标
    y=(event.y)//(windowwidth//10)
    print ("clicked at", x, y)

    if map[x][y]==" ":
       showinfo(title="提示",message="此处无方块")
    else:
        if Select_first == False:
            p1=Point(x,y)
            #画选定(x1,y1)处的框线
            firstSelectRectId=cv.create_rectangle(x*(windowwidth/10),y*(windowwidth/10),x*(windowwidth/10)+(windowwidth/10),y*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
            Select_first = True
            print('选定第一个点',x,y)
        else:
            p2=Point(x,y)
            #判断第二次点击的方块是否已被第一次点击选取,如果是则返回。
            if (p1.x == p2.x) and (p1.y == p2.y):
                return
            #画选定(x2,y2)处的框线
            print('选定第二个点',x,y)
            SecondSelectRectId=cv.create_rectangle(x*(windowwidth/10),y*(windowwidth/10),x*(windowwidth/10)+(windowwidth/10),y*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
            print('第二次点击的方块',SecondSelectRectId)
            cv.pack()

            #判断是否连通
            if IsSame(p1,p2) and IsLink(p1,p2):
                print('连通',x,y)
                Select_first = False
                #画选中方块之间连接线 
                drawLinkLine(p1,p2)
                # time.sleep(2)   #这种方法不行,直接sleep()这段时间计算机什么也不干,是不行的
                # clearTwoBlock()

                t=Timer(timer_interval,clearTwoBlock)#定时函数,等待timer_interval之后执行clearTwoBlock函数,这段时间内主程序继续进行
                t.start()  


            else:  #重新选定第一个方块  
                #清除第一个选定框线 
                cv.delete(firstSelectRectId)
                cv.delete(SecondSelectRectId)
                #print('清除第一个选定框线')
                #firstSelectRectId=SecondSelectRectId
                #p1=Point(x,y)           #设置重新选定第一个方块的坐标
                Select_first = False

9、定义鼠标右键事件代码 AutoFindLink(event)

def AutoFindLink(event):#自动查找
    global firstSelectRectId,SecondSelectRectId
    global p1,p2
    timer_interval = 0.2
    m_nRoW=Height
    m_nCol=Width
    bFound = False
    #第一个方块从地图的0位置开始
    for i in range(0, m_nRoW* m_nCol):
        #找到则跳出循环
        if (bFound):
            break
        
        #算出对应的虚拟行列位置
        x1 = i % m_nCol 
        y1 = i // m_nCol
        p1=Point(x1,y1)
        #无图案的方块跳过
        if (map[x1][y1] == ' '):
            continue
        #第二个方块从前一个方块的后面开始
        for j in range( i +1 , m_nRoW* m_nCol):
            #算出对应的虚拟行列位置
            x2 = j % m_nCol 
            y2 = j // m_nCol                
            p2=Point(x2,y2)
            # 第二个方块不为空 且与第一个方块的动物相同
            if (map[x2][y2] != ' ' and IsSame(p1,p2)):
                #判断是否可以连通
                if (IsLink(p1, p2)):
                    bFound = True
                    break
    #找到后自动消除
    if (bFound):  #p1(x1,y1)与p2(x2,y2)连通
        print('找到后',p1.x,p1.y,p2.x,p2.y)
        #drawLinkLine(p1,p2)
        #画选定(x1,y1)处的框线
        firstSelectRectId =cv.create_rectangle(x1*(windowwidth/10),y1*(windowwidth/10),x1*(windowwidth/10)+(windowwidth/10),y1*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
        #time.sleep(2)
        SecondSelectRectId=cv.create_rectangle(x2*(windowwidth/10),y2*(windowwidth/10),x2*(windowwidth/10)+(windowwidth/10),y2*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
        print('右击——firstSelectRectId',firstSelectRectId)
        print('右击——SecondSelectRectId',SecondSelectRectId)
        t=Timer(timer_interval,clearTwoBlock)    #定时函数,注意clearTwoBlock后面不要加括号,Timer的参数是要一个函数名而不是函数
        t.start() 

    return bFound 

III、游戏的主函数逻辑

LinePointStack = []   #将找出来的连接折点找出来放入列表中,以备连线时用
Line_id = []   #将连接好的线的id存储在Line_id列表中以备删除连接线
Select_first=False     #是否已经选中第一块
firstSelectRectId=-1   #被选中第一块地图对象
SecondSelectRectId=-1  #被选中第二块地图对象
Height = 10            #纵向格子数
Width = 10             #横向格子数
windowwidth = 600      #窗口大小
windowheight = 600
map =  [[" " for y in range(Height)]for x in range(Width)]         #初始化一个空二维矩阵
image_map =  [[" " for y in range(Height)]for x in range(Width)]   #初始化一个空的二维矩阵

root = Tk()
root.title('pyhton 连连看')
cv = Canvas(root, bg = 'white', width = windowwidth, height = windowheight)

#修改图片大小
icon = []
for i in range(1,11):     #从1-10共十张照片
    image = Image.open("D:\\ryc\python_learning\\10_linkup\\fan_avatar\\fans_avatar%s.png"%(i))
    image = image.resize(((windowwidth//10)-2, (windowheight//10)-2), Image.ANTIALIAS)   # // ———— 整除 ; antialias是平滑处理
    icon.append(image)

#PhotoImage()返回图像文件对象,并将十张图片放到一个列表中
imgs = []    
for i in range(0,10):
    #image = Image.open("D:\\ryc\python_learning\\10_linkup\\fan_avatar\\fans_avatar%s.png"%(i))
    image = icon[i]
    photo = ImageTk.PhotoImage(image) 
    imgs.append(photo)

drawgrid()   #画网格

cv.bind("<Button-1>", leftbuttonevent)      #鼠标左键事件
cv.bind("<Button-3>", AutoFindLink)    #鼠标右键事件   <Button-2>是鼠标滚轮键
cv.pack()

create_map()
print_map()
root.mainloop()    #窗口显示

IV、完整代码

from tkinter import *
from PIL import Image, ImageTk
import random
import time
from threading import Timer
#from tkinter.messagebox import *

#============================================创建Point点类================================================
class Point:   
    def __init__(self,x,y):   #构造函数,用于类实例化为对象时的初始化,具体解释见
        self.x = x
        self.y = y
#=========================================================================================================

#=========================================在窗口中按照map矩阵中的索引号完成图片显示==============================================
def print_map( ):
    global image_map
    #将map矩阵中的索引号显示为对应的图片
    for x in range(0,Width): 
       for y in range(0,Height):
           if(map[x][y]!=' '):
               img1= imgs[int(map[x][y])]
               id=cv.create_image((x*(windowwidth/10)+(windowwidth/20),y*(windowheight/10)+(windowheight/20)),image=img1)#图像中心在窗口中摆放的水平初始偏移量为40,竖直初始偏移量为40,每个图片相对偏移为80(初始偏移量的二倍)
               image_map[x][y]=id  #把创建的图片的id放入image——map列表中与当前索引号相同的位置上,以便后续删除该id对应的图片
    cv.pack()
    # 按照图片显示位置将图片对应的imgs中的索引号显示成相应矩阵形式
    for y in range(0,Height):
        for x in range(0,Width):
            print (map[x][y],end=' ')#end = ‘ ’有了end就不是自动换行了,而是自动加空格
        print('')#自动换行
#=============================================================================================================================

#================================产生map地图矩阵(矩阵的每个位置都存放着图片的索引号)=============================================
def create_map():
    global map
    tmpMap = []
    m=(Width)*(Height)//10
    print('m=',m)

    #给map矩阵的每一个位置放上打乱顺序之后的图片索引号(每个索引号对应着固定的图片)
    for x in range(0,m):
       for i in range(0,10):# 每种方块有10个
          tmpMap.append(x)  #在tmpMap中放入0-9十种数字,每种数字十个,数字的大小对应的是imgs列表中图像对象的索引号
    
    random.shuffle(tmpMap)  #将上面顺序放入的数字打乱顺序
    for x in range(0,Width):#0--14 
       for y in range(0,Height):#0--14
           map[x][y]=tmpMap[x*Width+y]  #将tmpmap中的数据按顺序放入map形成一个10*10的矩阵
    print('-----------------map[][]----------------\n',map)
#=============================================================================================================================

#=============================================连通条件判断=====================================================================
#========================判断两个方块是否可以连通============================
def IsLink (p1,p2):
    if lineLink(p1,p2):
        return True
    elif oneCornerLink(p1,p2):
        return True
    elif twoCornerLink(p1,p2):
        return True
    else:
        return False
#=================================直接连通==================================
def lineLink(p1,p2):
    absDistance = 0 #绝对距离(中间隔着的格子数)
    spaceCount = 0  #中间空着的格子数
    #p1和p2同行或者同列
    if (p1.x == p2.x or p1.y == p2.y):
        print('同行或者同列情况!')
        #同列情况
        if (p1.x == p2.x and p1.y != p2.y):
            print('同列')
            absDistance = abs(p1.y -p2.y) - 1 
            if p1.y > p2.y:
                zf = -1
            elif p1.y < p2.y:
                zf = 1
            for i in range(1,absDistance + 1):
                if (map[p1.x][p1.y + i * zf] == ' '):
                    spaceCount = spaceCount + 1
                else:
                    break
        #同行情况
        elif (p1.y == p2.y and p1.x != p2.x):
            print('同行')
            absDistance = abs(p1.x -p2.x) - 1 
            if p1.x > p2.x:
                zf = -1
            elif p1.x < p2.x:
                zf = 1
            for i in range(1,absDistance + 1):
                if (map[p1.x + i * zf][p1.y] == ' '):
                    spaceCount = spaceCount + 1
                else:
                    break

        if (spaceCount == absDistance):
            print("行或者列直接连通!")
            return True
        else:
            print('行/列不能消除!')
            return False
    else:
        return False  #不是同行同列直接返回False
#==============================一个折点连通=================================
def oneCornerLink(p1,p2):
    checkP1 = Point(p1.x,p2.y)   #第一个直角点
    checkP2 = Point(p2.x,p1.y)   #第二个直角点
    #第一个直角点检测
    if (map[checkP1.x][checkP1.y] == ' '):
        if (lineLink(checkP1,p1) and lineLink(checkP1,p2)):
            print("直角消除OK",checkP1.x,checkP1.y)
            LinePointStack.append(checkP1)
            return True
    #第二个直角点检测
    if (map[checkP2.x][checkP2.y] == ' '):
        if (lineLink(checkP2,p1) and lineLink(checkP2,p2)):
            print("直角消除OK",checkP2.x,checkP2.y)
            LinePointStack.append(checkP2)
            return True
    else:
        print("不能直角消除")
        return False
#==============================两个折点连通=================================
def twoCornerLink(p1,p2):
    #从四个方向探测
    for i in range(0,4):
        checkP = Point(p1.x,p1.y)
        #向下
        if(i == 0):
            checkP.y+=1
            while ((checkP.y < Height) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('下探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.y+=1
        #向上
        elif(i == 1):
            checkP.y-=1
            while ((checkP.y > 0) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('上探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.y-=1
        #向左
        elif(i == 2):
            checkP.x-=1
            while ((checkP.x > 0) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('左探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.x-=1
        #向右
        elif(i == 3):
            checkP.x+=1
            while ((checkP.x < Height) and (map[checkP.x][checkP.y] == " ")):
                if (oneCornerLink(checkP,p2)):
                    print('右探测成功!')
                    LinePointStack.append(checkP)
                    return True
                checkP.x+=1
    print("不能通过两个折点连接")
    return False
#=============================================================================================================================

#================================================判断是非为同一图像============================================================
def IsSame(p1,p2):
    if (map[p1.x][p1.y] == map[p2.x][p2.y]):
        print("两个图片相同")
        return True
    else:
        return False
#=============================================================================================================================

#=======================================================画网格================================================================
def drawgrid():
    for i in range(0,11):
        cv.create_line(0,(windowwidth/10)*i,windowwidth,(windowwidth/10)*i,width = 2)
    for i in range(0,11):
        cv.create_line((windowwidth/10)*i,0,(windowwidth/10)*i,windowwidth,width = 2)
    cv.pack()
#======================================================画连接线================================================================
#连接线的绘制
def  drawLine(p1,p2):
    print("drawLine p1,p2",p1.x,p1.y,p2.x,p2.y)  
    id=cv.create_line(p1.x*(windowwidth/10)+(windowwidth/20),p1.y*(windowwidth/10)+(windowwidth/20),p2.x*(windowwidth/10)+(windowwidth/20),p2.y*(windowwidth/10)+(windowwidth/20),width=5,fill='red')
    #cv.pack()
    return id  #存储在Line_id列表中以备删除连接线

#绘制连接线,并将连好的线的id保存在line_id列表中
def drawLinkLine(p1,p2):
    print(LinePointStack,len(LinePointStack))
    if (len(LinePointStack)==0):
        print("直接连通")
        Line_id.append(drawLine(p1,p2))  #连接p1,p2并将连好的线的id存储在Line_id列表中
    elif (len(LinePointStack)==1):
        z=LinePointStack.pop()
        print("一折连通点z",z.x,z.y)
        Line_id.append(drawLine(p1,z))
        Line_id.append(drawLine(p2,z))
    elif (len(LinePointStack)==2):
        z1=LinePointStack.pop()
        print("2折连通点z1",z1.x,z1.y)
        Line_id.append(drawLine(p1,z1))
        z2=LinePointStack.pop()
        print("2折连通点z2",z2.x,z2.y)
        Line_id.append(drawLine(z1,z2)) 
        Line_id.append(drawLine(p2,z2))
#=============================================================================================================================

#====================================================删除连接线================================================================
def delConnectLine():    
    while len(Line_id)>0:
        idpop=Line_id.pop()  #pop()函数返回值为列表中被删除的部分
        cv.delete(idpop)
#=============================================================================================================================

#=================================================清除连接线和方块==============================================================
def clearTwoBlock():
    global firstSelectRectId,SecondSelectRectId
    print('clearTwoBlock__firstSelectRectId',firstSelectRectId)
    print('clearTwoBlock__SecondSelectRectId',SecondSelectRectId)
    #清除第一个选定框线 
    cv.delete(firstSelectRectId)
    #清除第2个选定框线 
    print('here---------------------')
    cv.delete(SecondSelectRectId) 
    #清空记录方块的值
    map[p1.x][p1.y] = " "
    cv.delete(image_map[p1.x][p1.y])
    map[p2.x][p2.y] = " "
    cv.delete(image_map[p2.x][p2.y])
    Select_first = False
    delConnectLine()#清除选中方块之间连接线   
#=============================================================================================================================

#===============================================鼠标左键事件代码===============================================================
def leftbuttonevent(event):
    timer_interval = 0.1   # 这个间隔时间太长,那么在点击太快的话会出现误消除现象
    global Select_first,p1,p2
    global firstSelectRectId,SecondSelectRectId
    
    print ("clicked at", event.x, event.y)
    x=(event.x)//(windowwidth//10)  #换算棋盘坐标
    y=(event.y)//(windowwidth//10)
    print ("clicked at", x, y)

    if map[x][y]==" ":
       showinfo(title="提示",message="此处无方块")
    else:
        if Select_first == False:
            p1=Point(x,y)
            #画选定(x1,y1)处的框线
            firstSelectRectId=cv.create_rectangle(x*(windowwidth/10),y*(windowwidth/10),x*(windowwidth/10)+(windowwidth/10),y*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
            Select_first = True
            print('选定第一个点',x,y)
        else:
            p2=Point(x,y)
            #判断第二次点击的方块是否已被第一次点击选取,如果是则返回。
            if (p1.x == p2.x) and (p1.y == p2.y):
                return
            #画选定(x2,y2)处的框线
            print('选定第二个点',x,y)
            SecondSelectRectId=cv.create_rectangle(x*(windowwidth/10),y*(windowwidth/10),x*(windowwidth/10)+(windowwidth/10),y*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
            print('第二次点击的方块',SecondSelectRectId)
            cv.pack()

            #判断是否连通
            if IsSame(p1,p2) and IsLink(p1,p2):
                print('连通',x,y)
                Select_first = False
                #画选中方块之间连接线 
                drawLinkLine(p1,p2)
                # time.sleep(2)   #这种方法不行,直接sleep()这段时间计算机什么也不干,是不行的
                # clearTwoBlock()

                t=Timer(timer_interval,clearTwoBlock)#定时函数,等待timer_interval之后执行clearTwoBlock函数,这段时间内主程序继续进行
                t.start()  


            else:  #重新选定第一个方块  
                #清除第一个选定框线 
                cv.delete(firstSelectRectId)
                cv.delete(SecondSelectRectId)
                #print('清除第一个选定框线')
                #firstSelectRectId=SecondSelectRectId
                #p1=Point(x,y)           #设置重新选定第一个方块的坐标
                Select_first = False
#=============================================================================================================================

#===============================================鼠标右键事件代码===============================================================
def AutoFindLink(event):#自动查找
    global firstSelectRectId,SecondSelectRectId
    global p1,p2
    timer_interval = 0.2
    m_nRoW=Height
    m_nCol=Width
    bFound = False
    #第一个方块从地图的0位置开始
    for i in range(0, m_nRoW* m_nCol):
        #找到则跳出循环
        if (bFound):
            break
        
        #算出对应的虚拟行列位置
        x1 = i % m_nCol 
        y1 = i // m_nCol
        p1=Point(x1,y1)
        #无图案的方块跳过
        if (map[x1][y1] == ' '):
            continue
        #第二个方块从前一个方块的后面开始
        for j in range( i +1 , m_nRoW* m_nCol):
            #算出对应的虚拟行列位置
            x2 = j % m_nCol 
            y2 = j // m_nCol                
            p2=Point(x2,y2)
            # 第二个方块不为空 且与第一个方块的动物相同
            if (map[x2][y2] != ' ' and IsSame(p1,p2)):
                #判断是否可以连通
                if (IsLink(p1, p2)):
                    bFound = True
                    break
    #找到后自动消除
    if (bFound):  #p1(x1,y1)与p2(x2,y2)连通
        print('找到后',p1.x,p1.y,p2.x,p2.y)
        #drawLinkLine(p1,p2)
        #画选定(x1,y1)处的框线
        firstSelectRectId =cv.create_rectangle(x1*(windowwidth/10),y1*(windowwidth/10),x1*(windowwidth/10)+(windowwidth/10),y1*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
        #time.sleep(2)
        SecondSelectRectId=cv.create_rectangle(x2*(windowwidth/10),y2*(windowwidth/10),x2*(windowwidth/10)+(windowwidth/10),y2*(windowwidth/10)+(windowwidth/10),width=2,outline="yellow")
        print('右击——firstSelectRectId',firstSelectRectId)
        print('右击——SecondSelectRectId',SecondSelectRectId)
        t=Timer(timer_interval,clearTwoBlock)    #定时函数,注意clearTwoBlock后面不要加括号,Timer的参数是要一个函数名而不是函数
        t.start() 

    return bFound 
#=============================================================================================================================

#============================================================主函数部分=======================================================
LinePointStack = []   #将找出来的连接折点找出来放入列表中,以备连线时用
Line_id = []   #将连接好的线的id存储在Line_id列表中以备删除连接线
Select_first=False     #是否已经选中第一块
firstSelectRectId=-1   #被选中第一块地图对象
SecondSelectRectId=-1  #被选中第二块地图对象
Height = 10            #纵向格子数
Width = 10             #横向格子数
windowwidth = 600      #窗口大小
windowheight = 600
map =  [[" " for y in range(Height)]for x in range(Width)]         #初始化一个空二维矩阵
image_map =  [[" " for y in range(Height)]for x in range(Width)]   #初始化一个空的二维矩阵

root = Tk()
root.title('pyhton 连连看')
cv = Canvas(root, bg = 'white', width = windowwidth, height = windowheight)

#修改图片大小
icon = []
for i in range(1,11):     #从1-10共十张照片
    image = Image.open("D:\\ryc\python_learning\\10_linkup\\fan_avatar\\fans_avatar%s.png"%(i))
    image = image.resize(((windowwidth//10)-2, (windowheight//10)-2), Image.ANTIALIAS)   # // ———— 整除 ; antialias是平滑处理
    icon.append(image)

#PhotoImage()返回图像文件对象,并将十张图片放到一个列表中
imgs = []    
for i in range(0,10):
    #image = Image.open("D:\\ryc\python_learning\\10_linkup\\fan_avatar\\fans_avatar%s.png"%(i))
    image = icon[i]
    photo = ImageTk.PhotoImage(image) 
    imgs.append(photo)

drawgrid()   #画网格

cv.bind("<Button-1>", leftbuttonevent)      #鼠标左键事件
cv.bind("<Button-3>", AutoFindLink)    #鼠标右键事件   <Button-2>是鼠标滚轮键
cv.pack()

create_map()
print_map()
root.mainloop()    #窗口显示
#=============================================================================================================================

最后

  历经三天终于把这个东西搞定了,各种改错,其中不乏因为敲错大小写导致排错一个小时的情况,但是每个错误哪怕是再小也是需要认真分析的,这何尝不是一种乐趣呢?
  都到这儿了,创作不易,各位看官留下你们宝贵的赞吧!笔者在此拜谢!!

在这里插入图片描述

其他python应用实例见:https://blog.csdn.net/weixin_45386875/article/details/113766276

猜你喜欢

转载自blog.csdn.net/weixin_45386875/article/details/113838844