Tkinter:Python 中 canvas 框架上的矩形中的按钮 3。
Tkinter: Button in a rectangle on a canvas of frame in Python 3.
我一直在尝试创建一个更像按钮的移动椭圆。到目前为止,我不打算在单击按钮时执行任何功能,因此我创建了一个传递函数。
场景如下:
tkinter window 中的框架在指定位置被品红色的 canvas 和圆圈(黄色)覆盖。这个圆圈实际上是一个按钮,按下它会打开一个弹出菜单,显示一些信息(现在不是必需的)。我设法创建了圆圈,但努力将按钮合并到椭圆形中。按钮和圆圈必须在一起,因为这对应该每 3 秒在 canvas 帧中移动一次(听起来像 GPS 点,但点是按钮)。
但是当我尝试创建按钮时,canvas 消失了,框架根据按钮的宽度调整大小。
请帮助我找出错误和相应的正确代码:
enter code here
from tkinter import *
import random
import time
def nothing():
pass
main = Tk()
frame_1 = Frame(main)
frame_1.grid(row=0, column=0)
main_canvas = Canvas(frame_1, width=200, height=200, bg='magenta')
oval = main_canvas.create_oval(20, 20, 40, 40, outline='black', fill='yellow')
main_canvas.pack()
frame_2 = Frame(main)
frame_2.grid(row=0, column=1)
'''
button2 = Button(main_canvas, text="Q", command=nothing, anchor=W)
button2.configure(width=3, activebackground="#33B5E5", relief=FLAT)
button2_window = main_canvas.create_window(10, 10, anchor=NW, window=button2)
button2.pack(side=TOP)
'''
label_f2_1 = Label(frame_2, text="")
label_f2_1.pack()
label_f2_2 = Label(frame_2, text="")
label_f2_2.pack()
x_current, y_current = 30, 30
for loops in range(86400):
x_new = random.randint(10, 190)
y_new = random.randint(10, 190)
main_canvas.move(oval, x_new-x_current, y_new-y_current)
x_current, y_current = x_new, y_new
main_canvas.update()
time.sleep(1)
now = str(time.ctime())
label_f2_2.configure(text=now[10:])
label_f2_1.configure(text=now[0:10])
# print(time.localtime())
main.mainloop()
在 Python 3.7 解释器上使用 PyEdu 运行。
canvas 消失了,因为那是 pack
的行为——它会导致小部件扩大或缩小以适应其子项。该按钮是 canvas 的子按钮,因此 canvas 会尝试缩小以适应 canvas。
此解决方案在很多方面都是错误的,例如,要将小部件添加到 canvas,您不应该使用 pack
、place
或 grid
.相反,您应该使用 canvas 方法 create_window
.
但是,您根本不需要椭圆内的按钮。您可以创建一个绑定,只要用户直接点击椭圆,该绑定就会调用一个函数。
例如,以下将导致函数 showInfo
在单击椭圆时被调用:
def showInfo(event):
print("you clicked on the oval")
main_canvas.tag_bind(oval, "<1>", showInfo)
但是,由于您制作动画循环的方式,这不会很好地工作。您的代码比其他任何东西都更容易休眠,这使得 tkinter 很难处理事件。 sleep
正在休眠时,Tkinter 无法处理事件。
而不是使用 for
循环,您需要编写一个函数来完成您在循环内所做的所有事情(即:它绘制一帧动画)。这个函数以后可以用after
再次调用自己,建立一个永久循环。
例如:
def doOneIteration():
newx = random.randint(10, 190)
newy = random.randint(10, 190)
main_canvas.coords(oval, newx, newy, newx+20, newy+20)
now = str(time.ctime())
label_f2_2.configure(text=now[10:])
label_f2_1.configure(text=now[0:10])
main.after(1000, doOneIteration)
doOneIteration()
第一次调用 doOneIteration
时,它将执行循环体中的所有操作。然后,当它完成时,它会导致自己在一秒钟内再次被调用。下次调用它时,它将完成工作,然后在一秒钟内再次调用它自己。只要程序运行,它就会执行此操作。
这里的优点是您绝不会导致程序进入休眠状态。这确保 tkinter 能够不间断地处理稳定的事件流。
我一直在尝试创建一个更像按钮的移动椭圆。到目前为止,我不打算在单击按钮时执行任何功能,因此我创建了一个传递函数。
场景如下: tkinter window 中的框架在指定位置被品红色的 canvas 和圆圈(黄色)覆盖。这个圆圈实际上是一个按钮,按下它会打开一个弹出菜单,显示一些信息(现在不是必需的)。我设法创建了圆圈,但努力将按钮合并到椭圆形中。按钮和圆圈必须在一起,因为这对应该每 3 秒在 canvas 帧中移动一次(听起来像 GPS 点,但点是按钮)。
但是当我尝试创建按钮时,canvas 消失了,框架根据按钮的宽度调整大小。 请帮助我找出错误和相应的正确代码:
enter code here
from tkinter import *
import random
import time
def nothing():
pass
main = Tk()
frame_1 = Frame(main)
frame_1.grid(row=0, column=0)
main_canvas = Canvas(frame_1, width=200, height=200, bg='magenta')
oval = main_canvas.create_oval(20, 20, 40, 40, outline='black', fill='yellow')
main_canvas.pack()
frame_2 = Frame(main)
frame_2.grid(row=0, column=1)
'''
button2 = Button(main_canvas, text="Q", command=nothing, anchor=W)
button2.configure(width=3, activebackground="#33B5E5", relief=FLAT)
button2_window = main_canvas.create_window(10, 10, anchor=NW, window=button2)
button2.pack(side=TOP)
'''
label_f2_1 = Label(frame_2, text="")
label_f2_1.pack()
label_f2_2 = Label(frame_2, text="")
label_f2_2.pack()
x_current, y_current = 30, 30
for loops in range(86400):
x_new = random.randint(10, 190)
y_new = random.randint(10, 190)
main_canvas.move(oval, x_new-x_current, y_new-y_current)
x_current, y_current = x_new, y_new
main_canvas.update()
time.sleep(1)
now = str(time.ctime())
label_f2_2.configure(text=now[10:])
label_f2_1.configure(text=now[0:10])
# print(time.localtime())
main.mainloop()
在 Python 3.7 解释器上使用 PyEdu 运行。
canvas 消失了,因为那是 pack
的行为——它会导致小部件扩大或缩小以适应其子项。该按钮是 canvas 的子按钮,因此 canvas 会尝试缩小以适应 canvas。
此解决方案在很多方面都是错误的,例如,要将小部件添加到 canvas,您不应该使用 pack
、place
或 grid
.相反,您应该使用 canvas 方法 create_window
.
但是,您根本不需要椭圆内的按钮。您可以创建一个绑定,只要用户直接点击椭圆,该绑定就会调用一个函数。
例如,以下将导致函数 showInfo
在单击椭圆时被调用:
def showInfo(event):
print("you clicked on the oval")
main_canvas.tag_bind(oval, "<1>", showInfo)
但是,由于您制作动画循环的方式,这不会很好地工作。您的代码比其他任何东西都更容易休眠,这使得 tkinter 很难处理事件。 sleep
正在休眠时,Tkinter 无法处理事件。
而不是使用 for
循环,您需要编写一个函数来完成您在循环内所做的所有事情(即:它绘制一帧动画)。这个函数以后可以用after
再次调用自己,建立一个永久循环。
例如:
def doOneIteration():
newx = random.randint(10, 190)
newy = random.randint(10, 190)
main_canvas.coords(oval, newx, newy, newx+20, newy+20)
now = str(time.ctime())
label_f2_2.configure(text=now[10:])
label_f2_1.configure(text=now[0:10])
main.after(1000, doOneIteration)
doOneIteration()
第一次调用 doOneIteration
时,它将执行循环体中的所有操作。然后,当它完成时,它会导致自己在一秒钟内再次被调用。下次调用它时,它将完成工作,然后在一秒钟内再次调用它自己。只要程序运行,它就会执行此操作。
这里的优点是您绝不会导致程序进入休眠状态。这确保 tkinter 能够不间断地处理稳定的事件流。