我的小弟弟正在进入编程,而在他的科学博览会项目中,他正在模拟天空中的一群鸟.他已经完成了大部分代码编写工作,并且工作得很好,但是鸟类需要每时每刻都在移动.
然而,Tkinter占用了自己的事件循环的时间,所以他的代码不会运行.做root.mainloop()运行,运行和继续运行,它运行的唯一事情是事件处理程序.
有没有办法让他的代码与mainloop一起运行(没有多线程,这很混乱,这应该保持简单),如果是这样,它是什么?
现在,他想出了一个丑陋的黑客,把他的move()功能绑在一起<b1-motion>,所以只要他按住按钮并摆动鼠标,就可以了.但必须有一个更好的方法.
每个tkinter教程我都看到了tkinter.mainloop必须要求绘制窗口和要处理的事件的声明,并且它们总是调用此函数,即使在hello world程序中也是如此.但是,当我在交互式shell中尝试这些时,无需调用mainloop即可正确绘制窗口. 这个在tkinter中嵌入matplotlib图形的例子产生了一个相对复杂的应用程序,带有用于在tkinter窗口内平移,缩放和调整绘图大小的按钮,再次,如果你删除对mainloop的调用并在交互式shell中运行代码,这一切都有效. .当然,如果我在交互式shell之外运行脚本(删除了mainloop),程序结束得太快就看不到会发生什么,但如果我添加一个调用input 保持程序打开一切正常(我在Linux上运行python 3.2.2).
那么mainloop到底做了什么,什么时候需要调用呢?
编辑:澄清一下,如果我打开GNOME终端并输入
$python3
>>> import tkinter
>>> root = tkinter.Tk()
Run Code Online (Sandbox Code Playgroud)
一个窗口立即出现,而不必调用mainloop,更复杂的tkinter功能似乎也可以工作(例如,向窗口添加按钮).在IDLE中,需要调用mainloop.我的理解是,在调用mainloop之前,不应绘制任何内容,也不应处理任何事件.
从effbot.org文档中,我们有关于该update功能的以下内容:
处理所有挂起事件,调用事件回调,完成任何挂起的几何管理,根据需要重绘小部件,以及调用所有挂起的空闲任务.应谨慎使用此方法,因为如果从错误的位置(例如,从事件回调中调用,或者从可以以任何方式从事件回调调用的函数等)调用它,可能会导致非常讨厌的竞争条件).如有疑问,请
update_idletasks改用.
另一方面,这是关于update_idletasks功能:
调用所有待处理的空闲任务,而不处理任何其他事件.这可用于在必要时执行几何管理和重绘小部件,而无需调用任何回调.
据我所知,两者都调用所有挂起的空闲任务,完成任何挂起的几何管理并根据需要重绘小部件.我看到的唯一区别是update 处理所有挂起事件并调用事件回调.update我想这就是为什么我们不应该在一个甚至回调中打电话.
然而,我所看到的例子,其中update_idletasks并update使用一个接一个,我不能明白其中的道理,因为从理论上讲update做的一切update_idletasks一样.
这些待处理事件究竟是什么以及文档正在讨论的空闲任务?有什么区别和关系?
话虽这么回答,在我应该使用什么是真正的情况update了update_idletasks?具体例子也是值得赞赏的.
这是一个非常基本的程序,我想制作两个移动球,但其中只有一个实际移动.
我也尝试了一些变化但是不能让第二个球移动; 另一个相关的问题 - 有些人使用这种move(object)方法来实现这个目标,而其他人则做一个delete(object)然后重新绘制它.我应该使用哪一个?为什么?
这是我的代码,它只是动画/移动一个球:
from Tkinter import *
class Ball:
def __init__(self, canvas, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.canvas = canvas
self.ball = canvas.create_oval(self.x1, self.y1, self.x2, self.y2, fill="red")
def move_ball(self):
while True:
self.canvas.move(self.ball, 2, 1)
self.canvas.after(20)
self.canvas.update()
# initialize root Window and canvas
root = Tk()
root.title("Balls")
root.resizable(False,False)
canvas = Canvas(root, width = 300, height = 300)
canvas.pack()
# create two ball objects …Run Code Online (Sandbox Code Playgroud) Ctrl-C/SIGTERM/SIGINT 似乎被 tkinter 忽略。通常可以通过回调再次捕获它。这似乎不起作用,所以我想我应该在另一个线程中运行 tkinter ,因为它的mainloop() 是一个无限循环和 block。实际上我也想这样做以在单独的线程中从标准输入读取。即使在此之后,Ctrl-C 仍然不会被处理,直到我关闭窗口。这是我的 MWE:
#! /usr/bin/env python
import Tkinter as tk
import threading
import signal
import sys
class MyTkApp(threading.Thread):
def run(self):
self.root = tk.Tk()
self.root.mainloop()
app = MyTkApp()
app.start()
def signal_handler(signal, frame):
sys.stderr.write("Exiting...\n")
# think only one of these is needed, not sure
app.root.destroy()
app.root.quit()
signal.signal(signal.SIGINT, signal_handler)
Run Code Online (Sandbox Code Playgroud)
结果:
这是怎么回事?如何让终端中的 Ctrl-C 关闭应用程序?
更新:按照建议添加 poll,在主线程中工作,但在另一个线程中启动时没有帮助......
class MyTkApp(threading.Thread):
def poll(self):
sys.stderr.write("poll\n")
self.root.after(50, self.poll) …Run Code Online (Sandbox Code Playgroud) 我开发了一个简单的Python应用程序,然后我决定使用Tkinter添加一个简单的GUI.
问题是,虽然主要功能正在做它的东西,窗口冻结.
我知道这是一个常见问题,我已经读过我应该使用多线程(非常复杂,因为函数也更新了GUI)或者将我的代码分成不同的函数,每个函数都工作了一段时间.无论如何,我不想为这样一个愚蠢的应用程序更改我的代码.
我的问题是:是否有可能没有简单的方法来每秒更新我的Tkinter窗口?我只想申请KISS规则!
我会在下面给你一个伪代码示例,但我没试过但是没有用:
class Gui:
[...]#costructor and other stuff
def refresh(self):
self.root.update()
self.root.after(1000,self.refresh)
def start(self):
self.refresh()
doingALotOfStuff()
#outside
GUI = Gui(Tk())
GUI.mainloop()
Run Code Online (Sandbox Code Playgroud)
它只会执行一次刷新,我无法理解为什么.
非常感谢你的帮助.
我正在运行这段代码,它应该创建一个窗口,但它没有在 Pycharm 中创建任何窗口。我正在使用带有 Python 3.6 的 Pycharm 社区版。当我在 IDLE 中运行此代码时,会生成该窗口。
import tkinter
from datetime import date, datetime
root = tkinter.Tk()
c = tkinter.Canvas(root,width =800, height =768, bg = 'black')
c.pack()
c.create_text(100,50, anchor = 'w', fill = 'orange', \
font = 'Arial 28 bold underline', text = 'My Countdown calendar')
Run Code Online (Sandbox Code Playgroud)