Вывод промежуточных результатов в новом окне во время основного цикла tkinter - Threading - PullRequest
2 голосов
/ 28 апреля 2019

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

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

Я подготовил фрагмент кода, который демонстрирует мой подход:

import tkinter as tk
import threading
import time as t

class CalculationDialog(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()
        self.root = tk.Tk()
        self.textField = tk.Text(self.root)
        self.bCloseDialog = tk.Button(self.root, text="Close", state=tk.DISABLED, command=self.root.destroy)
        self.textField.grid(row=1, column=0)
        self.bCloseDialog.grid(row=2, column=0)

    def callback(self):
        self.root.quit()

    def run(self):

        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        self.root.mainloop()


def calculate():
    app = CalculationDialog()

    # Conduct calculation and output to textfield of app

    func(app.textField,0)

def func(output,input):
    for i in range(input,100):
        result = i**2
        output.insert(tk.END,str(result)+"\n")
        t.sleep(0.1)



main = tk.Tk()

buttonCalc = tk.Button(main,text="Calculate",command=calculate)
buttonCalc.pack(side=tk.TOP)

main.mainloop()

Есть ли у кого-нибудь предложениячего мне не хватает?Иногда я получаю сообщение о том, что CalculationDialog не имеет атрибута root.Однако эта ошибка не всегда возникает.

Cheers

Ответы [ 2 ]

2 голосов
/ 29 апреля 2019

Опираясь на ответ @HenryYik, который показывает правильный подход, но выдает RuntimeError при попытке нажать calculate несколько раз подряд:

RuntimeError: threads can only be started once

СледующееНапример, появляется tk.Toplevel, который показывает промежуточные результаты и позволяет повторно нажимать кнопку calculate.Он порождает столько окон верхнего уровня, что показывает столько же промежуточных результатов.
Каждый новый поток идентифицируется уникальным номером;консольный вывод печатается после завершения потока.

Кнопка close на Toplevel становится активной после завершения вычисления.

import tkinter as tk
import threading
import time


def func(thread_number=[0]):   # note: the mutable default arg creates a closure that keeps track of the thread number.
    global thr
    local_thread_number = thread_number[0]
    thread_number[0] += 1
    def close():
        thr = None
        top.destroy()
    top = tk.Toplevel(root)
    text_field = tk.Text(top)
    close_btn = tk.Button(top, text="Close", state=tk.DISABLED, command=close)
    text_field.grid(row=1, column=0)
    close_btn.grid(row=2, column=0)
    for i in range(1, 100):
        result = i**2
        if i % 10 == 0:
            text_field.insert(tk.END, str(result)+"\n")
        time.sleep(0.05)
    print(f'thread {local_thread_number} done!')
    close_btn.config(state=tk.NORMAL)


def get_and_start_thread():
    global thr
    thr = threading.Thread(target=func)
    thr.start()


root = tk.Tk()
thr = None

button_calc = tk.Button(root,text="Calculate",command=get_and_start_thread)
button_calc.pack(side=tk.TOP)

root.mainloop()
1 голос
/ 29 апреля 2019

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

Это может быть что-то вроде этого:

import tkinter as tk
import threading
import time as t

def func():
    frame = tk.Frame(root)
    frame.pack()
    textField = tk.Text(frame)
    bCloseDialog = tk.Button(frame, text="Close", state=tk.DISABLED, command=root.destroy)
    textField.grid(row=1, column=0)
    bCloseDialog.grid(row=2, column=0)
    for i in range(1,100):
        result = i**2
        textField.insert(tk.END,str(result)+"\n")
        t.sleep(0.1)

root = tk.Tk()
thr = threading.Thread(target=func)

buttonCalc = tk.Button(root,text="Calculate",command=thr.start)
buttonCalc.pack(side=tk.TOP)

root.mainloop()
...