Tkinter понимание mainloop
до сих пор я заканчивал свои программы Tkiter: tk.mainloop()
, или ничего не показать! См. пример:
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):
pass
ball = Ball(canvas, "red")
tk.mainloop()
однако, когда попробовал следующий шаг в этой программе (заставляя мяч двигаться по времени), книга, из которой я читаю, говорит сделать следующее. Измените функцию draw на:
def draw(self):
self.canvas.move(self.id, 0, -1)
и добавьте в мою программу следующий код:
while 1:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
но я заметил, что добавление этого блока кода, использовать tk.mainloop()
бесполезно, так как все появился бы даже без него!!!
в этот момент я должен упомянуть, что моя книга никогда не говорит о tk.mainloop()
(может быть, потому, что он использует Python 3), но я узнал об этом в интернете, так как мои программы не работали, копируя код книги!
поэтому я попытался сделать следующее, что не сработает!!!
while 1:
ball.draw()
tk.mainloop()
time.sleep(0.01)
что происходит? Что делает tk.mainloop()
? Что делает tk.update_idletasks()
и tk.update()
сделать и как это отличается от tk.mainloop()
? Должен ли я использовать над петлей?tk.mainloop()
? или оба в моих программах?
3 ответов
tk.mainloop()
блоки. Это означает, что выполнение python программа останавливается. Вы можете увидеть это, написав:
while 1:
ball.draw()
tk.mainloop()
print "hello" #NEW CODE
time.sleep(0.01)
вы никогда не увидите вывод из инструкции print. Поскольку петли нет, мяч не движется.
С другой стороны, методы update_idletasks()
и update()
здесь:
while True:
ball.draw()
tk.update_idletasks()
tk.update()
...не блокируйте; выполнение продолжается после завершения этих методов, поэтому цикл while выполняет снова и снова, что заставляет мяч двигаться.
бесконечный цикл, содержащий вызовы метода update_idletasks()
и update()
может выступать в качестве замены для вызова tk.mainloop()
. Обратите внимание, что весь цикл, можно сказать блок как tk.mainloop()
потому что ничего после цикла while не будет выполняться.
tk.mainloop()
не является заменой только для строк:
tk.update_idletasks()
tk.update()
а, tk.mainloop()
подменяет все время петля:
while True:
tk.update_idletasks()
tk.update()
ответ на комментарий:
здесь tcl docs говорят:
обновить idletasks
эта подкоманда обновления сбрасывает все текущие запланированные события простоя из очереди событий Tcl. События Idle используются для отсрочки обработки пока "больше нечего делать", с типичным случаем использования для они являются перерисовкой и пересчетом геометрии Tk. Откладывая пока Tk не простаивает, дорогостоящие операции перерисовки не выполняются, пока все из кластера событий (например, кнопка release, изменение текущее окно и т. д.) обрабатываются на уровне скрипта. Это делает ТК кажется, намного быстрее, но если вы в середине делать некоторые долго запуск обработки также может означать, что не обрабатываются события простоя в течение длительного времени. Вызывая idletasks обновления, перерисовывает из-за внутренних изменения состояния обрабатываются немедленно. (Перерисовывает из-за системы событий, например, будучи deiconified пользователем, нужно полное обновление обработанный.)
APN как описано в обновлении считается вредным, использование обновления для обработки перерисовки не обрабатываются idletasks обновления имеет много проблем. Joe Английский в компе.ленг.публикация tcl описывает альтернативу:
так update_idletasks()
вызывает некоторое подмножество событий для обработки, что update()
причины для обработки.
обновление ?айдлетаски?
команда update используется для приведения приложения "в актуальное состояние" по ввод цикла событий Tcl повторно до всех ожидающих событий (включая обратные вызовы idle) были обработаны.
если ключевое слово idletasks указано в качестве аргумента команды, тогда никакие новые события или ошибки не обрабатываются; только простоя обратные вызовы вызванный. Это вызывает операции, которые обычно откладываются, такие как отображение обновлений и вычислений макета окна, которые необходимо выполнить немедленно.
KBK (12 февраля 2000) -- мое личное мнение, что [обновление] команда не является одной из лучших практик, и программист хорошо советовал избегать этого. Я редко, если когда-либо видел использование [update], что невозможно более эффективно программировать другими средствами, как правило соответствующее использование обратных вызовов событий. Кстати, это предостережение применимо для всех команд Tcl (vwait и tkwait другие общие преступников), которые входят в цикл мероприятий рекурсивно, за исключением использование одного [vwait] на глобальном уровне для запуска цикла событий внутри оболочка, которая не запускает его автоматически.
наиболее распространенные цели, для которых я видел [обновление] рекомендуется: 1) сохранение GUI в живых, в то время как некоторые долгосрочные вычисления проведение. См. программу обратного отсчета для альтернативы. 2) ожидание настройки окна перед выполнением такие вещи, как управление геометрией на нем. Альтернативой является привязка к событиям, таким как как это уведомляет процесс геометрии окна. Видеть Центрирование окна для альтернативы.
что случилось с update? Есть несколько ответов. Во-первых, она, как правило, чтобы усложнить код окружающего GUI. Если вы работаете упражнения в программе обратного отсчета, вы получите представление о том, сколько проще это может быть, когда каждое событие обрабатывается по своему собственному обратному вызову. Во-вторых, это источник коварных Жуков. Общая проблема заключается в том, что выполнение [update] имеет почти неограниченные побочные эффекты; при возврате из [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()
проблема в том, что мяч.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's comment
tk.mainloop()
метод after () не блок (он фактически создает другой поток выполнения), поэтому выполнение продолжается в вашей программе python после вызова after (), что означает 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's comment.
root.mainloop()
while 1:
root.update()
... (очень!) примерно аналогично:
root.mainloop()
разница в том, что mainloop
является правильным способом кодирования, и бесконечный цикл тонко неверен. Однако я подозреваю, что в подавляющем большинстве случаев либо то, либо другое будет работать. Просто mainloop
является гораздо более чистым решением. Ведь, называя mainloop
по существу это под обложками:
while the_window_has_not_been_destroyed():
wait_until_the_event_queue_is_not_empty()
event = event_queue.pop()
event.handle()
... который, как видите, не сильно отличается от вашего собственного цикла while. Так зачем создайте свой собственный бесконечный цикл, когда у tkinter уже есть один, который вы можете использовать?
положите в самых простых терминах:всегда называю mainloop
как последняя логическая строка кода в вашей программе. Вот как Tkinter был разработан для использования.
Я использую шаблон дизайна MVC / MVA с несколькими типами "представлений". Один тип-это "GuiView", который является окном Tk. Я передаю ссылку на мой объект window, который делает такие вещи, как кнопки link для просмотра функций (которые также вызывает класс адаптера / контроллера).
для этого перед созданием объекта window необходимо завершить конструктор объекта view. После создания и отображения окна я хотел выполнить некоторые начальные задачи с помощью просмотр автоматически. Сначала я попытался сделать их post mainloop (), но это не сработало, потому что mainloop () заблокирован!
таким образом, я создал объект window и использовал tk.Update (), чтобы нарисовать его. Затем я начал выполнять свои начальные задания и, наконец, запустил mainloop.
import Tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None, view=None ):
tk.Frame.__init__( self, master )
self.view_ = view
""" Setup window linking it to the view... """
class GuiView( MyViewSuperClass ):
def open( self ):
self.tkRoot_ = tk.Tk()
self.window_ = Window( master=None, view=self )
self.window_.pack()
self.refresh()
self.onOpen()
self.tkRoot_.mainloop()
def onOpen( self ):
""" Do some initial tasks... """
def refresh( self ):
self.tkRoot_.update()