【问题标题】:Python Minesweeper game with GUI using Tkinter How to display on buttons correctly?使用 Tkinter 的带有 GUI 的 Python 扫雷游戏如何正确显示在按钮上?
【发布时间】:2011-09-09 16:24:06
【问题描述】:

我正在 python 2.7 中创建一个扫雷游戏。我在尝试创建 GUI 时遇到了几个问题。对于一些基本的设置窗口,我使用了一个名为 easyGUI 的库(可在此处找到:http://easygui.sourceforge.net/),但这不是我的问题。问题在于实际的扫雷窗口本身。我不确定如何在右键单击时放置标志,或者在左键单击空间时运行递归“location_reveal”函数。另一个问题是如何更改按钮上的文本以表示有多少边界地雷,以及每次用户做出决定时标志的位置。我遇到了一个巨大的问题,因为我认为我可以每次都销毁窗口并使用更新重新创建它,但这也不起作用。任何帮助将不胜感激。

from easygui import *
import random
import os
import Tkinter

def game_new():
    n = enterbox(msg='Enter your name.', title='Welcome new user!', strip=True)
    while n.strip() == "":
        n = enterbox(msg='Oops you forgot to enter a name!', title='Welcome new user!', strip=True)
    a = buttonbox(msg='Choose a game difficulty', title='Configuration', choices = ['Beginner','Intermediate','Expert','Custom'])
    if a[0] == 'B':
        return n, 9, 9, 10
    elif a[0] == 'I':
        return n, 16, 16, 40
    elif a[0] == 'E':
        return n, 30, 16, 99
    else:
        a,b,c = customconfigure()
        return n,a,b,c

def customconfigure():
    msg = "Minesweeper configuration:"
    title = "Settings"
    fieldNames = ["Width in mines (max 30):","Height in mines (max 20):","Mines:"]
    fieldValues = []  # we start with blanks for the values
    fieldValues = multenterbox(msg,title, fieldNames)
    while 1:
        if fieldValues == None: break
        errmsg = ""
        for i in range(len(fieldNames)):
            if fieldValues[i].strip() == "":
                errmsg += ('"%s" was left blank.\n\n' % fieldNames[i])
            elif not fieldValues[i].strip().isdigit:
                errmsg += ('"%s" must be an integer.\n\n' % fieldNames[i])
            elif i == 0 and not 3 <= int(fieldValues[i]) <= 30:
                errmsg += ('Width must be between 3 and 30 mines.\n\n')
            elif i == 1 and not 3 <= int(fieldValues[i]) <= 20:
                errmsg += ('Height must be between 3 and 20 mines.\n\n')
            elif i == 2 and not 1 <= int(fieldValues[i]) <= (int(fieldValues[0]) * int(fieldValues[1]) - 1):
                errmsg += ('Mines must be between 1 and ' + str(int(fieldValues[0]) * int(fieldValues[1]) - 1) + '\n\n')                
        if errmsg == "":
            break # no problems found
        fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
    return int(fieldValues[0]),int(fieldValues[1]),int(fieldValues[2])

def inchk(s,m):
    if not s.isdigit:
        return False
    if 1 <= int(s) <= m:
        return True
    else:
        return False    

def move_get():
    user_x = raw_input("Enter the 'x' of the point you want to preform an action on: ")
    while not inchk(user_x,width):
        user_x = raw_input("Invalid selection. Enter a value for 'x': ")
    user_y = raw_input("Enter the 'y' of the point you want to preform an action on: ")
    while not inchk(user_y,height):
        user_y = raw_input("Invalid selection. Enter a value for 'y': ")
    return int(user_x), int(user_y)


def location_reveal(x,y):
    global field
    global showing    
    global symbol_mine
    if field[x][y] == symbol_mine:
        game_over()
    else:
        showing[x][y] = " " + str(field[x][y]) + " "
        if showing[x-1][y] == "     " and field[x-1][y] != symbol_mine:
            location_reveal(x-1,y)
        if showing[x+1][y] == "     " and field[x+1][y] != symbol_mine:
            location_reveal(x+1,y)        
        if showing[x][y+1] == "     " and field[x][y+1] != symbol_mine:
            location_reveal(x,y+1)
        if showing[x][y-1] == "     " and field[x][y-1] != symbol_mine:
            location_reveal(x,y-1)
    playing()

def location_chosen(s):
    global field
    global showing
    x = int(s[:s.index(":")]) + 1
    y = int(s[s.index(":")+1:]) + 1
    msg = "Choose an action to "
    choices = ["Reveal","Flag","Back"]
    reply = buttonbox(msg,choices=choices)
    field_hid.destroy
    if reply == "Back":
        playing()
    elif reply == "Flag":
        showing[x][y] = " F "
        playing()
    else:
        location_reveal(x,y)

def playing():
    global field
    global showing
    global width
    global height
    global symbol_mine
    win = False
    def k():
        field_hid.destroy()
    count_mine = 0
    count_flag = 0
    for x in field:
        count_mine += x.count(symbol_mine)
    for x in showing:
        count_flag += x.count(' F ')
    for x in range(1, width):
        for y in range(1, height):
            if field[x][y] == symbol_mine and showing[x][y] == ' F ':
                if count_mine == count_flag:
                    win = True                 
                else:
                    win = False
                    break               


    field_hid = Tkinter.Tk()    
    for x in range(width):
        for y in range(height):
            s = str(x) + ":" + str(y)
            t = showing[x+1][y+1]
            b = Tkinter.Button(field_hid, text = t, command = lambda s=s: location_chosen(s))
            #b.bind('<Button-1>', field_hid.destroy())
            b.pack()
            b.grid(row=x, column=y)
    Tkinter.mainloop()  

def main():
    global width
    global height
    global field
    global showing
    global symbol_mine
    user_name, width, height, mines = game_new()
    symbol_empty = ' '
    symbol_mine = 'M' 
    play = True

    #Creates field with empty spaces
    field = [[symbol_empty for h in range(width+2)] for w in range(height+2)]    #
    showing = [["     " for h in range(width+2)] for w in range(height+2)]

    # Randomly places mines on the field
    mines_placed = 0
    while mines_placed < mines:
        y = random.randint(1,width)#
        x = random.randint(1,height)#
        if field[x][y] == symbol_empty:
            field[x][y] = symbol_mine
            mines_placed += 1

    # Checks How many mines border each square
    for x in range(1,height+1):
        for y in range(1, width+1):
            if field[x][y] != symbol_mine:
                mines_touching = 0
                for x2 in range(x-1,x+2):
                    for y2 in range(y-1,y+2):
                        if field[x2][y2] == symbol_mine:
                            mines_touching += 1
                if mines_touching > 0:
                    field[x][y] = str(mines_touching)

    #Creates the playing field
    playing()

main()

【问题讨论】:

    标签: python user-interface button tkinter minesweeper


    【解决方案1】:

    您可以使用按钮的configure 方法更改按钮上的文本(例如:b1.configure(text="whatever"))。您不需要在每次更新时重新创建整个窗口。只需创建一次按钮并根据需要更改文本或图像。

    例如,将您的按钮定义更改为如下所示:

    b = Tkinter.Button(field_hid, text = t)
    b.configure(command = lambda s=s, button=b: location_chosen(s,button))
    

    然后,把location_chosen的参数改成这样:

    def location_chosen(s,button):
        ...
    

    一旦你这样做了,方法location_chosen 将被传递给被点击的按钮的引用。然后,您可以通过 button.configure(...) 重新配置该按钮以具有您想要的任何标签或图像

    当然,另一种方法是将引用存储在数组或字典中。例如,您可以添加:

    self.button[s] = b
    

    这样,当您需要通过 x,y 引用特定按钮时,您可以引用 button[s](例如:self.button["1:2"].configure(...)

    【讨论】:

    • 感谢您的回复。我尝试使用配置,但是我所有的按钮都具有相同的名称: field_hid = Tkinter.Tk() for x in range(width): for y in range(height): s = str(x) + ":" + str( y) t = 显示[x+1][y+1] b = Tkinter.Button(field_hid, text = t, command = lambda s=s: location_chosen(s)) #b.bind(' ', field_hid.destroy()) b.pack() b.grid(row=x, column=y) Tkinter.mainloop()
    • @Nick Delbin:您可以重用相同的局部变量,但每个小部件仍然是一个单独的实体。您只需要保存参考。我推荐一本字典,以二元组为键。例如,self.button[(row,column)] = Tkinter.Button(...)。但是,对于您拥有的设计,您实际上并不需要它,因为您可以将小部件的引用作为回调的参数传递(例如:b = Tkinter.button(...); b.configure(command=lambda button=b: ...)
    • 我不确定你的意思是什么,有什么方法可以更具体吗?我了解使用配置,但它如何确定我希望更改文本的按钮?
    • @Nick Delben:我更新了我的答案,加入了一个示例,展示了如何将按钮引用传递给回调。
    • 感谢所有帮助。当我使用您建议的方法传递它时,它没有更改特定按钮,它更改了网格中的最后一个按钮。
    猜你喜欢
    • 1970-01-01
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-30
    相关资源
    最近更新 更多