【问题标题】:Tkinter: Button in a rectangle on a canvas of frame in Python 3.Tkinter:Python 3 框架画布上矩形中的按钮。
【发布时间】:2018-07-09 18:12:38
【问题描述】:

我一直在尝试创建一个更像按钮的移动椭圆形。到目前为止,我不打算在单击按钮时执行任何功能,因此我创建了一个 pass 功能。

场景如下: tkinter 窗口中的框架在指定位置被洋红色画布覆盖,并带有一个圆圈(黄色)。这个圆圈实际上是一个按钮,按下它会打开一个弹出菜单,显示一些信息(现在不是必需的)。我设法创建了圆圈,但努力将按钮合并到椭圆中。按钮和圆圈必须在一起,因为这对应该每 3 秒在画布框架中移动一次(听起来像 GPS 点,但点是按钮)。

但是当我尝试创建按钮时,画布消失了,并且框架根据按钮的宽度调整大小。 请帮助我相应地识别错误和正确的代码:

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。

【问题讨论】:

  • 是否有理由在椭圆内放置一个按钮,而不是让用户直接点击椭圆?
  • @BryanOakley 是的,当我单击椭圆时,会弹出一个消息框,显示分配给该椭圆的名称/标签。到目前为止,我相信这可以使用椭圆形内的按钮来实现。但如果你能提供任何其他选择,我会很高兴

标签: python button tkinter tkinter-canvas


【解决方案1】:

画布消失了,因为这是pack 的行为——它会导致小部件增长或缩小以适应其子级。该按钮是画布的子按钮,因此画布会尝试缩小以适合画布。

此解决方案在许多方面都是错误的,例如,将小部件添加到您不应该使用 packplacegrid 的画布。相反,您应该使用画布方法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 能够不间断地处理稳定的事件流。

【讨论】:

  • 您建议将鼠标悬停在椭圆形声音上作为更好的选择,但是,我的巢穴挑战是在较大的画布上有 n(比如 10 个)这样的椭圆形,并且每个椭圆形都使用标记/命名一个列表变量。单击时如何识别椭圆形。这一次,椭圆形的消息/名称必须在屏幕上以较小的窗口弹出,而无需单击(就像您将鼠标放在 google chrome 中的选项卡上时能够看到一个小的悬停文本一样)
  • next* , 点击它* ...抱歉打错了
  • @GauravKamila:这是一个单独的问题,虽然这个网站上可能已经有类似的问题。
猜你喜欢
  • 2020-10-13
  • 2012-06-09
  • 1970-01-01
  • 2016-12-03
  • 1970-01-01
  • 1970-01-01
  • 2012-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多