Pregunta

Mi hermano pequeño se está iniciando en la programación y, para su proyecto de la Feria de Ciencias, está simulando una bandada de pájaros en el cielo. Ha escrito la mayor parte de su código, y funciona bien, pero los pájaros necesitan moverse cada momento .

Tkinter, sin embargo, ocupa el tiempo para su propio bucle de eventos, por lo que su código no se ejecutará. Hacer root.mainloop() ejecuta, ejecuta y sigue ejecutándose, y lo único que ejecuta son los controladores de eventos.

¿Hay alguna manera de que su código se ejecute junto con el mainloop (sin subprocesos múltiples, es confuso y esto debería ser simple), y si es así, ¿qué es?

En este momento, se le ocurrió un truco feo, uniendo su función move() a <b1-motion>, de modo que mientras mantenga presionado el botón y mueva el mouse, funcionará. Pero tiene que haber una mejor manera.

¿Fue útil?

Solución

Use el método after en el objeto 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()

Aquí está la declaración y la documentación para el método <=>:

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."""

Otros consejos

La solución publicada por Bjorn da como resultado " RuntimeError: ¡Llamar a Tcl desde diferentes apartamentos " mensaje en mi computadora (RedHat Enterprise 5, python 2.6.1). Bjorn podría no haber recibido este mensaje, ya que, según un lugar I marcado , el mal manejo de subprocesos con Tkinter es impredecible y depende de la plataforma.

El problema parece ser que app.start() cuenta como una referencia a Tk, ya que la aplicación contiene elementos Tk. Lo arreglé reemplazando self.start() con un __init__ dentro de mainloop(). También lo hice para que todas las referencias Tk estén dentro de la función que llama a <=> o están dentro de las funciones que son llamadas por la función que llama a <=> (esto aparentemente es crítico para evitar el " apartamento diferente " error).

Finalmente, agregué un controlador de protocolo con una devolución de llamada, ya que sin esto el programa sale con un error cuando el usuario cierra la ventana Tk.

El código revisado es el siguiente:

# 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)

Al escribir su propio ciclo, como en la simulación (supongo), debe llamar a la función update que hace lo que hace el mainloop: actualiza la ventana con sus cambios, pero lo hace en su ciclo .

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

while 1:
   task()  

Otra opción es dejar que tkinter se ejecute en un hilo separado. Una forma de hacerlo es así:

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')

Tenga cuidado, sin embargo, la programación multiproceso es difícil y es muy fácil dispararse en el pie. Por ejemplo, debe tener cuidado al cambiar las variables miembro de la clase de muestra anterior para no interrumpir con el bucle de eventos de Tkinter.

Esta es la primera versión de trabajo de lo que será un lector GPS y un presentador de datos. tkinter es algo muy frágil con muy pocos mensajes de error. No pone cosas y no dice por qué la mayor parte del tiempo. Muy difícil venir de un buen desarrollador de formularios WYSIWYG. De todos modos, esto ejecuta una pequeña rutina 10 veces por segundo y presenta la información en un formulario. Tomó un tiempo hacerlo realidad. Cuando probé un valor de temporizador de 0, el formulario nunca apareció. ¡Ahora me duele la cabeza! 10 o más veces por segundo es lo suficientemente bueno para mí. Espero que ayude a alguien más. 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()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top