このページでは、Python で tkinter を利用した「アナログ時計」の作り方について解説していきたいと思います。
出来上がりは下のアニメのようになります。
シンプルだね…
なんかめちゃめちゃ簡単に作れそうだけど…
シンプルなのは間違いないけど、
実際作ってみると結構学べることは多いよ!
例えば、針は線で表現するんだけど、その線の座標はどうやって求めるか分かる?
確かに数学は使うけど、考え方さえ分かれば線の座標も簡単に求めることができるよ!
で、この考え方は画像処理などいろんな分野でも使えるから覚えておいた方がいい!
わかった…
とりあえず解説聞いてみるよ!
Contents
アナログ時計の作り方
ではアナログ時計の作り方について解説していきたいと思います。
このアナログ時計を作る上でのポイントは下記の5つになると思いますので、これらについて一つ一つ解説していきたいと思います。
- 時計はキャンバス上に作成する
- 現在時刻を取得する
- 針を描画する
- 針を進める
after
を利用して針を進める
時計はキャンバス上に作成する
これは皆さん予想されてるかと思いますが、tkinter を利用する場合、アナログ時計はキャンバス上に作成していきます。
アナログ時計を作る上では “針” を表現するための線を描画する必要がありますので、他のウィジェットでアナログ時計を作るのは難しいと思います。
キャンバスの作り方については下記ページで解説していますので、詳しく知りたい方は是非読んでみてください。
Tkinterの使い方:キャンバスウィジェットの作り方スポンサーリンク
現在時刻を取得する
アナログ時計では現在時刻に合わせて針が動作します。そのため、アナログ時計をプログラミングするためには、まず “現在時刻を取得” する必要があります。
その時刻の取得の仕方ですが、実は私も詳しくありません…。日本時間を取得するのが難しいんですよね…。
ということで、現在時刻の取得の仕方については検索して調べ、最終的には下記ページを参考にさせていただきました。
https://qiita.com/papi_tokei/items/43b1d15a6694f576486c
結論としては、下記のように処理を行えば、日本時間の時刻の時・分・秒を取得することができます。
# タイムゾーンの情報を作成
JST = timezone(timedelta(hours=9))
# タイムゾーンに合わせた現在時刻を取得
now = datetime.now(tz=JST)
# nowのhour,minute,secondから現在時刻の時・分・秒を取得する
return now.hour, now.minute, now.second
各処理の詳細は上記ページを参考にしていただければと思います。
9
っていう数字が出てくるけど、これは何?
プログラミングで利用する時刻を取得する関数では UTC の時刻を返却することが多いんだ
今回は JST、要は日本での時刻を取得したかったので、その時差を考慮して時刻を取得するように設定しているんだよ
針を描画する
アナログ時計をアプリで表現するためには秒針・分針・時針の3つの針を表現する必要があります。
針は create_line
で描画する
tkinter
の Canvas
クラスにおいては線を描画するメソッドが用意されているので、線の描画により針を表現していきたいと思います。
具体的に使用するメソッドは create_line
になります。
def create_line(x1, y1, x2, y2, ...., xn, yn, option, ....)
Canvas
クラスのオブジェクトに create_line
を実行することで、そのオブジェクトに対応するキャンバスに線を描画することができます。
create_line
メソッドを含む、キャンバスへの図形の描画については下記ページで詳細をまとめていますので、詳しく知りたい方はこのページを読んだ後にでも下記ページを読んでみてください。
create_line
には2点の座標の指定が必要
針を表現する線を描画するときにポイントになるのが、create_line
メソッドの第1引数 〜 第4引数に渡す値です。以降では、この第1引数 〜 第4引数を x1
, y1
, x2
, y2
として表したいと思います。
create_line
メソッド実行時には、座標 (x1
, y1
) の点と座標 (x2
, y2
) の点を結ぶ直線が描画されることになります。
したがって、針の位置に対応した座標 (x1
, y1
) と座標 (x2
, y2
) を求めさえすれば、あとは create_line
メソッドによる線の描画でアナログ時計の針を表現することができます。
今回は針を描画するために2点の座標のみを create_line
メソッドに指定しますが、
3点以上の座標を create_line
メソッドに指定することで、折線を描画するようなことも可能です。
座標 (x1
, y1
) の求め方
一方の点の座標、例えばその座標を (x1
, y1
) とすれば、(x1
, y1
) に関しては時計の中心座標を指定してやれば良いです。
例えば上の図のように時計をキャンバスの中心に作成するとし、さらにキャンバスの幅を CANVAS_WIDTH
、キャンバスの高さを CANVAS_HEIGHT
とすれば、x1
と y1
は下記のように計算することができますね!
x1 = CANVAS_WIDTH / 2
y1 = CANVAS_HEIGHT / 2
ちなみに、キャンバスにおける原点 (0
, 0
) の座標は、上の図のようにキャンバスの左上端になります。
座標 (x2
, y2
) の求め方
ちょっと求めるのが難しいのが、他方の点の座標である (x2
, y2
) の方です。ただ、こちらの座標も三角関数を利用すれば割と簡単に求めることができます。
そんなに拒絶反応示さなくても大丈夫!
Python だと三角関数を実行する関数が用意されているから、
最終的にはその関数を実行すれば良いだけだよ!
今回描画したい線は、下の図のような、座標 (x1
, y1
) と座標 (x2
, y2
) の2点を結ぶ線です。
上の図において、r
・θ
・dx
・dy
というパラメータが登場していますが、これらは下記の意味を持つパラメータになります。
r
:2点間の距離θ
:描画したい線とx
軸とのなす角dx
:x2
とx1
の差dy
:y2
とy1
の差
ここで三角関数の登場です。上の図において、sin θ
と cos θ
の値を求めることを考えると、これらの値は下記のようにして算出することができます。
sinθ = dy / r
cosθ = dx / r
ということは、dx
と dy
は上式の式変形により下記で求めることができることになります。
dy = r * sinθ
dx = r * cosθ
さらに、 dx = x2 - x1
であることと、dy = y2 - y1
であることを考慮すれば、x2
と y2
は下記の式で求めることができると言えます。
x2 = x1 + r * cosθ
y2 = y1 + r * sinθ
sin
や cos
は Python の標準モジュールの math
に関数が用意されていますので、それを実行すれば sin
や cos
の値を求めることは可能です。
さらに、x1
と y1
は既に座標 (x1, y1) の求め方で求めていますので、要は r
と θ
さえ分かれば x2
と y2
を求めることができそうですね!
じゃあ r
は何かというと、今回 create_line
メソッドに座標 (x1
, y1
) と座標 (x2
, y2
) を指定して描画しようとしているのは時計の針(時針・分針・秒針)なので、要は r
は針の長さであると考えられます。
ですので、r
は描画したい針の長さを設定してやれば良いです(単位はピクセルで設定する)。
基本的には好きな長さを設定すればいいよ!
だけど、キャンバスや時計からはみ出るとデザイン的にイマイチだから、
その点は注意したほうが良いと思うよ!
あとは、各針の長さを “時針 < 分針 < 秒針” となるように設定した方が、アナログ時計っぽさは出るかな!
その一方で、θ
は何かというと、これは針の傾きになります。で、アナログ時計の場合、この針の傾きは、秒針であれば “現在時刻の秒”、分針であれば “現在時刻の分”、時針であれば “現在時刻の時” によって変化しますので、これらの時刻の情報によって θ
を求めることができます。
例えば秒針の傾きであれば、秒針は 60
秒で針が 360
度回転しますので、1
秒間では 360 / 60
度回転することになります。したがって、秒針を描画するときの θ
は下記のように求めることができます。
# secondは現在時刻の秒
θ = second * 360 / 60 - 90
- 90
しているのは、0
秒の時の角度が -90
度になるようにするためです。
分針の場合は 1
分間で 360 / 60
度、時針の場合は 1
時間で 360 / 12
度回転することを考慮すれば、秒針と同様にして針を描画するときの θ
を求めることができます。
上の式で、角度 θ
を “時計回り” の方向を正方向として求めているところに違和感を覚えた方もいらっしゃると思います
数学でよく用いる座標では角度の正方向は “反時計回り” の方向になりますが、キャンバスで用いる座標では角度の正方向は “時計回り” の方向になります
ですので、上記のようにキャンバスに描画する際に用いる角度 θ
は、角度の正方向を “時計回り” の方向として求めることができます
角度の正方向が逆方向になるのは、数学でよく用いる座標とキャンバスで用いる座標とでは、y
軸方向の正方向が逆になるためです
ここまでの内容をまとめると、まず x2
と y2
は下記の式で求めることができます。
x2 = x1 + r * cos θ
y2 = y1 + r * sin θ
さらに、上式における r
と θ
は下記を示す値になります。
r
:描画する針の長さθ
:描画する針の傾き(現在時刻の秒・分・時から算出)
上記により x2
と y2
を求めることができますので、後は座標 (x1, y1) の求め方で求めた x1
と y1
とともに create_line
メソッドの引数に指定すれば、任意の時刻の針を描画することができます。
ただし、math
モジュールの sin
・cos
関数に指定する値の単位は “度” ではなく “ラジアン” である点に注意してください。
私たちが日常的に用いる角度の単位は “度” ですが、math
モジュールの sin
・cos
関数に指定する値の単位はラジアンです。ですので、sin
・cos
関数を実行する際には、事前に角度をラジアン単位のものに変換する必要があります。
この変換は、math
モジュールの radians
関数を利用する or 自身で角度に math.pi
/ 180
を掛けて算出することで実行することができます(math.pi
は math
モジュールで定義された円周率)。
angle = second * 360 / 60 - 90
# angleの単位は度なので、ラジアンに変換してから実行
x2 = x1 + r * cos(math.radians(angle))
y2 = x2 + r * sin(angle * math.pi / 180)
うーん、式は難しそうだけど、
結局は時刻から針の傾きさえ求めれば、
あとは足し算・掛け算・関数の実行をすれば良いだけだしなんとかなるかな…
そうそう!
ちなみに図形を描画したり点の座標を求めたりするときに
この r * cosθ
や r * sinθ
の計算はよく利用するから、
この式や考え方は覚えておくといいと思うよ!
針を進める
上記の解説に基づいてプログラミングすることで、針を表現する線を描画することはできると思います。
ただ、針は一度描画するだけではダメで、実際のアナログ時計のように時刻に合わせて針を進める必要があります。
ここでは、この時刻に合わせて針を進める際のポイントについて解説していきます。
ん?
単に角度と座標計算し直して、
また線を描画すればいいだけじゃないの?
それだと前に描画した線が残っちゃうよね
時計が針だらけになっちゃうよ…
座標等の考え方は針を描画するで解説した通りなのですが、時刻が進むたびにそのまま線を描画してしまうと前に書いた線がキャンバス上に残ってしまいます。
さらにこれを繰り返すと時計が針だらけになって今が何時なのか分からなくってしまいます。
これに対する対策としては下記の2つがありますので、ここからはこの2つの対策それぞれについて解説していきたいと思います。
- 描画した線の座標だけを変更する
- 前に描画した線を削除してから線を描画する
ちなみにこれらの対策においては Canvas
クラスの coords
メソッドと delete
メソッドを利用します。
こういった描画済みの図形を操作するメソッドについては下記ページで解説していますので、他のメソッドも知りたい方は、このページを読み終わった後にでも是非読んでみてください。
Tkinterの使い方:Canvasクラスで描画した図形を操作する描画した線の座標だけを変更する
tkinter の Canvas
クラスには、”描画済みの図形の座標だけを変更する” メソッド coords
が用意されています。
coords(self, tagOrId, x1, y1, x2, y2)
この coords
メソッドを実行することで、引数 tagOrId
で指定した線の座標を引数で指定した座標に変更することが可能です。
coords
メソッドを実行するのは、座標を変更したい図形が描画されているキャンバスのオブジェクトです。
引数 tagOrId
には図形の ID もしくはタグ名を指定します。
create_line
等の図形を描画するメソッドでは、戻り値がその描画した図形の ID となります。また、create_line
等の図形を描画するメソッドでは、tag
オプションを指定することで描画する図形にタグ名(名前みたいなもの)を付けることができます。
ですので、coords
メソッドの引数 tagOrId
には、create_line
の戻り値もしくは tag
オプションに指定したタグ名のどちらかを指定することになります。
これにより、create_line
により描画した線の座標を変更することができます。
例えば上の図では、ID or タグ名が hand
である線の座標を (x1
, y1
) と (x2
, y2
) に変更する様子を示しています(変更前の座標は (xo1
, yo1
) と (xo2
, yo2
) )。ちなみに hand
は秒針などの “針” を英語で表したものになります(手じゃないよ)。
この coords
メソッドを利用すれば、針を進める処理は下記のようにして実現することができます。
まず、事前に針を描画するで解説した通りに create_line
メソッドを実行して線を描画しておきます。この線の描画を行う際、create_line
メソッドの戻り値を変数等に格納して覚えておきます(タグを覚えておいても良いですが、今回は戻り値を利用する前提で説明します)。
# キャンバスはCanvasのオブジェクト
hand = canvas.create_line(
# 引数は略
)
さらに、針を進める際には針の描画(線の描画)は行わず、描画した線の座標のみを変更します。
具体的には、まず再度時刻を取得し、その時刻から座標 (x1
, y1
) と座標 (x2
, y2
) を計算します。ただし、一方の座標(座標 (x1
, y1
) )は毎回時計の中心の座標になるので、その座標を覚えておけば毎回計算し直す必要はありません。
そして、これらの座標 x1
・ y1
・x2
・y2
と最初に針を描画した時に覚えておいた “create_line
の戻り値” を引数に指定して coords
メソッドを実行します。
# キャンバスはCanvasのオブジェクト(handの描画先のオブジェクト)
canvas.coords(hand, x1, x2, y1, y2)
以上により、取得したタイミングの時刻に合わせて針の座標が変更され、”針を進める” を実現することができます。
ここでは coords
メソッドについて create_line
で描画した “線” のみに対して説明しました
しかし、楕円や長方形など、キャンバスに描画した図形であればどの図形であっても、coords
メソッドで座標を変更することが可能です
前に描画した線を削除してから線を描画する
tkinter の Canvas
クラスには、”描画済みの図形を削除する” メソッド delete
が用意されています。
delete(self, tagOrId)
この delete
メソッドを実行することで、引数 tagOrId
で指定した図形を削除することが可能です。
引数 tagOrId
の意味は描画した線の座標だけを変更するで説明したものと同じものになります。すなわち、create_line
の戻り値、もしくは create_line
実行時に tag
オプションに指定したタグ名、のどちらかを指定します。
この delete
メソッドにより、以前に描画した線をキャンバス上から削除することができます。ですので、後は時刻を取得し、さらに針を描画するで解説した通りに再度線を描画すれば、針が進んだように見せることができます。
スポンサーリンク
after
を利用して針を進める
針を進めるで解説した通りに処理を実行すれば、その実行した時点の時刻に合わせて針を進めることができます。
なので、あとはこの “針を進める” を定期的に(例えば秒針であれば1秒ごとに)実行すれば、アナログ時計のように時刻に合わせて針がどんどん進むような見た目を表現することができます。
ただし、tkinter で定期的に処理を行う際には、after
メソッドを利用する必要があることに注意してください。
例えば、下記のように無限ループの中で sleep
を行いながら “針を進める” 処理を行うようなこともできますが、これだと処理が mainloop
に戻らないので針を進めたとしてもキャンバスにその結果が反映されません(キャンバスに描画した図形が実際に画面に反映されるのは mainloop
に処理が戻った時になります)。
while True:
針を進める処理を実行
# 1秒間スリープ(事前にtimeをimportする必要あり
time.sleep(1)
after
メソッドを利用することで、mainloop
に処理を戻しつつ、定期的な処理も実行させることが可能になります。基本的に tkinter でのプログラミングを行う際には、定期的な処理を行う時は after
メソッドを利用するのが良いです。
def update(self):
針を進める処理を実行
# 1秒後に再度この関数を呼び出す(masterはアプリのメインウィンドウ)
self.master.after(1000, self.update)
ここでの解説で登場した mainloop
と after
は tkinter でのアプリ開発において非常に重要なものになります。下記で詳細に解説していますので、まだご存知ない方・理解が浅い方はぜひ読んでみてください。これらを知っているだけで作成できるアプリの幅が広がりますし、アプリの不具合の多くを防ぐことができるようになります。
また、tkinter を利用したプログラミングにおいても、マルチスレッドを利用した場合は after
を利用しなくても while
ループにより定期的な処理を実現することが可能です。
このマルチスレッドについては下記ページで解説していますので是非参考にしてください。マルチスレッドはプログラムを同時に平行で実行するために用いるもので、使いこなせると tkinter 利用時以外でも様々な場で役に立つと思います。
【Python/tkinter】tkinterでマルチスレッドを利用するアナログ時計のサンプルプスクリプト
最後に、ここまで解説してきた内容に基づいて作成したアナログ時計のサンプルスクリプトを紹介したいと思います。
スクリプト
アナログ時計のサンプルスクリプトは下記のようになります。
# -*- coding: utf-8 -*-
import tkinter
import math
from datetime import datetime, timedelta, timezone
# キャンバスのサイズの設定
CANVAS_WIDTH = 400
CANVAS_HEIGHT = CANVAS_WIDTH
CANVAS_SIZE = CANVAS_WIDTH
# 針の長さの設定
LENGTH_HOUR_HAND = CANVAS_SIZE / 2 * 0.6
LENGTH_MINUTE_HAND = CANVAS_SIZE / 2 * 0.7
LENGTH_SECOND_HAND = CANVAS_SIZE / 2 * 0.8
# 針の色の設定
COLOR_HOUR_HAND = "red"
COLOR_MINUTE_HAND = "blue"
COLOR_SECOND_HAND = "green"
# 針の太さの設定
WIDTH_HOUR_HAND = 6
WIDTH_MINUTE_HAND = 4
WIDTH_SECOND_HAND = 2
# 時計の前面と背景の色の設定
BG_COLOR = "white"
FG_COLOR = "gray"
# 時計の盤面を表す円の半径の設定
CLOCK_OVAL_RADIUS = CANVAS_SIZE / 2
# 時計の数字の位置の設定(中心からの距離)
DISTANCE_NUMBER = CANVAS_SIZE / 2 * 0.9
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:
'''時計を描画するクラス'''
def __init__(self, master):
# 各種設定を行なった後に時計の盤面を描画
self.initSetting(master)
self.createClock()
def initSetting(self, master):
'''時計描画に必要な設定を行う'''
# ウィジェットの作成先を設定
self.master = master
# 描画した針のオブジェクトを覚えておくリストを用意
self.hands = []
# 針の色のリストを用意
self.colors = [
COLOR_HOUR_HAND, COLOR_MINUTE_HAND, COLOR_SECOND_HAND
]
# 針の太さのリストを用意
self.widths = [
WIDTH_HOUR_HAND, WIDTH_MINUTE_HAND, WIDTH_SECOND_HAND
]
# 針の長さのリストを用意
self.lengths = [
LENGTH_HOUR_HAND, LENGTH_MINUTE_HAND, LENGTH_SECOND_HAND
]
# キャンバスの中心座標を覚えておく
self.center_x = CANVAS_WIDTH / 2
self.center_y = CANVAS_HEIGHT / 2
def createClock(self):
'''時計の盤面を作成する'''
# キャンバスを作成して配置する
self.canvas = tkinter.Canvas(
self.master,
width=CANVAS_WIDTH,
height=CANVAS_HEIGHT,
highlightthickness=0,
)
self.canvas.pack()
# 時計の盤面を表す円を描画する
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=BG_COLOR,
width=2,
outline=FG_COLOR
)
# 時計の盤面上に数字を描画する
for hour in range(1, 13):
# 角度を計算
angle = hour * 360 / 12 - 90
# 描画位置を計算
x1 = self.center_x
y1 = self.center_x
dx = DISTANCE_NUMBER * math.cos(math.radians(angle))
dy = DISTANCE_NUMBER * math.sin(math.radians(angle))
x2 = x1 + dx
y2 = y1 + dy
self.canvas.create_text(
x2, y2,
font=("", 20),
fill=FG_COLOR,
text=str(hour)
)
def drawHands(self, hour, minute, second):
'''針を表現する線を描画する'''
# 各線の傾きの角度を計算指定リストに追加
angles = []
angles.append(hour * 360 / 12 - 90)
angles.append(minute * 360 / 60 - 90)
angles.append(second * 360 / 60 - 90)
# 線の一方の座標をキャンバスの中心とする
x1 = self.center_x
y1 = self.center_y
# initSettingで作成したリストから情報を取得しながら線を描画
for angle, length, width, color in zip(angles, self.lengths, self.widths, self.colors):
# 線の他方の座標を計算
x2 = x1 + length * math.cos(math.radians(angle))
y2 = y1 + length * math.sin(math.radians(angle))
hand = self.canvas.create_line(
x1, y1, x2, y2,
fill=color,
width=width
)
# 描画した線のIDを覚えておく
self.hands.append(hand)
def updateHands(self, hour, minute, second):
'''針を表現する線の位置を更新する'''
angles = []
angles.append(hour * 360 / 12 - 90)
angles.append(minute * 360 / 60 - 90)
angles.append(second * 360 / 60 - 90)
# 線の一方の点の座標は常に時計の中心
x1 = self.center_x
y1 = self.center_y
# handは描画した線のID
for hand, angle, length in zip(self.hands, angles, self.lengths):
# 線の他方の点の座標は毎回時刻に合わせて計算する
x2 = x1 + length * math.cos(math.radians(angle))
y2 = y1 + length * math.sin(math.radians(angle))
# coordsメソッドにより描画済みの線の座標を変更する
hand = self.canvas.coords(
hand,
x1, y1, x2, y2
)
class AnalogClock:
'''アナログ時計を実現するクラス'''
def __init__(self, master):
# after実行用にウィジェットのインスタンスを保持
self.master = master
# 各種クラスのオブジェクトを生成
self.timer = Timer()
self.drawer = Drawer(master)
# 針を描画
self.draw()
# 1秒後に針を進めるループを開始
self.master.after(1000, self.update)
def draw(self):
'''時計の針を描画する'''
# 時刻を取得し、その時刻に合わせて針を描画する
hour, minute, second = self.timer.time()
self.drawer.drawHands(hour, minute, second)
def update(self):
'''時計の針を進める'''
# 時刻を取得し、その時刻に合わせて針を進める
hour, minute, second = self.timer.time()
self.drawer.updateHands(hour, minute, second)
# 1秒後に再度時計の針を進める
self.master.after(1000, self.update)
if __name__ == "__main__":
app = tkinter.Tk()
AnalogClock(app)
app.mainloop()
スポンサーリンク
スクリプト実行結果
スクリプトを実行すると下の画面のようなアプリが起動します。
緑色の線が秒針、青色の線が分針、赤色の線が時針をそれぞれ表しています。スクリプトを実行した時間によって針の位置が変化していることが確認できると思います。
また、アプリ起動後に放置しておくことで、1秒ごとに秒針が進み、秒針が12の位置に到達するごとに分針が進むことも確認できると思います(分針が12の位置に到達すれば時針も進みますが、最悪1時間近く待つ必要があるので確認するのは大変かもしれません…)。
スクリプトの設定
スクリプトの先頭部分で大文字の変数に値を格納している部分を変更することで、スクリプトの設定を行うことができます。
例えば針の色や太さ、キャンバスのサイズなどなどが設定可能です。
スクリプトの解説
最後に簡単にスクリプトの解説を行なっています。
まずこのスクリプトでは下記の3つのクラスを用意しています。
Timer
:時間を取得するクラスDrawer
:図形を描画するクラスAnalogClock
:アナログ時計を実現するクラス
Timer
Timer
クラスでは、現在時刻を取得するで解説した方法に基づいて日本時間の現在時刻の取得を行なっています。
Drawer
Drawer
クラスの drawHands
では針を描画するで解説した方法に基づいて針を描画する処理を行い、updateHands
では針を進めるで解説した方法に基づいて針を進める処理を行なっています(針は coords
メソッドにより進めています)。
drawHands
でも updateHands
でも、ループにより時針・分針・秒針に対して一気に処理を行なっているので若干複雑に見えるかもしれませんが、ループの中身を見れば解説した内容に基づいて処理していることが確認できると思います。
また針を進めるで解説した通り、updateHands
で実行している coords
メソッドでは、線描画時に実行した create_line
の戻り値を引数に指定する必要があります。
そのため、drawHands
の中で create_line
を実行した後にその戻り値を self.hands
というリストに格納するるようにしています。そして、updateHands
で coords
メソッドを実行する際には、そのリストを参照して引数に指定するようにしています。
他にも self.lengths
、self.colors
、self.widths
というリストを使用していますが、これらは __init__
から実行される initSetting
の中で作成されるリストで、各リストの要素には時針・分針・秒針それぞれの描画時の設定値(線の長さ・線の色・線の太さ)が格納されています。これらはスクリプトの設定で説明したように、スクリプトの先頭部分で変更可能です。
また createClock
では、アナログ時計の描画先となるキャンバスの作成や、時計の盤面となる円の描画、時計の盤面への数字の描画を行なっています。
各数字の描画位置は、針を描画するの座標 (x2, y2) の求め方で説明したのと同じ方法で求めています。
AnalogClock
AnalogClock
クラスでは、前述の Timer
クラスと Drawer
クラスのオブジェクトの作成と、そのオブジェクトへの処理の依頼(時刻の取得の依頼、針の描画の依頼、針を進める依頼)を行なっています。
特に “針を進める依頼” となる updateHands
の実行は、after を利用して針を進めるで説明したように after
メソッドを用いたループの中で定期的に実行するようにしています(1000
ms ごとに実行)。
スポンサーリンク
まとめ
このページでは、Python で tkinter を利用した「アナログ時計」の作り方を解説しました!
見た目はシンプルですが、針の描画や針を進める際にいろいろな工夫が必要であることを感じ取っていただけたのではないかと思います。
特に、座標 (x2, y2) の求め方で解説した座標の求める際の考え方、つまり三角関数を利用して座標を求める考え方はかなり多くの場面で活用できるものなので、是非この機会に覚えておいていただければと思います!例えば自力で画像を回転する場合もこの考え方を利用すれば簡単にプログラミングすることができます。
また、針を進めるで紹介した coords
メソッドや delete
メソッドなど、描画済みの図形を後から操作するメソッドを活用することで、キャンバス上の図形に “動き” を持たせるようなことも可能です。前述でも紹介しましたが、これらのメソッドについては下記ページで紹介していますので、興味のある方は是非読んでみてください!