-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathclock.py
More file actions
71 lines (56 loc) · 2.79 KB
/
Copy pathclock.py
File metadata and controls
71 lines (56 loc) · 2.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# project: Tkinterlite
# authors: Giuseppe Costanzi (1966bc)
# licence: GPL-3.0-or-later, see LICENSE
# -----------------------------------------------------------------------------
"""A clock on its own thread: how a thread and Tkinter work together.
Tkinter is single-threaded: only the thread that runs the main loop may touch
a widget. A second thread that calls label.config(...) itself works on one
machine, some of the time, and freezes or crashes on another - which is why
threads in Tkinter are argued about on every forum.
The pattern that works, in three parts:
the thread makes data Clock.run() puts a string on a queue
the queue carries it queue.Queue is safe between threads
the main loop takes it Main.check_clock() drains it, from after()
The thread never touches a widget; the main loop never waits for the thread.
For a clock this is more than is needed: after(1000, ...) alone would do. It is
here to show the pattern for work that does block - reading a slow device,
watching a folder - where the main loop must not stop and wait.
"""
import datetime
import queue
import threading
class Clock(threading.Thread):
"""Puts the time on a queue every second, on a thread of its own."""
def __init__(self):
# daemon: the thread ends with the program, even if stop() is forgotten.
# A name, so that it can be told apart in threading.enumerate() or a traceback.
super().__init__(name="clock", daemon=True)
self.queue = queue.Queue()
#: Set by stop(). An Event and not a boolean: wait() on it returns the
#: moment it is set, so the thread does not sleep out its second first.
self.stopping = threading.Event()
def stop(self):
"""Ask the thread to end. Called from the main loop, on exit."""
self.stopping.set()
def run(self):
"""The thread's own code: one message now, then one a second until stopped.
stopping.wait(1.0) is the sleep and the check in one: it returns False
after a second, or True as soon as stop() is called.
"""
self.put_time()
while not self.stopping.wait(1.0):
self.put_time()
def put_time(self):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.queue.put("Astral date: {0}".format(now))
def drain(self):
"""Take every message off the queue, oldest first, without waiting.
Called from the main loop, the only thread that takes from the queue:
so a queue that is not empty here still has something for get_nowait().
"""
messages = []
while not self.queue.empty():
messages.append(self.queue.get_nowait())
return messages