你如何与Tkinter的事件循环一起运行自己的代码?
我的小弟弟正在进行编程,他的科学博览会项目正在模拟一群天空中的鸟儿。 他的大部分代码都已经写好了,而且效果很好,但是鸟儿需要随时移动。
但是,Tkinter花费了自己事件循环的时间,所以他的代码不会运行。 做root.mainloop()
运行,运行,并继续运行,它唯一运行的事件处理程序。
有没有办法让他的代码与主循环一起运行(没有multithreading,这是混乱,这应该保持简单),如果是这样,它是什么?
现在,他想出了一个丑陋的黑客,将他的move()
函数绑定到<b1-motion>
,这样只要他按下button并且摆动鼠标,它就可以工作。 但是有一个更好的方法。
在Tk
对象上使用after
方法:
from tkinter import * root = Tk() def task(): print("hello") root.after(2000, task) # reschedule event in 2 seconds root.after(2000, task) root.mainloop()
以下是after
方法的声明和文档:
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."""
Bjorn发布的解决scheme导致我的计算机(RedHat Enterprise 5,python 2.6.1)出现“RuntimeError:从不同公寓调用Tcl”的消息。 比约恩可能没有得到这个消息,因为根据我检查过的一个地方 ,错误地处理与Tkinter的线程是不可预测的和依赖于平台的。
问题似乎是app.start()
视为对Tk的引用,因为应用程序包含Tk元素。 我通过在__init__
self.start()
replaceapp.start()
解决这个问题。 我也做过这样的事情,所有的Tk引用或者在调用mainloop()
的函数内部,或者在调用mainloop()
函数的函数内部(这对于避免“不同的公寓”错误显然是关键的)。
最后,我添加了一个带callback的协议处理程序,因为没有这个程序,当用户closuresTk窗口时,程序退出时会报错。
修改后的代码如下:
# 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)
在编写你自己的循环时,就像在模拟中一样(我假设),你需要调用update
函数来完成主mainloop
function:用你的修改更新窗口,但是在你的循环中进行。
def task(): # do something root.update() while 1: task()
另一个select是让tkinter在单独的线程上执行。 一种做法是这样的:
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')
但要小心,multithreading编程是很难的,在脚下拍摄自己真的很容易。 例如,当您更改上面的示例类的成员variables时,您必须小心,因此不要使用Tkinter的事件循环中断。
这是GPS读取器和数据展示器的第一个工作版本。 tkinter是一个非常脆弱的东西与方式太less的错误消息。 它没有把东西放在一起,并没有说明为什么很多时候。 非常难从一个好的所见即所得的forms开发人员。 无论如何,这个小例程每秒运行10次,并将信息呈现在表单上。 花了一段时间才能做到这一点。 当我尝试一个计时器值为0时,表单从来没有出现。 我的头痛了! 每秒10次或更多次对我来说已经足够了。 我希望它可以帮助别人。 迈克·莫罗
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()