Tkinter: Əsas hadisə qarşısını almaq üçün "dondurma"

Başlanğıc düyməsinə və bir tərəqqi barına sahib kiçik bir GUI testi var. İstədiyiniz davranış:

  • "Başlat"
  • Progressbar 5 saniyəyə salır
  • Progressbar göstəricisi durur

Gözlənilən davranış: "Start" düyməsinə 5 saniyə dondurulur, tərəqqi göstəricisi göstərilir (tərəddüd etmədən).

İşdə kodum:

 class GUI: def __init__(self, master): self.master = master self.test_button = Button(self.master, command=self.tb_click) self.test_button.configure( text="Start", background="Grey", padx=50 ) self.test_button.pack(side=TOP) def progress(self): self.prog_bar = ttk.Progressbar( self.master, orient="horizontal", length=200, mode="indeterminate" ) self.prog_bar.pack(side=TOP) def tb_click(self): self.progress() self.prog_bar.start() # Simulate long running process t = threading.Thread(target=time.sleep, args=(5,)) t.start() t.join() self.prog_bar.stop() root = Tk() root.title("Test Button") main_ui = GUI(root) root.mainloop() 

Burada Bryan Oakley-dən məlumatlara əsaslanaraq mən dərinliyi istifadə etməməm lazım olduğunu başa düşürəm. Mən bir axın yaratmağa çalışdım, amma hesab edirəm ki, axın əsas axından başlayıb, bu kömək etmir.

Mantıksal hissəni başqa bir sinifə yerləşdirmək və bu sinifdən bir GUI örneğini yaratmaq fikri var idi, burada A. Rodas kodunun nümunəsinə bənzərdir.

Mənim sualım:

Bunun əmrini necə kodlaya biləcəyini bilmirəm:

 self.test_button = Button(self.master, command=self.tb_click) 

başqa bir sinifdə olan funksiyanı çağırır. Bu pis bir şey mi, yoxsa mümkündür? Self.tb_click işləyən 2-ci sinif yaratmaq üçün necə? Əla işləyən nümunə kodunu izləməyə çalışdım. Rodas. Amma hərəkətə tətil edən bir Button widgetı işində həllini necə həyata keçirəcəyini anlamam.

Bir axını bir GUI sinifindən idarə etməliyəmsə, əsas mövzuya müdaxilə etməyən bir axın yaratmaq üçün necə?

26
25 мая '13 в 4:16 2013-05-25 04:16 Dirty Penguin 25 May 'da 4:16' də təyin olunur 2013-05-25 04:16
@ 3 cavab

Əsas mövzuya yeni bir mövzu bağladığınızda, axının sonunu gözləməyəcəkdir, belə ki GUI çox iş parçacığını istifadə etsəniz də blok olacaq.

Mantıksal hissəni başqa bir sinifə yerləşdirmək istəyirsinizsə, birbaşa Mövzuya tabe olursunuz və sonra bir düyməyə toxunaraq bu sinifin yeni bir obyektinə başlamalısınız. Bu subclass Thread konstruktoru bir Queue obyekti əldə edə bilər və sonra GUI bir hissəsi ilə keçə bilərsiniz. Buna görə də, mənim təklifim:

  • Ana mövzuya bir sıra obyekti yaradın.
  • Bu sıraya daxil olan yeni bir mövzu yaradın.
  • Əsas mövzuya növbə baxın

Sonra istifadəçi eyni düyməni iki dəfə (hər klik ilə yeni bir mövzu yaradan) basdıqda nə baş verdiyini problemin həll edilməsi lazımdır, ancaq başlanğıc düyməsini aradan qaldıraraq onu zəngdən self.prog_bar.stop() .

 import Queue class GUI: # ... def tb_click(self): self.progress() self.prog_bar.start() self.queue = Queue.Queue() ThreadedTask(self.queue).start() self.master.after(100, self.process_queue) def process_queue(self): try: msg = self.queue.get(0) # Show result of the task if needed self.prog_bar.stop() except Queue.Empty: self.master.after(100, self.process_queue) class ThreadedTask(threading.Thread): def __init__(self, queue): threading.Thread.__init__(self) self.queue = queue def run(self): time.sleep(5) # Simulate long running process self.queue.put("Task finished") 
41
25 мая '13 в 11:17 2013-05-25 11:17 Cavab A. Rodas tərəfindən 25 May 'da 11:17' də verilir 2013-05-25 11:17

Problem t.join (), tıklama hadisə imkanlarını blok edir, əsas mövzu yenidən işləmə üçün hadisə loopuna qayıtsın. E-poçt göndərərkən Tkinter-də bir prosesin və ya TTK tərəqqi barının bloklandığından sonra ttk Progressbar niyə görünür

border=0
2
25 мая '13 в 4:32 2013-05-25 04:32 Cavab 25 May 'da 4:32' də jmihalicza tərəfindən verilir 2013-05-25 04:32

Mən alternativ həlli üçün əsas göndərəcəyəm. Bu, hər bir TK tərəqqi göstəricisinə aid deyil, lakin bunun üçün çox asanlıqla həyata keçirilə bilər.

Tk-nin fonunda digər vəzifələri yerinə yetirmək, zəruri hallarda TK-nun yeniləməsini və gui-ni məhdudlaşdırmadığınız bir neçə sinif var!

