Saya tidak tahu apakah apa yang ingin saya capai bahkan mungkin. Saya telah menulis aplikasi tkinter yang mengimpor metode dari kelas eksternal. Metode ini menjalankan algoritme pendakian bukit yang akan terus berjalan dan mencoba meningkatkan "skor" yang telah dihitungnya. Setelah setiap pass, ini menyajikan output dan skor saat ini kepada pengguna dan bertanya (pada baris perintah) apakah mereka ingin melanjutkan.

Tantangan pertama dalam menjalankan ini adalah mengimplementasikan threading. Saya memiliki ini berfungsi, tetapi saya tidak tahu apakah saya telah melakukannya dengan benar.

Algoritme akan berlanjut hingga pengguna memberi sinyal bahwa mereka telah mendapatkan jawaban yang mereka cari, atau kehilangan keinginan untuk hidup dan menekan CTRL-C.

Di aplikasi utama tkinter saya, ini memberi saya dua masalah:

  1. Cara menampilkan output dari metode eksternal ini. Saya mencoba menulis while loop yang mensurvei bidang output secara berkala, (lihat bagian komentar dari metode "start_proc") tetapi itu jelas tidak akan pernah berhasil, dan selain itu, saya idealnya ingin melihat output waktu nyata; dan
  2. Cara berinteraksi dengan algoritme untuk melanjutkan atau menghentikan (lihat bagian komentar dari metode "my_long_procedure"). Seperti yang Anda lihat, saya dapat menyuntikkan atribut "berhenti", dan itu memang menghentikan algoritme, tetapi saya tidak dapat mengalihkan pandangan dari output karena jawaban yang diinginkan mungkin telah lewat sebelum saya dapat menekan stop.

Di bawah ini, saya harap, contoh sederhana dari apa yang saya coba lakukan.

Ini adalah latihan pembelajaran bagi saya dan saya akan berterima kasih atas bantuan apa pun.

import tkinter as tk
from threading import Thread
from random import randint
import time

class MyTestClass: # This would actually be imported from another module
    def __init__(self):
        self.stopped = False

    def my_long_procedure(self):
        # Fake method to simulate actual algorithm
        count = 0
        maxscore = 0
        i = 0
        while count < 1000 and not self.stopped:
            i += 1
            score = randint(1,10000)
            if score > maxscore:
                maxscore = score
                self.message = f'This is iteration {i} and the best score is {maxscore}'
                print(self.message)
                # self.carry_on = input("Do you want to continue? ")
                # if self.carry_on.upper() != "Y":
                #     return maxscore
            time.sleep(2)
        print('OK - You stopped me...')

class MyMainApp(tk.Tk):
    def __init__(self, title="Sample App", *args, **kwargs):
        super().__init__()
        self.title(title)
        self.test_run = MyTestClass()
        self.frame1 = tk.LabelFrame(self, text="My Frame")
        self.frame1.grid(row=0, column=0, columnspan=2, padx=5, pady=5, sticky=tk.NSEW)
        self.frame1.columnconfigure(0, weight=1)
        self.frame1.rowconfigure(0, weight=1)

        start_button = tk.Button(self.frame1, text="Start!",
                                command=self.start_proc).grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
        stop_button = tk.Button(self.frame1, text="Stop!",
                                command=self.stop_proc).grid(row=0, column=2, padx=5, pady=5, sticky=tk.E)
        self.output_box = tk.Text(self.frame1, width=60, height=8, wrap=tk.WORD)
        self.output_box.grid(row=1, column=0, columnspan=3, sticky=tk.NSEW)

    def start_proc(self):
        self.test_run.stopped = False
        self.control_thread = Thread(target=self.test_run.my_long_procedure, daemon=True)
        self.control_thread.start()
        time.sleep(1)
        self.output_box.delete(0.0, tk.END)
        self.output_box.insert(0.0, self.test_run.message)
        # self.control_thread.join()
        # while not self.test_run.stopped:
        #     self.output_box.delete(0.0, tk.END)
        #     self.output_box.insert(0.0, self.test_run.message)
        #     time.sleep(0.5)

    def stop_proc(self):
        self.test_run.stopped = True

if __name__ == "__main__":
    MyMainApp("My Test App").mainloop()
0
ArthurDent 4 Juli 2020, 18:04

1 menjawab

Jawaban Terbaik

Jika Anda memiliki implementasi algoritme, Anda dapat meneruskan panggilan balik (metode MyMainApp) sehingga algoritme memberi sinyal sendiri setiap kali ia telah melakukan beberapa "pekerjaan" yang layak untuk pemberitahuan kepada pengguna. Ini akan terlihat seperti:

def my_long_procedure(self,progress):

Prototipe panggilan balik dapat berupa:

 def progress(self,iteration,result):

Dan alih-alih print(self.message) Anda bisa melakukan progress(i,maxscore)

Memulai utas:

self.control_thread = Thread(target=self.test_run.my_long_procedure, daemon=True,args=(self.progress,))

Sayangnya Anda perlu menyadari bahwa Anda tidak dapat menyegarkan GUI dari utas lain selain utas utama. Ini adalah batasan tkinter yang banyak dibahas. Jadi singkatnya Anda tidak dapat langsung memanggil widget GUI Anda dari fungsi progress. Solusi untuk masalah ini adalah menyimpan progres dalam fungsi progres dan mendaftarkan fungsi yang akan dieksekusi setiap kali loop utama tkinter tidak digunakan. anda dapat melakukan sesuatu seperti self.after_idle(self.update_ui) dari progress metode. update_ui() akan menjadi metode baru yang memperbarui misalnya bilah kemajuan atau grafik Anda menggunakan data yang dikirimkan oleh panggilan balik progress dan disimpan sebagai properti MyMainApp. Lebih lanjut tentang "pola" ini (menggunakan antrian pesan alih-alih panggilan balik) di sini: https://www.oreilly.com/library/view/ python-cookbook/0596001673/ch09s07.html

1
Jean-Marc Volle 5 Juli 2020, 09:02