0

我想在画布上制作一个带有自定义移动小部件的 python tkinter 窗口来模拟运动。现在,我有一个画布和一个不动的椭圆形小部件。我在基层遇到问题;主循环()。我知道它在等待用户做某事时运行,但我很难看到:

  1. 如何控制/查看 mainloop() 正在重复的代码(在哪里,并且只有 tkinter?);

  2. 如果它自己不这样做,如何正确地中断它并从另一个函数返回它;

  3. 应该重申什么代码?所有 tkinter 对象,还是仅更新更改的对象?改用某种更新操作?最后;

  4. tkinter.mainloop() 和 window.mainloop() 之间的功能区别是什么?也许前面的问题会回答。

我对 Swift 有一点经验,昨天晚上开始学习非常相似的 Python。我已经对我的代码尝试了可能数百种突变,目前处于测试阶段。我已经将所有内容移入和移出主循环的明显范围,甚至在屏幕上显示了数百个微小的 Python 窗口。一切都做两件事之一:什么都不做,或者给我一个错误。由于我什至不知道什么正在运行,或者它是否正在运行,所以我无法诊断任何东西。我的目标只是重复移动一个一百像素的圆圈。我已经四处寻找资源,但是——可能是我——一个明确的资源很少。我把我的代码都标记好了。此页面最接近我正在寻找的内容:在 Tkinter Canvas Widget 内移动球(简单的打砖块游戏). 一切似乎都在主循环之下。那么,每一次都重绘一切吗?不幸的是,这是我的整个剧本;我不能只展示碎片。由于某种原因,它只打开一个小窗口,而不是全屏窗口。(编辑:我似乎丢失了屏幕尺寸代码)

import tkinter
import time

# Initial values for circle's corners and start idicator ('b'):
x1 = 10
y1 = 10
x2 = 210
y2 = 210

b = 0

# Window ('window')
window = tkinter.Tk()

# Canvas ('area')
area = tkinter.Canvas(window, width=1368, height=650)
area.place(x=0, y=0)


# Ovals to be placed on 'area'
oval1 = area.create_oval(x1,y1,x2,y2,fill='#42befe')
oval2 = area.create_oval(100,10,300,210,fill='#d00000')

# Turns b to 1 to start shifting when 'butt' is pressed:
def startFunc():
    b = 1
    print('b = 1')

# My button to activate 'startFunc'  
butt = tkinter.Button(window, text='Start movement', command=startFunc)
butt.pack()

# Adjusts the x and y coordinates when they are fed in:
def Shift(A, B, C, D):
    print('Shift activated.')
    window.after(1000)
    print('Edit and return:')
    A += 100
    B += 100
    C += 100
    D += 100
    return(A, B, C, D)


# Problems start about here: my Mainloop section;
# I have little idea how this is supposed to be.
while True:

    if b == 1:
        # Takes adjusted tuple
        n = Shift(x1, y1, x2, y2)
        print('Returned edited tuple.')

        # Changes coordinates
        x1 = n[0]
        y1 = n[1]
        x2 = n[2]
        y2 = n[3]
        print(f'{x1}, {y1}, {x2}, and {y2}')

        # Reiterate moving oval
    oval1 = area.create_oval(x1,y1,x2,y2,fill='#42befe')

    #Does this re-run 'window' relations outside here, or only within the 'while'?
    window.mainloop()

它应该显示一个 1368 x 650 的窗口,而不是一个很小的窗口。该按钮除了打印什么都不做,这意味着尽管有主循环,但最后的“while”没有运行。它希望它在“while”行内循环,这应该调整坐标并移动我的蓝色圆圈。迭代可能不会触及初始值,否则会重置它们。

4

1 回答 1

1

实际上,调用 mainloop 与将其添加到代码中而不是调用相同mainloop()

while the_program_is_running():
    event = wait_for_event()
    process_the_event(event)

根据经验,mainloop()应该在 UI 初始化并且您准备好让用户开始与您的程序交互之后调用一次。当它退出时,您通常不会有任何代码,并且您的程序将退出。


如何控制/查看 mainloop() 正在重复的代码(在哪里,并且只有 tkinter?);

我不知道你所说的“重申”是什么意思。它不运行任何代码,除了它自己的内部代码。它只是等待事件,然后将它们分派给处理程序。

如果它自己不这样做,如何正确地中断它并从另一个函数返回它;

在正在运行的程序中执行此操作极为罕见。通常,调用mainloop是您的程序在用户开始与之交互之前所做的最后一件事,并且一旦它退出您的程序就会退出。

但是,要回答如何中断它的具体答案,您可以调用quit根窗口的方法。这将导致最近的调用mainloop()返回。

应该重申什么代码?所有 tkinter 对象,还是仅更新更改的对象?改用某种更新操作?

这个问题很难回答,因为它没有多大意义。当您调用mainloop()时,它将监视所有 tkinter 对象上的所有事件。

tkinter.mainloop() 和 window.mainloop() 之间的功能区别是什么

它们具有完全相同的效果和行为。Tkinter 奇怪地选择mainloop从任何小部件中提供。调用它的最常见方式是从 tkinter 模块本身或从根窗口。

我的目标只是重复移动一个一百像素的圆圈。

执行此操作的正常方法是创建一个将其移动 100 个像素的函数。然后,该函数(或调用它的函数)可以将自己置于事件队列中以供将来运行。

例如,以下代码将每秒将画布对象移动 100 像素,直到程序退出:

def move_object():
    the_canvas.move(item_id, 100, 0)
    the_canvas.after(1000, move_object)

当它被调用时,它会将项目向右移动 100 像素。然后,它将在大约 1000 毫秒内被拾取和处理的事件队列中对自身进行新的调用。

在本网站上有许多使用示例after,包括您在问题中链接到的问题。

一切似乎都在主循环之下。那么,每一次都重绘一切吗?

不,不完全是。唯一重绘的对象是需要重绘的东西。在画布上移动对象、调整窗口大小、在窗口上拖动另一个窗口等,都会在事件队列上放置一个事件,告诉 tkinter“这个对象需要重绘”。该事件的处理由 自动发生mainloop。如果您的应用程序中没有发生任何事情,则主循环不会重绘任何内容。

它应该显示一个 1368 x 650 的窗口,而不是一个很小的窗口

那是因为你没有给主窗口一个大小。您已经为画布指定了大小,但您使用的大小place不会导致包含窗口增大或缩小以适应。作为初学者,您应该完全避免place使用packor grid,因为packgrid都会自动调整窗口大小以适应里面的所有内容。

虽然使用place它的简单性很有吸引力,但实际上,与使用其他几何管理器之一相比,它通常需要您做更多的工作,并且它会导致 GUI 对变化没有特别响应。

while True:

你几乎不应该在 tkinter 中这样做。Tkinter——以及几乎所有基于事件的程序——都依赖于稳定的事件流。当您有无限循环时,它无法处理这些事件。您可以在循环中进行显式调用以更新屏幕,但这是低效的,应该避免。如果您需要定期执行某些操作,请创建一个封装循环主体的函数,然后在处理事件时使用aftergetmainloop来运行它。

window.after(1000)

如果没有第二个参数,您几乎不应该使用after这种方式。这种用法在功能上与调用没有什么不同time.sleep(1),因为它阻止mainloop处理事件。您应该构建您的代码以允许稳定的事件流由mainloop.

while True: ... window.mainloop()

绝对需要避免mainloop在循环内调用。一个表现良好的 tkinter 程序应该只调用mainloop()一次。

于 2019-01-27T07:14:48.870 回答