Различия между версиями 1 и 2
Версия 1 от 2019-04-04 17:57:07
Размер: 1422
Редактор: FrBrGeorge
Комментарий:
Версия 2 от 2019-04-05 15:11:38
Размер: 6894
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 3: Строка 3:
Таймер: два подхода

Однопоточный:

{{{#!python
Таймер в эмуляции событийного подхода через `mainloop()` на самом деле не так просто сделать.
 * «Однопоточный» вариант. Tkinter умеет выполнить callback в указанное время (через указанный интервал). Для того, чтобы он запускался ''всё время'', в сам этот callback вставляется планировщик его следующего вызова:
 {{{#!python
Строка 15: Строка 13:
        self.label.pack()         self.label.grid()
Строка 26: Строка 24:
 * Внешний вариант. Мы можем использовать любой фреймфорк, в котором есть часики, и дёргать callback-и оттуда.
  * Пример для `threading`:
  {{{#!python
from threading import Thread, Event
Строка 27: Строка 29:
Многопоточный:
{{{#!python
Строка 37: Строка 37:
            # call a function
Строка 42: Строка 41:
# this will stop the timer input("А вы пока нажмите Enter\n")
Строка 44: Строка 43:
print("Тогда всё")
Строка 45: Строка 45:
  * Совместим `threading` и `tkinter`:
  {{{#!python
import tkinter as tk
import time
from threading import Thread, Event
Строка 46: Строка 51:
С порождение синтетического события:
https://stackoverflow.com/questions/270648/tkinter-invoke-event-in-main-loop
{{{#!python
class MyThread(Thread):
    def __init__(self, event, root):
        Thread.__init__(self)
        self.stopped = event
        self.root = root

    def run(self):
        while not self.stopped.wait(1):
            self.root.update_clock()

class App():
    def __init__(self):
        self.root = tk.Tk()
        self.label = tk.Label(text="")
        self.label.grid()
        self.button = tk.Button(text="Quit", command = self.delayed_quit)
        self.button.grid()
        self.update_clock()
        self.stopped = Event()
        self.thread = MyThread(self.stopped, self)
        self.thread.start()
        self.root.mainloop()

    def delayed_quit(self):
        self.stopped.set()
        self.root.after(3000, self.root.quit)
        self.label.configure(text="Wait 3 secs")

    def update_clock(self):
        now = time.strftime("%H:%M:%S")
        self.label.configure(text=now)

app=App()
  }}}
  Недостаток этого способа — в том, что мы вынуждены работать в ''двух'' моделях событий — tkiner-овской и thread-овой.
 * «Правильный» подход: все события должны быть tkinter-овскими, таймер их только генерирует и подбрасывает в очередь событий:
 {{{#!python
import time
Строка 50: Строка 90:
from threading import Thread, Event;
Строка 51: Строка 92:
def doFoo(*args):
    print("Hello, world")
class Clock(Thread):
    def __init__(self, root, grain=1, event="<<Tick>>"):
        super().__init__()
        self.root = root # Окно, которому посылать событие
        self.grain = grain # Размер одного тика в секундах (м. б дробный)
        self.event = event # TKinter-событие, которое надо посылать
        self.done = Event() # threading-событие, которое останавливет тред
Строка 54: Строка 100:
root = Tk()
root.bind("<<Foo>>", doFoo)
    def run(self):
        while not self.done.wait(self.grain):
            self.root.event_generate(self.event)
Строка 57: Строка 104:
# some time later, inject the "<<Foo>>" virtual event at the
# tail of the event queue
root.event_generate("<<Foo>>", when="tail")
}}}
class App(Frame):
    def __init__(self, master=None, **kwargs):
        Frame.__init__(self, master, **kwargs)
        self.grid()
        self.Clock = Clock(self)
        self.Time = StringVar()
        self.update_clock()
        self.Screen = Label(textvariable=self.Time)
        self.Screen.grid(row=0, column=0)
        self.Start = Button(text="Start", command=self.start)
        self.Start.grid(row=0, column=1)
        self.Quit = Button(text="Quit", command=self.quit)
        self.Quit.grid(row=0, column=2)
        self.bind(self.Clock.event, self.tick)
        # тред надо остановить, даже если окно просто закрыли
        self.bind("<Destroy>", self.quit)

    def tick(self, event):
        self.update_clock()

    def start(self):
        self.Clock.start()

    def quit(self, *events):
        self.Clock.done.set()
        self.master.quit()

    def update_clock(self):
        self.Time.set(time.strftime("%H:%M:%S"))

Tick = App()
Tick.mainloop()
 }}}
 Здесь используется т. н. ''синтетическое'' событие (в терминах `tkinter` — ''virtual'' event), которое мы называем сами (это как `class MyEvent(Event):` в Python)
Строка 63: Строка 140:
'''TODO'''
Строка 67: Строка 145:
Про ДЗ == ДЗ ==
'''TODO'''

Я в ближайшие пару дней буду делать proposal относительно семестровой работы по курсу. Если коротко:
Работа — это git-репозиторий с кодом на Python3,
 * который я могу склонировать и запустить
 * в котором есть более одного участника, и я могу посмотреть статистику участия
 * в котором есть немножко тестов (с использованием любого тест-фреймворка, годится встроенный питоний)
 *в котором есть немножко документации
  * программной (с использованием любого фреймворка, годится встроенный питоний, но можно и sphinx)
  * пользовательской (либо sphinx, либо прямо на GH)
  * описание проекта и постановка задачи на GH
 * в котором есть немножко локализации (с теми же оговорками)

Немножко — это реально немножко, чтобы я видел, что работа проделана. Например, если вы задумали какое-то приложение из реал пайфа, и в нём довольно много логики, обмазать ''всю'' её тестами будет долго. Но пяток должен быть.

Таймеры, события и GitHub

Таймер в эмуляции событийного подхода через mainloop() на самом деле не так просто сделать.

  • «Однопоточный» вариант. Tkinter умеет выполнить callback в указанное время (через указанный интервал). Для того, чтобы он запускался всё время, в сам этот callback вставляется планировщик его следующего вызова:

       1 import tkinter as tk
       2 import time
       3 
       4 class App():
       5     def __init__(self):
       6         self.root = tk.Tk()
       7         self.label = tk.Label(text="")
       8         self.label.grid()
       9         self.update_clock()
      10         self.root.mainloop()
      11 
      12     def update_clock(self):
      13         now = time.strftime("%H:%M:%S")
      14         self.label.configure(text=now)
      15         self.root.after(1000, self.update_clock)
      16 
      17 app=App()
    
  • Внешний вариант. Мы можем использовать любой фреймфорк, в котором есть часики, и дёргать callback-и оттуда.
    • Пример для threading:

         1 from threading import Thread, Event
         2 
         3 class MyThread(Thread):
         4     def __init__(self, event):
         5         Thread.__init__(self)
         6         self.stopped = event
         7 
         8     def run(self):
         9         while not self.stopped.wait(0.5):
        10             print("my thread")
        11 
        12 stopFlag = Event()
        13 thread = MyThread(stopFlag)
        14 thread.start()
        15 input("А вы пока нажмите Enter\n")
        16 stopFlag.set()
        17 print("Тогда всё")
      
    • Совместим threading и tkinter:

         1 import tkinter as tk
         2 import time
         3 from threading import Thread, Event
         4 
         5 class MyThread(Thread):
         6     def __init__(self, event, root):
         7         Thread.__init__(self)
         8         self.stopped = event
         9         self.root = root
        10 
        11     def run(self):
        12         while not self.stopped.wait(1):
        13             self.root.update_clock()
        14 
        15 class App():
        16     def __init__(self):
        17         self.root = tk.Tk()
        18         self.label = tk.Label(text="")
        19         self.label.grid()
        20         self.button = tk.Button(text="Quit", command = self.delayed_quit)
        21         self.button.grid()
        22         self.update_clock()
        23         self.stopped = Event()
        24         self.thread = MyThread(self.stopped, self)
        25         self.thread.start()
        26         self.root.mainloop()
        27 
        28     def delayed_quit(self):
        29         self.stopped.set()
        30         self.root.after(3000, self.root.quit)
        31         self.label.configure(text="Wait 3 secs")
        32 
        33     def update_clock(self):
        34         now = time.strftime("%H:%M:%S")
        35         self.label.configure(text=now)
        36 
        37 app=App()
      

      Недостаток этого способа — в том, что мы вынуждены работать в двух моделях событий — tkiner-овской и thread-овой.

  • «Правильный» подход: все события должны быть tkinter-овскими, таймер их только генерирует и подбрасывает в очередь событий:
       1 import time
       2 from tkinter import *
       3 from threading import Thread, Event;
       4 
       5 class Clock(Thread):
       6     def __init__(self, root, grain=1, event="<<Tick>>"):
       7         super().__init__()
       8         self.root = root    # Окно, которому посылать событие
       9         self.grain = grain  # Размер одного тика в секундах (м. б дробный)
      10         self.event = event  # TKinter-событие, которое надо посылать
      11         self.done = Event() # threading-событие, которое останавливет тред
      12 
      13     def run(self):
      14         while not self.done.wait(self.grain):
      15             self.root.event_generate(self.event)
      16 
      17 class App(Frame):
      18     def __init__(self, master=None, **kwargs):
      19         Frame.__init__(self, master, **kwargs)
      20         self.grid()
      21         self.Clock = Clock(self)
      22         self.Time = StringVar()
      23         self.update_clock()
      24         self.Screen = Label(textvariable=self.Time)
      25         self.Screen.grid(row=0, column=0)
      26         self.Start = Button(text="Start", command=self.start)
      27         self.Start.grid(row=0, column=1)
      28         self.Quit = Button(text="Quit", command=self.quit)
      29         self.Quit.grid(row=0, column=2)
      30         self.bind(self.Clock.event, self.tick)
      31         # тред надо остановить, даже если окно просто закрыли
      32         self.bind("<Destroy>", self.quit)
      33 
      34     def tick(self, event):
      35         self.update_clock()
      36 
      37     def start(self):
      38         self.Clock.start()
      39 
      40     def quit(self, *events):
      41         self.Clock.done.set()
      42         self.master.quit()
      43 
      44     def update_clock(self):
      45         self.Time.set(time.strftime("%H:%M:%S"))
      46 
      47 Tick = App()
      48 Tick.mainloop()
    

    Здесь используется т. н. синтетическое событие (в терминах tkintervirtual event), которое мы называем сами (это как class MyEvent(Event): в Python)

GitHub

TODO - issue - wiki - pages

ДЗ

TODO

Я в ближайшие пару дней буду делать proposal относительно семестровой работы по курсу. Если коротко: Работа — это git-репозиторий с кодом на Python3,

  • который я могу склонировать и запустить
  • в котором есть более одного участника, и я могу посмотреть статистику участия
  • в котором есть немножко тестов (с использованием любого тест-фреймворка, годится встроенный питоний)
  • в котором есть немножко документации
    • программной (с использованием любого фреймворка, годится встроенный питоний, но можно и sphinx)
    • пользовательской (либо sphinx, либо прямо на GH)
    • описание проекта и постановка задачи на GH
  • в котором есть немножко локализации (с теми же оговорками)

Немножко — это реально немножко, чтобы я видел, что работа проделана. Например, если вы задумали какое-то приложение из реал пайфа, и в нём довольно много логики, обмазать всю её тестами будет долго. Но пяток должен быть.

LecturesCMC/PythonDevelopment2019/07_TimersAndGhtHub (последним исправлял пользователь FrBrGeorge 2019-04-08 09:19:24)