Created
July 11, 2023 09:45
-
-
Save shiracamus/f34c54fcdefc4eba9d977e31c8e7bac8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import tkinter as tk | |
from datetime import datetime, timedelta, timezone | |
# サイズ設定 | |
SEG_WIDTH = 12 # このサイズを変更するだけで全体サイズが連動して変更される | |
SEG_HEIGHT = SEG_WIDTH * 4 | |
COLON_SIZE = SEG_WIDTH * 5 // 3 | |
CANVAS_WIDTH_NUMBER = SEG_WIDTH * 5 | |
CANVAS_WIDTH_COLON = CANVAS_WIDTH_NUMBER // 2 | |
CANVAS_HEIGHT = CANVAS_WIDTH_NUMBER * 2 - SEG_WIDTH | |
CANVAS_PAD = SEG_WIDTH * 5 // 6 | |
# 色の設定 | |
COLOR_BG = "black" | |
COLOR_SEG_ON = "orange" | |
COLOR_SEG_OFF = "gray20" | |
class ColonCanvas(tk.Canvas): | |
WIDTH = CANVAS_WIDTH_COLON | |
HEIGHT = CANVAS_HEIGHT | |
def __init__(self, master, color_seg_on, cnf={}, **kw): | |
super().__init__(master, cnf, **kw) | |
self.color_seg_on = color_seg_on | |
self._build() | |
def _build(self): | |
# 横方向の中心はキャンバスの中心 | |
center_x = self.WIDTH // 2 | |
# 縦方向の中心はキャンバスの1/3 と 2/3の位置 | |
for center_y in self.HEIGHT * 1 // 3, self.HEIGHT * 2 // 3: | |
# 正方形を作成 | |
x1 = center_x - COLON_SIZE // 2 | |
y1 = center_y - COLON_SIZE // 2 | |
x2 = center_x + COLON_SIZE // 2 | |
y2 = center_y + COLON_SIZE // 2 | |
id_ = self.create_rectangle(x1, y1, x2, y2) | |
self.itemconfig(id_, fill=self.color_seg_on, width=0) | |
class NumberCanvas(tk.Canvas): | |
WIDTH = CANVAS_WIDTH_NUMBER | |
HEIGHT = CANVAS_HEIGHT | |
# 0~9の数字を表示する7セグメントの点灯(True)消灯(False)情報 | |
SEG_ON = ( | |
# 上, 左上, 右上, 中, 左下, 右下, 下 | |
(True, True, True, False, True, True, True), # 0 | |
(False, False, True, False, False, True, False), # 1 | |
(True, False, True, True, True, False, True), # 2 | |
(True, False, True, True, False, True, True), # 3 | |
(False, True, True, True, False, True, False), # 4 | |
(True, True, False, True, False, True, True), # 5 | |
(True, True, False, True, True, True, True), # 6 | |
(True, True, True, False, False, True, False), # 7 | |
(True, True, True, True, True, True, True), # 8 | |
(True, True, True, True, False, True, True) # 9 | |
) | |
# 横長六角形の基準セグメントの各頂点のx座標とy座標 | |
SEG_XS = ( | |
-WIDTH // 2 + SEG_WIDTH, # 左上 | |
-WIDTH // 2 + SEG_WIDTH // 2, # 左中 | |
-WIDTH // 2 + SEG_WIDTH, # 左下 | |
+WIDTH // 2 - SEG_WIDTH, # 右下 | |
+WIDTH // 2 - SEG_WIDTH // 2, # 右中 | |
+WIDTH // 2 - SEG_WIDTH, # 右上 | |
) | |
SEG_YS = ( | |
-SEG_WIDTH // 2, # 左上 | |
0, # 左中 | |
+SEG_WIDTH // 2, # 左下 | |
+SEG_WIDTH // 2, # 右下 | |
0, # 右中 | |
-SEG_WIDTH // 2, # 右上 | |
) | |
# 基準セグメント座標を7セグメントの表示位置に変換するための回転・移動情報 | |
SEG_TRANSFORM = ( | |
# [回転するか?, 横方向の移動量, 縦方向の移動量] | |
(False, WIDTH // 2, SEG_WIDTH // 2), # 横上 | |
(True, SEG_WIDTH // 2, HEIGHT // 2 - SEG_HEIGHT // 2), # 縦左上 | |
(True, WIDTH - SEG_WIDTH // 2, HEIGHT // 2 - SEG_HEIGHT // 2), # 縦右上 | |
(False, WIDTH // 2, HEIGHT // 2), # 横中 | |
(True, SEG_WIDTH // 2, HEIGHT // 2 + SEG_HEIGHT // 2), # 縦左下 | |
(True, WIDTH - SEG_WIDTH // 2, HEIGHT // 2 + SEG_HEIGHT // 2), # 縦右下 | |
(False, WIDTH // 2, HEIGHT - SEG_WIDTH // 2) # 横下 | |
) | |
def __init__(self, master, color_seg_on, cnf={}, **kw): | |
super().__init__(master, cnf, **kw) | |
self.color_seg_on = color_seg_on | |
self._build() | |
def _build(self): | |
'''基準セグメント座標を回転・移動して7つの六角形セグメントを作成する''' | |
self.segs = [] | |
for is_rotate, x_shift, y_shift in self.SEG_TRANSFORM: | |
if is_rotate: | |
# 回転必要な場合は、基準セグメントの頂点の座標を90度を回転 | |
r_xs = [-n for n in self.SEG_YS] | |
r_ys = [n for n in self.SEG_XS] | |
else: | |
# 回転不要な場合は、基準セグメントの頂点の座標をそのまま使用 | |
r_xs = self.SEG_XS | |
r_ys = self.SEG_YS | |
# 基準セグメントの各頂点を移動 | |
t_xs = [n + x_shift for n in r_xs] | |
t_ys = [n + y_shift for n in r_ys] | |
# 移動後の座標に六角形を作成してリストに追加 | |
seg = self.create_polygon( | |
*[xy for xys in zip(t_xs, t_ys) for xy in xys], | |
fill=COLOR_SEG_OFF, | |
width=0, | |
) | |
self.segs.append(seg) | |
def update(self, number): | |
'''numberの数字を7セグメントで表示する''' | |
for seg, seg_on in zip(self.segs, self.SEG_ON[number]): | |
color = self.color_seg_on if seg_on else COLOR_SEG_OFF | |
self.itemconfig(seg, fill=color) | |
class Timer: | |
JST = timezone(timedelta(hours=9)) | |
@classmethod | |
def time(cls): | |
now = datetime.now(tz=cls.JST) | |
return now.hour, now.minute, now.second, now.microsecond | |
class Drawer: | |
'''時計を描画するクラス''' | |
CANVAS_CLASSES = ( | |
NumberCanvas, # 時の10の位 | |
NumberCanvas, # 時の1の位 | |
ColonCanvas, | |
NumberCanvas, # 分の10の位 | |
NumberCanvas, # 分の1の位 | |
ColonCanvas, | |
NumberCanvas, # 秒の10の位 | |
NumberCanvas, # 秒の1の位 | |
) | |
def __init__(self, master, color_seg_on): | |
'''6個の数字キャンバスと2個のコロンキャンバスを作成''' | |
self.number_canvases = [] | |
for canvas_class in self.CANVAS_CLASSES: | |
canvas = canvas_class( | |
master, | |
color_seg_on, | |
width=canvas_class.WIDTH, | |
height=canvas_class.HEIGHT, | |
bg=COLOR_BG, | |
highlightthickness=0, | |
) | |
if canvas_class == NumberCanvas: | |
self.number_canvases.append(canvas) | |
# 左から順番にpackで詰めていく | |
canvas.pack(side=tk.LEFT, padx=CANVAS_PAD, pady=CANVAS_PAD) | |
def update(self, hour, minute, second): | |
'''時分秒の各桁毎にNumberCanvasに表示する''' | |
numbers = ( | |
*divmod(hour, 10), | |
*divmod(minute, 10), | |
*divmod(second, 10), | |
) | |
for canvas, number in zip(self.number_canvases, numbers): | |
canvas.update(number) | |
class DigitalClock: | |
def __init__(self, master, color_seg_on): | |
self.master = master | |
self.drawer = Drawer(master, color_seg_on) | |
def update(self): | |
'''表示時刻を更新する''' | |
hour, minute, second, microsecond = Timer.time() | |
self.drawer.update(hour, minute, second) | |
# 単に1000を指定すると処理遅延が蓄積して一度に2秒進むことがあるため、 | |
# microsecondを使って次の秒までの残り時間を計算して指定する | |
self.master.after(1000 - microsecond // 1000, self.update) | |
if __name__ == "__main__": | |
app = tk.Tk() | |
app.title("Digital Clock") | |
app.config(bg=COLOR_BG) | |
clock = DigitalClock(app, color_seg_on="blue") | |
clock.update() # オブジェクト生成が終わってから更新処理開始 | |
app.mainloop() # 描画・イベント処理ループ開始 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment