Domanda

Il mio fratellino si sta appena dedicando alla programmazione e, per il suo progetto Science Fair, sta facendo una simulazione di uno stormo di uccelli nel cielo. Ha ottenuto la maggior parte del suo codice scritto e funziona bene, ma gli uccelli devono spostare ogni momento .

Tkinter, tuttavia, dedica il tempo per il proprio ciclo di eventi, e quindi il suo codice non verrà eseguito. Esegue root.mainloop() corre, corre e continua a funzionare, e l'unica cosa che esegue sono i gestori di eventi.

C'è un modo per far funzionare il suo codice insieme al mainloop (senza multithreading, è confuso e questo dovrebbe essere mantenuto semplice), e in tal caso, che cos'è?

In questo momento, ha escogitato un brutto hack, legando la sua funzione move() a <b1-motion>, in modo che, finché tiene premuto il pulsante e fa oscillare il mouse, funzioni. Ma deve esserci un modo migliore.

È stato utile?

Soluzione

Utilizza il metodo after sull'oggetto Tk:

from tkinter import *

root = Tk()

def task():
    print("hello")
    root.after(2000, task)  # reschedule event in 2 seconds

root.after(2000, task)
root.mainloop()

Ecco la dichiarazione e la documentazione per il metodo <=>:

def after(self, ms, func=None, *args):
    """Call function once after given time.

    MS specifies the time in milliseconds. FUNC gives the
    function which shall be called. Additional parameters
    are given as parameters to the function call.  Return
    identifier to cancel scheduling with after_cancel."""

Altri suggerimenti

La soluzione pubblicata da Bjorn genera un " RuntimeError: chiamata a Tcl da un altro appartamento " messaggio sul mio computer (RedHat Enterprise 5, python 2.6.1). Bjorn potrebbe non aver ricevuto questo messaggio, poiché, secondo un posto I spuntato , gestire male il threading con Tkinter è imprevedibile e dipende dalla piattaforma.

Il problema sembra essere che app.start() conti come riferimento a Tk, poiché l'app contiene elementi Tk. Ho risolto il problema sostituendo self.start() con un __init__ all'interno di mainloop(). Ho anche fatto in modo che tutti i riferimenti Tk siano all'interno della funzione che chiama <=> o all'interno delle funzioni chiamate da la funzione che chiama <=> (questa è apparentemente fondamentale per evitare l '" diverso appartamento " errore).

Infine, ho aggiunto un gestore di protocollo con un callback, poiché senza questo il programma esce con un errore quando la finestra Tk viene chiusa dall'utente.

Il codice modificato è il seguente:

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

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

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        label = tk.Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
    print(i)

Quando scrivi il tuo ciclo, come nella simulazione (suppongo), devi chiamare la funzione update che fa quello che mainloop fa: aggiorna la finestra con le tue modifiche, ma lo fai nel tuo ciclo .

def task():
   # do something
   root.update()

while 1:
   task()  

Un'altra opzione è quella di consentire l'esecuzione di tkinter su un thread separato. Un modo per farlo è così:

import Tkinter
import threading

class MyTkApp(threading.Thread):
    def __init__(self):
        self.root=Tkinter.Tk()
        self.s = Tkinter.StringVar()
        self.s.set('Foo')
        l = Tkinter.Label(self.root,textvariable=self.s)
        l.pack()
        threading.Thread.__init__(self)

    def run(self):
        self.root.mainloop()


app = MyTkApp()
app.start()

# Now the app should be running and the value shown on the label
# can be changed by changing the member variable s.
# Like this:
# app.s.set('Bar')

Attenzione però, la programmazione multithreading è difficile ed è davvero facile sparare a se stessi nel piede. Ad esempio, devi fare attenzione quando modifichi le variabili membro della classe di esempio sopra in modo da non interrompere il ciclo degli eventi di Tkinter.

Questa è la prima versione funzionante di quello che sarà un lettore GPS e un presentatore di dati. tkinter è una cosa molto fragile con troppi messaggi di errore. Non mette roba e non dice perché per la maggior parte del tempo. Molto difficile proveniente da un buon sviluppatore di moduli WYSIWYG. Comunque, questo esegue una piccola routine 10 volte al secondo e presenta le informazioni in un modulo. Ci è voluto un po 'per farlo accadere. Quando ho provato un valore di timer pari a 0, il modulo non è mai apparso. Mi fa male la testa! 10 o più volte al secondo è abbastanza buono per me. Spero che aiuti qualcun altro. Mike Morrow

import tkinter as tk
import time

def GetDateTime():
  # Get current date and time in ISO8601
  # https://en.wikipedia.org/wiki/ISO_8601 
  # https://xkcd.com/1179/
  return (time.strftime("%Y%m%d", time.gmtime()),
          time.strftime("%H%M%S", time.gmtime()),
          time.strftime("%Y%m%d", time.localtime()),
          time.strftime("%H%M%S", time.localtime()))

class Application(tk.Frame):

  def __init__(self, master):

    fontsize = 12
    textwidth = 9

    tk.Frame.__init__(self, master)
    self.pack()

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Time').grid(row=0, column=0)
    self.LocalDate = tk.StringVar()
    self.LocalDate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalDate).grid(row=0, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Date').grid(row=1, column=0)
    self.LocalTime = tk.StringVar()
    self.LocalTime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalTime).grid(row=1, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Time').grid(row=2, column=0)
    self.nowGdate = tk.StringVar()
    self.nowGdate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGdate).grid(row=2, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Date').grid(row=3, column=0)
    self.nowGtime = tk.StringVar()
    self.nowGtime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGtime).grid(row=3, column=1)

    tk.Button(self, text='Exit', width = 10, bg = '#FF8080', command=root.destroy).grid(row=4, columnspan=2)

    self.gettime()
  pass

  def gettime(self):
    gdt, gtm, ldt, ltm = GetDateTime()
    gdt = gdt[0:4] + '/' + gdt[4:6] + '/' + gdt[6:8]
    gtm = gtm[0:2] + ':' + gtm[2:4] + ':' + gtm[4:6] + ' Z'  
    ldt = ldt[0:4] + '/' + ldt[4:6] + '/' + ldt[6:8]
    ltm = ltm[0:2] + ':' + ltm[2:4] + ':' + ltm[4:6]  
    self.nowGtime.set(gdt)
    self.nowGdate.set(gtm)
    self.LocalTime.set(ldt)
    self.LocalDate.set(ltm)

    self.after(100, self.gettime)
   #print (ltm)  # Prove it is running this and the external code, too.
  pass

root = tk.Tk()
root.wm_title('Temp Converter')
app = Application(master=root)

w = 200 # width for the Tk root
h = 125 # height for the Tk root

# get display screen width and height
ws = root.winfo_screenwidth()  # width of the screen
hs = root.winfo_screenheight() # height of the screen

# calculate x and y coordinates for positioning the Tk root window

#centered
#x = (ws/2) - (w/2)
#y = (hs/2) - (h/2)

#right bottom corner (misfires in Win10 putting it too low. OK in Ubuntu)
x = ws - w
y = hs - h - 35  # -35 fixes it, more or less, for Win10

#set the dimensions of the screen and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))

root.mainloop()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top