Python:アナログ時計の連続秒針は、累積演算子では無理?
解決したいこと
tkinterでアナログ時計の勉強をしています。
datetimeクラスで現在の時刻を入手し、それを角度に変換してアナログ時計にすることは成功しました。ただし、秒針においては1秒毎のステップ秒針でした。
連続秒針に改良するにあたり、まずは、microsecondを含めての角度計算で連続秒針にすることはできました。そこでより円滑な動きを目指し、秒針の角速度を累積演算子で処理することで、実現できないか試してみました。
発生している問題・エラー
正確に動作せず、時間経過とともに暴走します。
print()で確認しましたが、ラジアンの累積は正しいようです。10進数や、float()の計算誤差から生じているのか、πが無限数であることに起因する問題なのか、原因が理解できません。また、この方法では連続秒針の実現は無理なのでしょうか。
原因・解決策など、教えて頂ければ助かります。
該当するソースコード
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import tkinter
import math
from datetime import datetime, timedelta, timezone
# キャンバスのサイズの設定
CANVAS_WIDTH = 400
CANVAS_HEIGHT = CANVAS_WIDTH
CANVAS_SIZE = CANVAS_WIDTH
L_HAND = CANVAS_SIZE / 2 * 0.8
BG_COLOR = "white"
FG_COLOR = "gray"
# 時計の盤面を表す円の半径の設定
CLOCK_OVAL_RADIUS = CANVAS_SIZE / 2
# 時計の数字の位置の設定(中心からの距離)
DISTANCE_NUMBER = CANVAS_SIZE / 2 * 0.9
# キャンバスの中心座標
center_x = CANVAS_WIDTH / 2
center_y = CANVAS_HEIGHT / 2
x0, y0 = center_x, center_y
# 秒点を再配置する時間の間隔
TIME_INTERVAL = 300
# 秒点を再配置する角度の間隔
RAD_INTERVAL = (1/100)*math.pi
rad = 0
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 Setup:
def __init__(self, master):
# 各種設定を行なった後に時計の盤面を描画
self.initSetting(master)
self.setClock()
def initSetting(self, master):
'''時計描画に必要な設定を行う'''
self.master = master
self.llength = L_HAND
def setClock(self):
# キャンバスを作成して配置する
self.canvas = tkinter.Canvas(
self.master,
width=CANVAS_WIDTH,
height=CANVAS_HEIGHT,
highlightthickness=0,
)
self.canvas.pack()
# 時計の盤面を表す円を描画する
x1 = x0 - CLOCK_OVAL_RADIUS
y1 = y0 - CLOCK_OVAL_RADIUS
x2 = x0 + CLOCK_OVAL_RADIUS
y2 = y0 + CLOCK_OVAL_RADIUS
self.canvas.create_oval(
x1, y1, x2, y2,
fill=BG_COLOR,
width=2,
outline=FG_COLOR)
# 時計の盤面上に、数字の代わりに点を描画する
for hour in range(1, 13):
# 角度を計算
angle = (hour * 360)/12 - 90
# 描画位置を計算
dx = DISTANCE_NUMBER * math.cos(math.radians(angle))
dy = DISTANCE_NUMBER * math.sin(math.radians(angle))
x2 = x0 + dx
y2 = y0 + dy
self.canvas.create_oval(x2, y2, x2+10, y2+10, fill="#0fffff")
def setHands(self, second):
'''針を表現する点を描画する'''
global rad
ang_sec = (second * 360)/60 - 90
rad = math.radians(ang_sec)
x2 = x0 + self.llength * math.cos(rad)
y2 = y0 + self.llength * math.sin(rad)
self.canvas.create_oval(x2, y2, x2+10, y2+10, fill="red", tag="point1")
def rotateHands(self):
global rad
x2 = x0 + self.llength * math.cos(rad)
y2 = y0 + self.llength * math.sin(rad)
# coordsメソッドにより描画済みの線の座標を変更する
self.canvas.coords("point1", x2, y2, x2+10, y2+10)
# 描画する角度を増加
rad += RAD_INTERVAL
self.canvas.after(TIME_INTERVAL, self.rotateHands)
class AnalogClock:
def __init__(self, master):
# after実行用にウィジェットのインスタンスを保持
self.master = master
# 各種クラスのオブジェクトを生成
self.timer = Timer()
self.setup = Setup(master)
# 針を描画
self.draw()
# 1秒後に針を進めるループを開始
self.master.after(1000, self.update)
def draw(self):
'''時計の針を描画する'''
# 時刻を取得し、その時刻に合わせて針を描画する
hour, minute, second = self.timer.time()
self.setup.setHands(second)
def update(self):
'''時計の針を進める'''
self.setup.rotateHands()
self.master.after(TIME_INTERVAL, self.update)
if __name__ == "__main__":
app = tkinter.Tk()
AnalogClock(app)
app.mainloop()
0