Tkinter Tic Tac Toe 在某个盒子里画一个形状

Tkinter Tic Tac Toe Drawing a shape in a certain box

我是编程新手,正在努力学习 python 我决定为我的第一个项目制作井字游戏。我已经制作了绘制 X 和 O 的函数,但我无法在我单击的框中绘制它们。到目前为止,这是代码:

ttt = Tk()
ttt.title("Tic Tac Toe")
w = Canvas(ttt, width = 902, height = 902)
w.configure (bg =  "white")
w.pack()

m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

def drawx(event):
    x, y = event.x, event.y
    w.create_line(49, 49, 249, 249, fill = "black")
    w.create_line(49, 249, 249, 49, fill = "black")

def drawo(event):
    x2, y2 = event.x, event.y
    x0 = x2 - 100
    y0 = y2 - 100
    x1 = x2 + 100
    y1 = y2 + 100
    return w.create_oval(x0, y0, x1, y1)

w.create_line(0, 300, 901, 300, fill = "black") 
w.create_line(0, 601, 901, 601, fill = "black")
w.create_line(300, 0, 300, 901, fill = "black")
w.create_line(601, 0, 601, 901, fill = "black")

在这里,形状将根据我的光标坐标绘制,我知道应该修改它。如有任何帮助或建议,我们将不胜感激。

您可以 "discretize" xy 值,方法是整数除法,然后乘以各个单元格的宽度(并为中心添加一些偏移量)。

def drawo(event):
    x2, y2 = event.x, event.y
    x2 = x2 // 300 * 300 + 150
    y2 = y2 // 300 * 300 + 150
    ...

drawx 相同。

或者,您可以对网格中的不同单元格使用不同的 canvas 元素(或按钮、标签或类似元素),并从 event 中获得点击的 widget

为了向 tobias_k 的回答添加更多细节,下面的代码将在单击鼠标右键时绘制一个 X,在单击鼠标左键时绘制一个 O。

代码的改进可能是使用变量来定义 canvas 和每个 X/O 的大小,而不是硬编码 200 或 300。

from tkinter import *

ttt = Tk()
ttt.title("Tic Tac Toe")
w = Canvas(ttt, width = 902, height = 902)
w.configure (bg =  "white")
w.pack()

m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

def drawx(event):
    print("drawx")
    x, y = event.x, event.y
    x0 = x // 300 * 300 + 50
    y0 = y // 300 * 300 + 50
    x1 = x0 + 200
    y1 = y0 + 200
    #return w.create_oval(x0, y0, x1, y1)
    w.create_line(x0,y0,x1,y1, fill = "black")
    w.create_line(x0,y0+200,x1,y1-200, fill = "black")

def drawo(event):
    print("drawo")
    x, y = event.x, event.y
    x0 = x // 300 * 300 + 50
    y0 = y // 300 * 300 + 50
    x1 = x0 + 200
    y1 = y0 + 200
    return w.create_oval(x0, y0, x1, y1)

w.create_line(0, 300, 901, 300, fill = "black") 
w.create_line(0, 601, 901, 601, fill = "black")
w.create_line(300, 0, 300, 901, fill = "black")
w.create_line(601, 0, 601, 901, fill = "black")

w.bind('<Button-1>',drawo)
w.bind('<Button-3>',drawx)

ttt.mainloop()

需要绑定鼠标点击事件canvas:

w.bind('<Button-1>', on_click)

然后确定在 on_click 处理程序中单击了哪个单元格:

SIZE = 300
player = 1  # 1 for O, 2 for X

def on_click(event):
    global m
    global player
    row = event.y // SIZE
    col = event.x // SIZE
    # check whether the cell is not filled yet
    if m[row][col] == 0:
        # calculate the center of the cell
        cx = col * SIZE + SIZE // 2
        cy = row * SIZE + SIZE // 2
        # draw X or O based on current player
        if player == 1:
            draw_O(cx, cy)
        else:
            draw_X(cx, cy)
        # set cell is filled
        m[row][col] = player
        # now you need to check whether current player wins
        ...
        # if no one wins, toggle player
        player = 2 if player == 1 else 1

def draw_O(x, y):
    radius = SIZE // 3
    w.create_oval(x-radius, y-radius, x+radius, y+radius, width=5, tag='cell')  # tag is used for resetting the game

def draw_X(x, y):
    radius = SIZE // 3
    w.create_line(x-radius, y-radius, x+radius, y+radius, width=5, tag='cell')
    w.create_line(x+radius, y-radius, x-radius, y+radius, width=5, tag='cell')

如果您想重置游戏:

def reset_game():
    global m
    global player
    # remove all 'O' and 'X'
    w.delete('cell')
    m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    player = 1

为了更好的设计,将以上逻辑全部放在一个class中(继承自Canvas)。然后你可以使用实例变量而不是全局变量。