n° 185
Maggio/Giugno 2013
Maggio 20, 2013, 06:47:02 pm *
Benvenuto! Accedi o registrati.
Hai dimenticato l'e-mail di attivazione?

Accesso con nome utente, password e durata della sessione
Notizia:
 
   Indice   Linux Windows Techassistance Gameassistance videogame hardware Aiuto Ricerca Agenda Downloads Accedi Registrati  

* Messaggi recenti
Messaggi recenti
Pagine: [1]   Vai giù
  Stampa  
Autore Discussione: Tkinter grid_remove() non funziona se in thread?  (Letto 2234 volte)
0 utenti e 1 Utente non registrato stanno visualizzando questa discussione.
alex.75
invioattach
Full Member
***

Karma: +14/-3
Scollegato Scollegato

Messaggi: 341



Mostra profilo WWW
« inserita:: Maggio 01, 2012, 08:17:50 pm »

Il codice proposto non funziona come dovrebbe alla riga 105:
#self.p_bar.grid_remove()  # not work!!

Si tratta di una finestra che mostra una etichetta e una progress bar con l'avanzamento di una certa operazione eseguita da una classe apposita ("Job").
L'etichetta che mostra lo stato dei lavori e la progress bar funzionano egregiamente ma alla riga incriminata, a lavoro ultimato, dovrebbe essere "nascosta" la progress bar.
Il comando "grid_remove()" lascia il programma in stato "bloccato", anche se posto in un blocco try-except. Stesso malfunzionamento con "grid_forget()".

Codice:
import thread
import threading
import time
import Tkinter
from Tkinter import *
import ttk
import tkMessageBox

class Job():

   def __init__(self, number):
      self.stop_requested = False
      self.is_running = False
      self.number = number
      self.current = 0

   def do_work(self):
      self.stop_requested = False
      self.is_running = True
      for x in range(1, self.number+1):
         if self.stop_requested:
            break
         time.sleep(0.7)
         self.current = x
         print 'job %s' % self.current
        
      self.is_running = False

   def stop(self):
      self.stop_requested = True
      self.is_running = False      


class Application(Frame):

   def __init__(self, master=None):        
      #Frame.__init__(self, master, width=400, height=250, padx=20, pady=20, background='#F0F0FA')
      Frame.__init__(self, master, width=400, height=250)
      self.grid(padx=10, pady=10)
      self.createWidgets()

      #create a job and run it in a thread to leave __init__ else window is not drawn
      job_1 = Job(10)
      t = threading.Thread(target=self._start, args=(job_1,) )
      t.start()

   def createWidgets(self):
      print 'createWidgets'

      control_padding = {'padx':5, 'pady':3}
  
      self.label_job1 = ttk.Label(self, text='Job 1')
      self.label_job1.grid(control_padding, sticky=W)
      self.label_job1.columnconfigure(0, weight=1)

      self.label_job1_text = ttk.Label(self, text='...')
      self.label_job1_text.grid(control_padding, row=0, column=1, sticky=W)
      self.label_job1_text.columnconfigure(1, weight=3)
      

      self.label_job2 = ttk.Label(self, text='Job 2')
      self.label_job2.grid(control_padding, row=2, column=0, sticky=W)

      self.label_job2_text = ttk.Label(self, text='...')
      self.label_job2_text.grid(control_padding, row=2, column=1, sticky=W)
    

      self.p_bar = ttk.Progressbar(self, orient='horizontal', length=self['width'], mode='determinate')
      self.p_bar.grid(control_padding, columnspan=2)

      self.button_quit = ttk.Button(self, text='ABANDON', command=self.stop)
      self.button_quit.grid(control_padding, columnspan=2, sticky=SE)

   def stop(self):
      if self.job:
         self.job.stop()

      time.sleep(0.5)
      self.quit()  


   def _start(self, job_1, job_2=None):
      print 'start'
      if job_2:
         self.label_job2_text['text'] = "wait job 1 finish"
      else:
         self.label_job2_text['text'] = "not requested"
    
      self.job = job_1
      self.p_bar['maximum'] = self.job.number
      self.p_bar['value'] = 0
    
      #thread for job 1
      t = threading.Thread(target=self.job.do_work )
      t.start()
      while self.job.is_running:
         self.label_job1_text['text'] = 'running, ({current}/{total})'.format(
            current=self.job.current, total=self.job.number
            )
         self.p_bar['value'] = self.job.current
         time.sleep(0.5) #update GUI

      print 'end'
      self.label_job1_text['text'] = 'done!'  
      #self.p_bar.grid_remove()  # not work!!
      #self.p_bar['length'] = 0
      self.button_quit['text'] = "CLOSE"

      #self.stop()  # automatically close at end
      


root = Tk()
app = Application(master=root)

def quit_handler():
   if tkMessageBox.askokcancel("Quit?", "Are you sure you want to quit?"):
      app.stop()
      #root.quit()
root.protocol("WM_DELETE_WINDOW", quit_handler)
app.master.title('Progress bar test')
app.mainloop()


#if root.state == root.state.
try:
   root.destroy()
except:
   print 'fail to destroy root'
   pass        

print '### FINISH ###'

Per mia comodità il codice è presente anche qui: pastebin

Non so perché non sia possibile tale operazione.
Ho provato a far eseguire blocchi di codice in altri thread ed altre modifiche al codice ma questa resta la forma più lineare e logica e vorrei lasciarla così o migliorarla.
Per ora la cosa più importante è capire il perché, poi da li trovare il modo giusto per ottenere il risultato.

Accetto suggerimenti anche su una eventuale diversa impostazione del codice.
Di fatto volevo esporre la funzione "start()" pubblica e richiamarla da fuori della classe Application, obbligatoriamente prima di "mainloop()" ma questo comportava il dover usare un ulteriore "livello" di thread per poter far partire la "renderizzazione" della finestra (e i suoi widget) immediatamente e non dopo che il "lavoro" di Job fosse terminato, alla fine ho ceduto ed ho spostato la creazione di Job all'interno di Application. Lo scopo era tenerlo "fuori" e renderlo indipendente dalla Application che in realtà è un'alternativa alla nuda e semplice console.

La mia priorità comunque è quella di capire perché "grid_remove()" non funzioni come mi aspetto.
Sospetto sia per via del thread; al di fuori del thread in cui è eseguita la funzione "start" infatti funziona (ma ovviamente a me serve che sia chiamata li dove l'ho messa).

Alessandro

Registrato
alex.75
invioattach
Full Member
***

Karma: +14/-3
Scollegato Scollegato

Messaggi: 341



Mostra profilo WWW
« Risposta #1 inserita:: Maggio 14, 2012, 06:50:26 pm »

Risolto utilizzando il metodo "after()" dell'oggetto master (il Tk passato al Frame), usando una funzione che controlla periodicamente che il lavoro del thread sia terminato.

Codice:
    def periodicalCheck(self):
        if self.job_1 and not self.job_1.is_running:
            self._finish()
        else:
            self.master.after(100, self.periodicalCheck)

Ovviamente questa funzione deve essere richiamata dal thread principale.
Registrato
Pagine: [1]   Vai su
  Stampa  
 
Vai a:  

Copyright © 2011 Edizioni Master SpA. p.iva : 02105820787

Tutti i diritti di proprietà letteraria e artistica riservati. - Privacy



Links to Page