Главный недостаток первого подхода в том, что одновременно может происходить только одна анимация: если нажать клавишу R или O во время движения, новый запрос приостанавливает предыдущее перемещение до своего окончания, потому что каждый обработчик операции перемещения допускает только один поток управления при своей работе. То есть в каждый конкретный момент времени может выполняться только один цикл, использующий time.sleep, а новый вызов этой функции из метода update фактически является рекурсивным вызовом, который приостанавливает уже выполняющийся цикл.
Обновление экрана во время перемещений тоже происходит несколько замедленно, потому что производится, только когда метод update вызывается вручную (попробуйте вытянуть фигуру или перекрыть окно другим окном во время перемещения и вы в этом убедитесь сами). Фактически если закомментировать вызов метода update в примере 9.30, графический интерфейс вообще перестанет откликаться во время выполнения операций перемещения — он не будет перерисовываться при перекрытии другими окнами, не будет откликаться на действия пользователя и никакого эффекта анимации воспроизводиться не будет (по истечении времени вы просто увидите окно в заключительном состоянии). Это полноценная имитация влияния операций, выполняющихся продолжительное время, на графический интерфейс.
Пример 9.31 переопределяет метод moveInSquares, чтобы снять такие ограничения, — применяя метод after, он обеспечивает перемещение практически без пауз. Кроме того, он демонстрирует наиболее часто используемый (и, вероятно, лучший) способ обработки событий от таймера в графических интерфейсах на основе библиотеки tkinter. Разбиение задания на части вместо того чтобы выполнять его целиком, позволяет выполнить естественное распределение частей по времени и выполнять несколько заданий одновременно.
Пример 9.31. PP4E\Gui\Tour\canvasDraw_tags_after.py
аналогично, но с применением метода widget.after() вместо циклов time.sleep; поскольку это планируемые события, появляется возможность перемещать овалы и прямоугольники _одновременно_ и отпадает необходимость вызывать метод update для обновления графического интерфейса; движение станет беспорядочным, если еще раз нажать ‘o’ или ‘r’ в процессе воспроизведения анимации: одновременно начнут выполняться несколько операций перемещения;
from tkinter import *
import canvasDraw_tags
class CanvasEventsDemo(canvasDraw_tags.CanvasEventsDemo):
def moveEm(self, tag, moremoves):
(diffx, diffy), moremoves = moremoves[0], moremoves[1:] self.canvas.move(tag, diffx, diffy) if moremoves:
self.canvas.after(250, self.moveEm, tag, moremoves)
def moveInSquares(self, tag):
allmoves = [(+20, 0), (0, +20), («20, 0), (0, «20)] * 5 self.moveEm(tag, allmoves)
if __name__ == ‘__main__’:
CanvasEventsDemo() mainloop()
Эта версия наследует все изменения из предыдущей версии и при этом позволяет перемещать овалы и прямоугольники одновременно — нарисуйте несколько овалов и прямоугольников, а затем нажмите клавишу O и затем сразу клавишу R. Попробуйте нажать обе клавиши несколько раз — чем больше нажатий, тем интенсивнее движение, потому что генерируется много событий, перемещающих объекты из того места, в котором они находятся. Если во время перемещения нарисовать новую фигуру, она, как и раньше, начнет перемещаться немедленно.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011