tkinter:クラスのインスタンス化の引数に、Canvasのoptionを渡す方法
解決したいこと
この半年間はクラス化を集中的に勉強しており、最近はネット上の記事を参考にして、自習しています。
「デジタル時計作り方(7セグ表示版)」 https://daeudaeu.com/tkinter-digital-clock/ はとても参考になりました。
次の課題として、クラスのインスタンス化の際に、引数を指定することで、表示色を変更することを考えました。具体的には、初期設定のオレンジ色を、引数で青色に変更を試みています。
引数で、Canvas上の図形optionを変更することになるので、苦労しています。解決策や問題点など、ご教授お願いします。
発生している問題・エラー
最初は、コード上は定数となっている色を、インスタンス作成時の引数で変更することを考えました。しかし、引数の追加処理が増えすぎたため断念しました。
次に、インスタンス作成時に渡されたfillオプションで、色変更を検討しました。
https://memopy.hatenadiary.jp/entry/2017/06/17/172611
を参考にして、コードを修正しました。
その結果、デジタル時計のコロン部分はうまくいきましたが、数字部分がうまくいきません。実行時には一瞬表示できるものの、時間更新がうまくいきません。どうやら動作はしていますが、表示されていません。
引数である(self, master, cnf, **kw)についての理解が足りないのか、
afterにおける引数の渡し方が悪いのか、分かりません。
いろいろと変数を追加・削除し、エラーが出なくなりましたが、ここまで4週間程の試行錯誤で、心が折れました。コードが長くて申し訳ありません。
該当するソースコード
# -*- coding: utf-8 -*-
import tkinter as tk
from datetime import datetime, timedelta, timezone
#幅の設定
SEG_WIDTH = 12
CANVAS_WIDTH_NUMBER = 60
CANVAS_WIDTH_COLON = 30
# 色の設定
COLOR_BG = "black"
COLOR_SEG_ON = "orange"
COLOR_SEG_OFF = "gray20"
class ColonCanvas(tk.Canvas):
canvas_width = CANVAS_WIDTH_COLON
canvas_height = CANVAS_WIDTH_NUMBER * 2 - SEG_WIDTH
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master, cnf, **kw)
def draw(self, master, cnf, **kw):
for i in range(2):
# 横方向の中心はキャンバスの中心
center_x = ColonCanvas.canvas_width / 2
# 縦方向の中心はキャンバスの1/3 or 2/3の位置
center_y = (i + 1) * ColonCanvas.canvas_height / 3
# 長方形の各辺の長さが20となるように座標を設定
x1 = center_x - 10
y1 = center_y - 10
x2 = center_x + 10
y2 = center_y + 10
# 長方形を描画
id_1=self.create_rectangle(x1, y1, x2, y2)
#
self.itemconfig(id_1, fill = "", width = 0)
self.itemconfig(id_1, **kw)
class NumberCanvas(tk.Canvas):
# 各種パラメータの設定。
canvas_width = CANVAS_WIDTH_NUMBER
seg_width = SEG_WIDTH
#
canvas_height = canvas_width * 2 - seg_width
seg_length = canvas_width - seg_width
# 数字0から9をセグ表示する際の、各セグの点灯or消灯の情報のリスト
ON_OFF_INFOS = [
# 上, 上左, 上右, 中, 下左, 下右, 下
[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
]
# 各セグ0〜9を描画するときの情報のリスト
SEG_DRAW_PARAMS = [
# [回転するか?, 横方向の移動量, 縦方向の移動量]
[False, canvas_width / 2, seg_width / 2],
[True, seg_width / 2, canvas_height / 2 - seg_length / 2],
[True, canvas_width - seg_width / 2, canvas_height / 2 - seg_length / 2],
[False, canvas_width / 2, canvas_height / 2],
[True, seg_width / 2, canvas_height / 2 + seg_length / 2],
[True, canvas_width - seg_width / 2, canvas_height / 2 + seg_length / 2],
[False, canvas_width / 2, canvas_height - seg_width / 2]
]
# 基準セグの、六角形の、各頂点のx座標とy座標
XS = [
- canvas_width / 2 + seg_width, # 左上
- canvas_width / 2 + seg_width / 2, # 左中
- canvas_width / 2 + seg_width, # 左下
canvas_width / 2 - seg_width, # 右下
canvas_width / 2 - seg_width / 2, # 右中
canvas_width / 2 - seg_width # 右上
]
YS = [
- seg_width / 2, # 左上
0, # 左中
seg_width / 2, # 左下
seg_width / 2, # 右下
0, # 右中
- seg_width / 2 # 右上
]
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master, cnf, **kw)
self.segs = []
def draw(self, master, cnf, **kw):
'''各座標で7つのセグを一気に描画する'''
for draw_param in NumberCanvas.SEG_DRAW_PARAMS:
is_rotate, x_shift, y_shift = draw_param
if is_rotate:
# 回転必要な場合は、基準セグの頂点の座標を90度を回転
r_xs = [-n for n in NumberCanvas.YS]
r_ys = [n for n in NumberCanvas.XS]
else:
# 回転不要な場合は基準セグの頂点の座標をそのまま使用
r_xs = NumberCanvas.XS
r_ys = NumberCanvas.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(
t_xs[0], t_ys[0],
t_xs[1], t_ys[1],
t_xs[2], t_ys[2],
t_xs[3], t_ys[3],
t_xs[4], t_ys[4],
t_xs[5], t_ys[5],
fill = COLOR_SEG_OFF,
width = 0,
)
# 描画した六角形のIDをリストに格納
self.segs.append(seg)
def update(self, num, master, cnf, **kw):
'''数字numをセグ表示する'''
for seg, is_on in zip(self.segs, NumberCanvas.ON_OFF_INFOS[num]):
if is_on:
# 点灯する場合のセグの色
color = COLOR_SEG_ON
#工夫した箇所
self.itemconfig(seg, fill = "")
self.itemconfig(seg, **kw)
else:
# 消灯する場合のセグの色
color = COLOR_SEG_OFF
#本来のコード
#self.itemconfig(seg, fill = color)
class Timer:
def __init__(self):
self.JST = timezone(timedelta(hours=9))
def time(self):
now = datetime.now(tz=self.JST)
return now.hour, now.minute, now.second
class Drawer:
'''時計を描画するクラス'''
CANVAS_NUMBER = 1
CANVAS_COLON = 2
def __init__(self, master=None, cnf={}, **kw):
# 各種設定を行なった後に時計の盤面を描画
self.initSetting(master)
self.createClock()
def initSetting(self, master):
'''時計描画に必要な設定を行う'''
# ウィジェットの作成先を設定
self.master = master
# 作成するキャンバスの種類をリストにする
self.canvas_types = [
Drawer.CANVAS_NUMBER,
Drawer.CANVAS_NUMBER,
Drawer.CANVAS_COLON,
Drawer.CANVAS_NUMBER,
Drawer.CANVAS_NUMBER,
Drawer.CANVAS_COLON,
Drawer.CANVAS_NUMBER,
Drawer.CANVAS_NUMBER
]
self.number_canvases = []
self.colon_canvases = []
def createClock(self):
#6個の数字キャンバスと2個のコロンキャンバスを作成
for canvas_type in self.canvas_types:
if canvas_type == Drawer.CANVAS_NUMBER:
canvas = NumberCanvas(
self.master,
width=NumberCanvas.canvas_width,
height=NumberCanvas.canvas_height,
bg=COLOR_BG,
highlightthickness=0,
)
self.number_canvases.append(canvas)
else:
# コロン表示用のキャンバスを作成
canvas = ColonCanvas(
self.master,
width=ColonCanvas.canvas_width,
height=ColonCanvas.canvas_height,
bg=COLOR_BG,
highlightthickness=0,
)
self.colon_canvases.append(canvas)
# 左から順番にpackで詰めていく
canvas.pack(side=tk.LEFT, padx=10, pady=10)
def draw(self, master, cnf, **kw):
for canvas in self.colon_canvases:
canvas.draw(master, cnf, **kw)
for canvas in self.number_canvases:
canvas.draw(master, cnf, **kw)
def update(self, hour, minute, second, master, cnf, **kw):
#個々のインスタンスに対して、メソッドを実行している:セグ表示を更新する'''
nums = []
nums.append(hour // 10)
nums.append(hour % 10)
nums.append(minute // 10)
nums.append(minute % 10)
nums.append(second // 10)
nums.append(second % 10)
for canvas, num in zip(self.number_canvases, nums):
canvas.update(num, master, cnf, **kw)
class DigitalClock:
def __init__(self, master=None, cnf={}, **kw):
self.master = master
# 各種クラスのオブジェクトを生成
self.timer = Timer()
self.drawer = Drawer(master)
# コロンとセグを描画する
self.draw(master, cnf, **kw)
# 時刻をセグ表示する
self.update(master, cnf, **kw)
def draw(self, master, cnf, **kw):
'''コロンとセグを描画する'''
self.drawer.draw(master, cnf, **kw)
def update(self, master, cnf, **kw):
'''時刻をセグ表示を更新する'''
hour, minute, second = self.timer.time()
self.drawer.update(hour, minute, second, master, cnf, **kw)
self.master.after(1000, self.update, master, cnf)
if __name__ == "__main__":
app = tk.Tk()
# 背景色を設定
app.config(bg=COLOR_BG)
DigitalClock(app, fill="blue")
app.mainloop()