TkRepeatingTask və BackgroundTask dərsləri aşağıdakılardır:

 import threading class TkRepeatingTask(): def __init__( self, tkRoot, taskFuncPointer, freqencyMillis ): self.__tk_ = tkRoot self.__func_ = taskFuncPointer self.__freq_ = freqencyMillis self.__isRunning_ = False def isRunning( self ) : return self.__isRunning_ def start( self ) : self.__isRunning_ = True self.__onTimer() def stop( self ) : self.__isRunning_ = False def __onTimer( self ): if self.__isRunning_ : self.__func_() self.__tk_.after( self.__freq_, self.__onTimer ) class BackgroundTask(): def __init__( self, taskFuncPointer ): self.__taskFuncPointer_ = taskFuncPointer self.__workerThread_ = None self.__isRunning_ = False def taskFuncPointer( self ) : return self.__taskFuncPointer_ def isRunning( self ) : return self.__isRunning_ and self.__workerThread_.isAlive() def start( self ): if not self.__isRunning_ : self.__isRunning_ = True self.__workerThread_ = self.WorkerThread( self ) self.__workerThread_.start() def stop( self ) : self.__isRunning_ = False class WorkerThread( threading.Thread ): def __init__( self, bgTask ): threading.Thread.__init__( self ) self.__bgTask_ = bgTask def run( self ): try : self.__bgTask_.taskFuncPointer()( self.__bgTask_.isRunning ) except Exception as e: print repr(e) self.__bgTask_.stop() 

Burada istifadə etdiklərini göstərən TK testidir. Demoyu hərəkətdə görmək istəyirsinizsə, sadəcə bu dərsləri modul altına əlavə edin:

 def tkThreadingTest(): from tkinter import Tk, Label, Button, StringVar from time import sleep class UnitTestGUI: def __init__( self, master ): self.master = master master.title( "Threading Test" ) self.testButton = Button( self.master, text="Blocking", command=self.myLongProcess ) self.testButton.pack() self.threadedButton = Button( self.master, text="Threaded", command=self.onThreadedClicked ) self.threadedButton.pack() self.cancelButton = Button( self.master, text="Stop", command=self.onStopClicked ) self.cancelButton.pack() self.statusLabelVar = StringVar() self.statusLabel = Label( master, textvariable=self.statusLabelVar ) self.statusLabel.pack() self.clickMeButton = Button( self.master, text="Click Me", command=self.onClickMeClicked ) self.clickMeButton.pack() self.clickCountLabelVar = StringVar() self.clickCountLabel = Label( master, textvariable=self.clickCountLabelVar ) self.clickCountLabel.pack() self.threadedButton = Button( self.master, text="Timer", command=self.onTimerClicked ) self.threadedButton.pack() self.timerCountLabelVar = StringVar() self.timerCountLabel = Label( master, textvariable=self.timerCountLabelVar ) self.timerCountLabel.pack() self.timerCounter_=0 self.clickCounter_=0 self.bgTask = BackgroundTask( self.myLongProcess ) self.timer = TkRepeatingTask( self.master, self.onTimer, 1 ) def close( self ) : print "close" try: self.bgTask.stop() except: pass try: self.timer.stop() except: pass self.master.quit() def onThreadedClicked( self ): print "onThreadedClicked" try: self.bgTask.start() except: pass def onTimerClicked( self ) : print "onTimerClicked" self.timer.start() def onStopClicked( self ) : print "onStopClicked" try: self.bgTask.stop() except: pass try: self.timer.stop() except: pass def onClickMeClicked( self ): print "onClickMeClicked" self.clickCounter_+=1 self.clickCountLabelVar.set( str(self.clickCounter_) ) def onTimer( self ) : print "onTimer" self.timerCounter_+=1 self.timerCountLabelVar.set( str(self.timerCounter_) ) def myLongProcess( self, isRunningFunc=None ) : print "starting myLongProcess" for i in range( 1, 10 ): try: if not isRunningFunc() : self.onMyLongProcessUpdate( "Stopped!" ) return except : pass self.onMyLongProcessUpdate( i ) sleep( 1.5 ) # simulate doing work self.onMyLongProcessUpdate( "Done!" ) def onMyLongProcessUpdate( self, status ) : print "Process Update: %s" % (status,) self.statusLabelVar.set( str(status) ) root = Tk() gui = UnitTestGUI( root ) root.protocol( "WM_DELETE_WINDOW", gui.close ) root.mainloop() if __name__ == "__main__": tkThreadingTest() 

İdxalın iki nöqtəsi. Mən vurğulamaq istəyirəm ki, BackgroundTask:

1) Arxa fonda işlədiyiniz funksiya, əgər mümkünsə, yolun ortasında vəzifəni ləğv etməyə imkan verən zəng və hörmət funksiyasına bir göstərici qəbul etməlidir.

2) Proqramdan çıxdığınız zaman fon vəzifəsinin dayandırılmasını təmin etməlisiniz. Bu mövzu hələ həll olunmayacaq, hətta əgər gui bağlanarsa, həll etməyəcəksiniz!

1
30 янв. BuvinJ-ə verilən cavabı Jan 30 2017-01-30 01:29 '17, 1:29 2017-01-30 01:29

Etiketler haqqında digər suallar və ya bir sual