запуск и остановка процесса с помощью кнопок tkinter

#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()