Ответ 1
tk.mainloop()
. Это означает, что выполнение вашей программы python приостанавливается. Вы можете это увидеть, написав:
while 1:
ball.draw()
tk.mainloop()
print "hello" #NEW CODE
time.sleep(0.01)
Вы никогда не увидите вывод из инструкции печати. Поскольку нет петли, мяч не перемещается.
С другой стороны, методы update_idletasks()
и update()
здесь:
while True:
ball.draw()
tk.update_idletasks()
tk.update()
... не блокировать; выполнение продолжается после завершения этих методов, поэтому цикл while выполняется снова и снова, что заставляет мяч перемещаться.
Бесконечный цикл, содержащий вызовы методов update_idletasks()
и update()
, может выступать в качестве замены для вызова tk.mainloop()
. Обратите внимание, что весь цикл while может быть заблокирован так же, как tk.mainloop()
, потому что ничто после цикла while не будет выполняться.
Однако tk.mainloop()
не заменяет только строки:
tk.update_idletasks()
tk.update()
Скорее, tk.mainloop()
является заменой целого цикла while:
while True:
tk.update_idletasks()
tk.update()
Ответ на комментарий:
Вот что tcl docs говорят:
Обновить idletasks
Эта подкоманда обновления очищает все текущие запланированные праздные события из очереди событий Tcl. Простаивающие события используются для отсрочки обработки пока "нет ничего другого", с типичным вариантом использования для они являются перепрограммированием Tk и пересчетами геометрии. Откладывая до тех пор, пока Tk не будет работать, дорогостоящие операции перерисовывания не будут выполняться до тех пор, пока все из кластера событий (например, отпускание кнопки, изменение текущее окно и т.д.) обрабатываются на уровне script. Это делает Tk кажутся намного быстрее, но если вы в середине делаете длинный он также может означать, что события бездействия не обрабатываются долгое время. Вызывая обновление idletasks, перерисовывает из-за внутренних изменения состояния обрабатываются немедленно. (Перерисовывается из-за системы события, например, будучи подтвержденными пользователем, необходимо полное обновление, которое должно быть обрабатывается.)
APN Как описано в "Обновлении", считается вредным, использование обновления для обработки перерисовки, не обрабатываемые обновлением idletasks, имеют много проблем. Джо Английский в публикации comp.lang.tcl описывается альтернатива:
Итак, update_idletasks()
вызывает обработку некоторого подмножества событий, которое update()
вызывает обработку.
Обновить? idletasks?
Команда update используется для приведения приложения в состояние "актуально" с помощью многократно вводить цикл событий Tcl, пока все ожидающие события события (включая незапланированные обратные вызовы).
Если ключевое слово idletasks указано в качестве аргумента команды, то никакие новые события или ошибки не обрабатываются; только холостые обратные вызовы вызывается. Это вызывает операции, которые обычно откладываются, например отображать обновления и расчеты компоновки окон, которые должны выполняться немедленно.
KBK (12 февраля 2000 г.) - Мое личное мнение заключается в том, что [обновление] команда не является одной из лучших практик, и программист хорошо посоветовал избежать этого. Я редко видел когда-либо использование [обновления], которое не может быть более эффективно запрограммирован другим способом, как правило, соответствующее использование обратных вызовов событий. Кстати, эта осторожность применяется ко всем командам Tcl (vwait и tkwait - другие общие преступников), которые вводят цикл событий рекурсивно, за исключением используя один [vwait] на глобальном уровне для запуска цикла событий внутри оболочку, которая не запускает ее автоматически.
Наиболее распространенными целями, для которых я видел [обновление], являются: 1) Сохранение GUI вживую, в то время как некоторый длительный расчет выполнения. См. Программу обратного отсчета для альтернативы. 2) Ожидание настройки окна перед тем, как делать управление геометрией на нем. Альтернативой является привязка к таким событиям так как они уведомляют процесс геометрии окна. Видеть Центрирование окна для альтернативы.
Что случилось с обновлением? Есть несколько ответов. Во-первых, он имеет тенденцию чтобы усложнить код окружающего GUI. Если вы работаете упражнения в программе "Обратный отсчет", вы почувствуете, насколько проще это может быть, когда каждое событие обрабатывается по собственному обратному вызову. Во-вторых, это источник коварных ошибок. Общая проблема заключается в том, что выполнение [обновление] имеет почти неограниченные побочные эффекты; по возвращении от [обновления], script может легко обнаружить, что ковер был вытащил из-под нее. Дальнейшее обсуждение этого вопроса феномен над Update считается опасным.
.....
Есть ли вероятность, что моя программа может работать без цикла while?
Да, но все становится немного сложнее. Вы могли бы подумать над чем-то вроде следующего:
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
def draw(self):
while True:
self.canvas.move(self.id, 0, -1)
ball = Ball(canvas, "red")
ball.draw()
tk.mainloop()
Проблема заключается в том, что ball.draw() заставит выполнение вводить бесконечный цикл в методе draw(), поэтому tk.mainloop() никогда не будет выполняться, и ваши виджеты никогда не будут отображаться. В программировании gui необходимо избегать бесконечных циклов, чтобы сохранить виджеты, реагирующие на ввод пользователя, например. щелчки мыши.
Итак, возникает вопрос: как вы выполняете что-то снова и снова, не создавая бесконечного цикла? У Tkinter есть ответ на эту проблему: метод виджета after()
:
from Tkinter import *
import random
import time
tk = Tk()
tk.title = "Game"
tk.resizable(0,0)
tk.wm_attributes("-topmost", 1)
canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
def draw(self):
self.canvas.move(self.id, 0, -1)
self.canvas.after(1, self.draw) #(time_delay, method_to_execute)
ball = Ball(canvas, "red")
ball.draw() #Changed per Bryan Oakley comment
tk.mainloop()
Метод after() не блокирует (он фактически создает другой поток выполнения), поэтому выполнение продолжается в вашей программе python после вызова(), что означает, что tk.mainloop() выполняется следующим образом, поэтому ваши виджеты настроить и отобразить. Метод after() также позволяет вашим виджетам оставаться отзывчивыми к другим пользовательским вводам. Попробуйте запустить следующую программу, а затем щелкните мышью на разных местах на холсте:
from Tkinter import *
import random
import time
root = Tk()
root.title = "Game"
root.resizable(0,0)
root.wm_attributes("-topmost", 1)
canvas = Canvas(root, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
self.canvas.bind("<Button-1>", self.canvas_onclick)
self.text_id = self.canvas.create_text(300, 200, anchor='se')
self.canvas.itemconfig(self.text_id, text='hello')
def canvas_onclick(self, event):
self.canvas.itemconfig(
self.text_id,
text="You clicked at ({}, {})".format(event.x, event.y)
)
def draw(self):
self.canvas.move(self.id, 0, -1)
self.canvas.after(50, self.draw)
ball = Ball(canvas, "red")
ball.draw() #Changed per Bryan Oakley comment.
root.mainloop()