TkInter отключить кнопки во время работы потока - PullRequest
0 голосов
/ 09 июля 2019

У меня есть приложение, которое пытается генерировать текст при нажатии кнопки.Большую часть времени генерация текста происходит быстро, но есть одна функция, выполнение которой занимает около 20 секунд (в зависимости от объема текста).Во время этого процесса графический интерфейс пользователя зависал, поэтому я перенес эту функцию в отдельный поток, и в этом поле все нормально.

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

Вот код, который у меня есть:

def generate_text():
    choice = dropdown_choice.get()
    if context_obj.context_text.get() == '':
        if choice == 'OpenAI':
            context = 'Some random context text'

        else:
            context = ' '
    else:
        context = context_obj.context_text.get()

    if choice == 'OpenAI':
        progress.start(50)
        progress_bar_text = Label(text='Please wait while the text is being generated',
                            background='#E5F2FF',
                            font=("Helvetica", 12))

        progress_bar_text.place(relx=.2, 
                            rely=.66, 
                            anchor="c")

         # multithreading for the OpenAI text generation
        q = queue.Queue()
        thread1 = Thread(target=openAI_generator.sample, args=[text_amount.get(), temperature.get(), context, q])
        thread1.start()

        def display_AI_text(q):
            openAI_text = q.get()
            generated_text.configure(state='normal')
            generated_text.delete(1.0,END)
            generated_text.insert(tk.END, openAI_text)
            generated_text.configure(state='disabled')

            progress.stop()
            progress_bar_text.place_forget()

        thread2 = Thread(target=display_AI_text, args=[q])
        thread2.start()

В этом коде thread1 выполняет функцию, а thread2 принимает входные данные этой функции и отображает их.

Я хотел бы сделать, пока выполняется thread2,все кнопки, которые нужно отключить, и когда поток завершится, кнопки снова станут активными.

Я попытался добавить:

thread2 = Thread(target=display_AI_text, args=[q])
generate_button.config(state="disabled")
thread2.start()

, а затем:

thread2.join()
generate_button.config(state="normal")

но этот код останавливает приложениеЯ предполагаю, что основной поток ожидает завершения thread2, и поэтому он не отвечает.

Кто-нибудь знает способ преодоления этой проблемы?

1 Ответ

1 голос
/ 09 июля 2019

Во многих GUI вы не можете изменить GUI в потоке - вы должны сделать это в основном процессе.

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

В Tkinter вы можете использовать

root.after(time_in_milliseconds, function_name) 

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

Или он может периодически проверять

thread2.is_alive()

вместо использования thread2.join(), поскольку is_alive() не блокирует код.

import tkinter as tk
from threading import Thread
import time

def long_running_function():
    print('start sleep')
    time.sleep(3)
    print('end sleep')

def start_thread():
    global t
    global counter

    b['state'] = 'disable'
    counter = 0

    t = Thread(target=long_running_function)
    t.start()

    check_thread()
    # or check after 100ms
    # root.after(100, check_thread) 

def check_thread():
    global counter

    if not t.is_alive():
        b['state'] = 'normal'
        l['text'] = ''
    else:
        l['text'] = str(counter)
        counter += 0.1

        # check again after 100ms
        root.after(100, check_thread) 

#-----------------------------------------------------

# counter displayed when thread is running        
counter = 0

root = tk.Tk()

l = tk.Label(root)
l.pack()

b = tk.Button(root, text="Start", command=start_thread)
b.pack()

root.mainloop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...