#python #user-interface #tkinter #callback #python-multithreading
#python #пользовательский интерфейс #tkinter #обратный вызов #python -многопоточность
Вопрос:
(прокрутите вниз для отредактированной версии этого вопроса)
Я ожидаю, что переменная «a», ставшая глобальной в функции обратного вызова «play», будет обновляться каждый раз, когда я нажимаю кнопки «play» или «stop». Как вы можете видеть, запустив код, функция обратного вызова обновляет свое значение всякий раз, когда я нажимаю кнопки, но если попытаться проверить значение глобальной переменной в главном цикле tkinter, я получаю обновление только при первом запуске программы, а затем ничего не меняется.
from tkinter import *
a = 0
class gui:
def __init__(self, window):
# play button
self.play_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.play_frame.grid(row=0, column=0, padx=1, pady=1)
self.play_button = Button(self.play_frame, text="play", fg="blue", command=lambda: self.play(1))
self.play_button.pack()
# stop button
self.stop_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.stop_frame.grid(row=0, column=2, padx=1, pady=1)
self.stop_button = Button(self.stop_frame, text="stop", fg="red", command=lambda: self.play(0))
self.stop_button.pack()
def play(self, switch):
global a
a = switch
print (a)
root = Tk()
if a == 1:
print ("one")
elif a == 0:
print ("zero")
app = gui(root)
root.mainloop()
РЕДАКТИРОВАТЬ 1:
Я написал код снова, чтобы прояснить вопрос и смоделировать то, что происходит в ситуации, которую я пытаюсь воспроизвести с помощью этих упрощенных примеров. Я ожидаю, что функция тестера будет выводить «запущено» или «не запущено» в зависимости от каждой кнопки, которую я нажимаю. Я запускаю «tester» внутри потока, потому что в актуальном проекте, над которым я работаю, «tester» — это более сложный процесс:
from tkinter import *
import threading
import time
a = 0
class gui:
def __init__(self, window):
# play button
self.play_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.play_frame.grid(row=0, column=0, padx=1, pady=1)
self.play_button = Button(self.play_frame, text="play", fg="blue", command=lambda: self.play(1))
self.play_button.pack()
# stop button
self.stop_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.stop_frame.grid(row=0, column=2, padx=1, pady=1)
self.stop_button = Button(self.stop_frame, text="stop", fg="red", command=lambda: self.play(0))
self.stop_button.pack()
def play(self, switch):
global a
a = switch
print (a)
root = Tk()
def tester(trig):
while True:
if trig == 1:
time.sleep(0.5)
print ("running")
elif trig == 0:
time.sleep(0.5)
print ("not running")
t1 = threading.Thread (target = tester, args = [a], daemon = True)
t1.start()
app = gui(root)
root.mainloop()
Комментарии:
1. Я в замешательстве, переменная не меняется при нажатии кнопки?
2. Добавьте третью кнопку, которая просто выводит значение. Вы увидите, что он меняется. Кроме того, обычно считается плохой практикой использовать глобальные переменные с классами.
3. Я отредактировал предыдущий вопрос, чтобы упростить понимание проблемы, первоначальная формулировка действительно была неясной.
Ответ №1:
если оператор if выполняется только один раз, если вы хотите распечатывать один или два при каждом нажатии кнопки, я бы предложил поместить оператор if в функцию воспроизведения.
from tkinter import *
a = 0
class gui:
def __init__(self, window):
# play button
self.play_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.play_frame.grid(row=0, column=0, padx=1, pady=1)
self.play_button = Button(self.play_frame, text="play", fg="blue", command=lambda: self.play(1))
self.play_button.pack()
# stop button
self.stop_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.stop_frame.grid(row=0, column=2, padx=1, pady=1)
self.stop_button = Button(self.stop_frame, text="stop", fg="red", command=lambda: self.play(0))
self.stop_button.pack()
def play(self, switch):
global a
a = switch
if a == 1:
print ("one")
elif a == 0:
print ("zero")
root = Tk()
app = gui(root)
root.mainloop()
Комментарии:
1. Вы также можете удалить ‘global a’, если это его единственная функция
2. Я опубликовал редактирование своего первоначального вопроса, чтобы упростить понимание вопроса, но спасибо за этот первый ответ, он фактически открыл перспективу возможного решения проблемы.
Ответ №2:
Используется передача примитивного типа в функцию pass by value
, поэтому следующая строка:
t1 = threading.Thread (target = tester, args = [a], daemon = True)
передаст значение a
(поскольку a
имеет примитивный тип int
) tester
, которому равно 0
.
Вы можете использовать IntVar
вместо for a
, так что pass by reference
используется:
from tkinter import *
import threading
import time
class gui:
def __init__(self, window):
# play button
self.play_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.play_frame.grid(row=0, column=0, padx=1, pady=1)
self.play_button = Button(self.play_frame, text="play", fg="blue", command=lambda: self.play(1))
self.play_button.pack()
# stop button
self.stop_frame = Frame(master=window, relief=FLAT, borderwidth=1)
self.stop_frame.grid(row=0, column=2, padx=1, pady=1)
self.stop_button = Button(self.stop_frame, text="stop", fg="red", command=lambda: self.play(0))
self.stop_button.pack()
def play(self, switch):
a.set(switch) # update 'a'
print(a.get())
root = Tk()
a = IntVar(value=0)
def tester(trig):
while True:
value = trig.get()
if value == 1:
time.sleep(0.5)
print ("running")
elif value == 0:
time.sleep(0.5)
print ("not running")
t1 = threading.Thread (target = tester, args = [a], daemon = True)
t1.start()
app = gui(root)
root.mainloop()