Таргетинг на кнопки Python

#python #tkinter

#python #tkinter

Вопрос:

Я пытаюсь создать функцию, которая автоматически создает кнопки. Это я сделал легко, но как мне создать эти кнопки в цикле, у которых есть специальное имя, чтобы при нажатии кнопки функция знала, какая кнопка нажата, и могла изменить ТЕСТ этих кнопок? До сих пор я ставил

 from tkinter import Tk, Label, Button
import random as rdn

x_user = 'X'
o_user = 'O'

window = Tk()
window.resizable(False, False)

def button_pressed():
    Button = 'Test'

for rows in range(3):
    button_number = 1
    for colums in range(3):
        Button (window, text='-', width='5', height='5', command=button_pressed) .grid(row=rows, column=colums)
        button_number  = 1

window.mainloop()
  

Ответ №1:

Я бы сказал, что это ограничение tkinter в том, что при нажатии кнопки выполняемая функция не даст вам представления о том, что ее вызвало.

Однако в Python вы можете сгенерировать функцию «на лету», чтобы обойти это ограничение. Мой пример заключается в следующем:

 def sayit(s):
    def f():
        print("Button %s is pressed" % s)
    return f

for rows in range(3):
    button_number = 1
    for colums in range(3):
        Button (window, text='-', width='5', height='5', command=sayit(button_number)) .grid(row=rows, column=colums)
        button_number  = 1
  

sayit() Функция принимает один параметр и возвращает функцию, которая не принимает аргумент. Когда я создаю кнопку, я назначаю эту сгенерированную на лету функцию в качестве команды.

Если вы хотите превратить это в игру в крестики-нолики, вам также нужно иметь что-то, что удерживало бы весь текст кнопок и связывало бы его с вашим номером кнопки (например, список), чтобы вы могли сбрасывать текст каждой кнопки при нажатии. Как показано ниже:

 from tkinter import Tk, Label, Button, StringVar
import random as rdn

x_user = 'X'
o_user = 'O'
who = 'X'

window = Tk()
window.resizable(False, False)

def click(n):
    def f():
        global who
        buttons[n].set(who)
        who = 'O' if who == 'X' else 'X'
    return f

buttons = []
button_number = 0
for rows in range(3):
    for colums in range(3):
        s = StringVar()
        s.set("-")
        buttons.append(s)
        Button(window, textvariable=s, width='5', height='5', command=click(button_number)).grid(row=rows, column=colums)
        button_number  = 1

window.mainloop()
  

Ответ №2:

Простой способ — переопределить функцию button_pressed() , чтобы она принимала параметр, который является кнопкой, на которую нажимают. Затем установите для command опции кнопки значение lambda функция, в которой button_pressed() вызывается сама кнопка в качестве параметра функции. Ниже приведен пример кода:

 from tkinter import *

window = Tk()
window.resizable(False, False)

marks = ['X', 'O']
colors = ['red', 'green']
player = 0

def button_pressed(btn):
    global player
    # change the button text and make it not clickable
    btn.config(text=marks[player], disabledforeground=colors[player], state='disabled')
    player = 1 - player # change player

for row in range(3):
    for col in range(3):
        btn = Button(window, text='', font=('', 24, 'bold'), width=3)
        btn.config(command=lambda b=btn: button_pressed(b))
        btn.grid(row=row, column=col)

window.mainloop()