tkinter: 時計の針の回転をどれだけ高速にできるか
解決したいこと
バーサライタという器具があります。
これは、一列に並べたLEDを高速で動かすことで残像を起こし、画像を表示するものです。回転数は大体秒速10~30回転(600~1800rpm)が必要と言われています。
参考:https://homemadegarbage.com/tag/%e3%83%90%e3%83%bc%e3%82%b5%e3%83%a9%e3%82%a4%e3%82%bf
これを、tkinter上で表現しようと試みています。
まず、円を1度ずつ360の座標に分割しました。次に回転速度を1度/1ミリ秒とすると360度/360ミリ秒となり、1秒では2.78回転となります。
そこで、回転速度をあげて、1秒30回転以上にできる方法はないか、考えています。
ただし角度を増やすと座標が飛んで残像が得られないため、秒数を短くする必要があります。しかし、after()メソッドは1ミリ秒以下にはできません。
試したこと
以下のコードで、残像が得られるかどうか、試してみました。
円を、角度36度の扇、10枚に分割して、扇の中で往復させて、扇を重ねてみました。数値的には1秒あたり27.8回転していることになります。
結果は、残像らしきものはありますが、目標とはほど遠いものです。
そこで、「針の動きを1秒間30回以上することが可能なのか」が質問です。
after()メソッドでは、不可能なのか?
プログラム的に他の方法があるのか?
以下のコードに問題があるのか?
それとも、LEDではなくモニター画面だから、人間の視覚的に無理なのか。
何でも良いので、助言を頂けたら幸いです。
該当するソースコード
# -*- coding: utf-8 -*-
import tkinter as tk
import math
import random
# キャンバスのサイズの設定
CANVAS_WIDTH = 400
CANVAS_HEIGHT = CANVAS_WIDTH
CANVAS_SIZE = CANVAS_WIDTH
# 時計の前面と背景の色の設定
BG_COLOR = "black"
#時計の外枠
FG_COLOR = "gray"
# 盤面を表す円の半径の設定
CLOCK_OVAL_RADIUS = CANVAS_SIZE / 2
R = CLOCK_OVAL_RADIUS*0.95
X0 = CANVAS_HEIGHT/2
Y0 = CANVAS_WIDTH/2
Color_Lst =["blue", "orange", "red", "green", "purple"]
class POV(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.frame0= tk.Frame(self.master, bg="white")
self.frame0.pack()
# キャンバスの中心座標を覚えておく
self.center_x = CANVAS_WIDTH / 2
self.center_y = CANVAS_HEIGHT / 2
# 変数定義
self.timer_on = False
self.ovalhand_lst = []
self.after_id = 0
self.rad =0
self.create_widget()
self.create_button()
self.draw_hand10()
def create_widget(self):
self.canvas = tk.Canvas(self.frame0, width = CANVAS_WIDTH, height = CANVAS_HEIGHT,
bg=BG_COLOR, highlightthickness=0)
#gridは3x3
self.canvas.grid(column=0, row=0, columnspan=3, padx=0, pady=0,
sticky=tk.NSEW,
)
# 時計の盤面を表す円を描画する
x1 = self.center_x - CLOCK_OVAL_RADIUS
y1 = self.center_y - CLOCK_OVAL_RADIUS
x2 = self.center_x + CLOCK_OVAL_RADIUS
y2 = self.center_y + CLOCK_OVAL_RADIUS
self.canvas.create_oval(
x1, y1, x2, y2,
fill="black",
width=2,
outline="gold"
)
def draw_hand10(self):
for j in range(10):
rad_s = (math.pi/180*36)*j
#20個のLEDを模した円を並べる
for i in range(1,21):
xa = X0 + R*math.cos(rad_s + self.rad)*0.05*i
ya = Y0 + R*math.sin(rad_s + self.rad)*0.05*i
#
xb = xa-5
yb = ya-5
xc = xa+5
yc = ya+5
oval_hand =self.canvas.create_oval(xb, yb, xc, yc, fill = "white", outline = "", width = 3)
self.ovalhand_lst.append(oval_hand)
def rotate_hand10(self):
#円を10個に分割
for j in range(10):
rad_s = (math.pi/180*36)*j
for i in range(20):
xa = X0 + R*math.cos(rad_s + self.rad)*0.05*(i+1)
ya = Y0 + R*math.sin(rad_s + self.rad)*0.05*(i+1)
#
xb = xa-5
yb = ya-5
xc = xa+5
yc = ya+5
#
k = j%5
rcol = Color_Lst[k]
its = j*20 +i
self.canvas.coords(self.ovalhand_lst[its], xb, yb, xc, yc)
self.canvas.itemconfig(self.ovalhand_lst[its], fill =rcol, outline = "")
#1度ずつふえる 36度まで。その後0度に戻す
if self.rad < math.pi/180*36:
self.rad += (math.pi/180)
else:
self.rad =0
#afterメソッドで、1ミリ秒後に繰り返す。5とか6の方がそれっぽいか?
self.after_id = self.after(1, self.rotate_hand10)
def create_button(self):
# スタート/ストップボタン
self.start_button = tk.Button(self.frame0, width=20, height=1, text="スタート/ストップ", fg = "gray20",
font=("MSゴシック体", "14"), command=self.start_button_clicked)
self.start_button.grid(column=0, row=2, columnspan=3, sticky=tk.NSEW)
#startボタンを押した時
def start_button_clicked(self):
if self.timer_on == False:
self.timer_on = True
self.rotate_hand10()
elif self.timer_on == True:
self.timer_on = False
#更新をキャンセル
self.after_cancel(self.after_id)
if __name__ == "__main__":
app = tk.Tk()
POV(app)
app.mainloop()