Saturday, April 04, 2015

和時計


pythonで時計のプログラムを作ってみた。
python2系とTkinterが必要です、インストールしてください。
テストはLinuxMintでしか、してません。

特徴は
1.24時間表示で12時方向が昼の12時(図は午後7時56分38秒)
2.日の出、日の入り、南中時刻を表示(日の出5時24分、日の入り午後6時3分、南中11時44分)
3.大きさをマウスで変更可(root.minsizeからroot.maxsizeまで)

まずファイルをエディタで開いて、最初の方にある、「この場所のデータ」をお住まいの緯度、経度、標高に書き換えてください。
下のリストで「<」は「<」(半角)、「>」は「>」(半角)に書き換えてください。


#! /usr/bin/python
# coding: UTF-8

import Tkinter as tk
import math, time, datetime

# この場所のデータ
la = 34.8   # 緯度(度)。北緯は+,南緯は-
lo = 140.0  # 経度(度)。東経は+,西経は-
alt = 0.0   # 標高(m)
tdiff = 9.0   # グリニッジ標準時との時差

# グローバル変数
win_size = 200
backboard = []
rad = math.pi/180

# 太陽計算インターバルのための初期化
start_time = time.localtime()
h_old = start_time[3]
h = h_old + 1

# 三角関数を度で計算
def sind(d):
    return math.sin(d*rad)
def cosd(d):
    return math.cos(d*rad)
def tand(d):
    return math.tan(d*rad)
   
# Julius year の計算(2000/1/1からの時間)
def jy(yy, mm, dd, h, m, s, tdiff):
    yy -= 2000
    if mm <= 2 :
        mm += 12
        yy -= 1
    k = 365 * yy + 30 * mm + dd - 33.5 - tdiff / 24.0 + math.floor(3 * (mm + 1) / 5.0) \
    + math.floor(yy / 4.0) - math.floor(yy / 100.0) + math.floor(yy / 400.0)
    k += ((s / 60.0 + m) / 60.0 + h) / 24.0  # 時間をたす
    k += (65 + yy) / 86400.0  # delta T をたす
    return k / 365.25

# 太陽位置1 (celestial longitude, degree)
def spls(t):  # t Julius
    l = 280.4603 + 360.00769 * t \
    + (1.9146 - 0.00005 * t) * sind(357.538 + 359.991 * t) \
    + 0.0200 * sind(355.05 +  719.981 * t) \
    + 0.0048 * sind(234.95 +   19.341 * t) \
    + 0.0020 * sind(247.1  +  329.640 * t) \
    + 0.0018 * sind(297.8  + 4452.67  * t) \
    + 0.0018 * sind(251.3  +    0.20  * t) \
    + 0.0015 * sind(343.2  +  450.37  * t) \
    + 0.0013 * sind( 81.4  +  225.18  * t) \
    + 0.0008 * sind(132.5  +  659.29  * t) \
    + 0.0007 * sind(153.3  +   90.38  * t) \
    + 0.0007 * sind(206.8  +   30.35  * t) \
    + 0.0006 * sind( 29.8  +  337.18  * t) \
    + 0.0005 * sind(207.4  +    1.50  * t) \
    + 0.0005 * sind(291.2  +   22.81  * t) \
    + 0.0004 * sind(234.9  +  315.56  * t) \
    + 0.0004 * sind(157.3  +  299.30  * t) \
    + 0.0004 * sind( 21.1  +  720.02  * t) \
    + 0.0003 * sind(352.5  + 1079.97  * t) \
    + 0.0003 * sind(329.7  +   44.43  * t)
    while l >= 360 :
        l -= 360
    while l < 0 :
        l += 360
    return l

# 太陽位置2 (distance, AU)
def spds(t):  # t Julius
    r = (0.007256 - 0.0000002 * t) * sind(267.54 + 359.991 * t) \
    + 0.000091 * sind(265.1 +  719.98 * t) \
    + 0.000030 * sind( 90.0) \
    + 0.000013 * sind( 27.8 + 4452.67 * t) \
    + 0.000007 * sind(254   +  450.4  * t) \
    + 0.000007 * sind(156   +  329.6  * t)
    r = math.pow(10, r)
    return r

# 太陽位置3 (declination, degree)
def spal(t):  # t Julius
    ls = spls(t)
    ep = 23.439291 - 0.000130042 * t
    al = math.atan(tand(ls) * cosd(ep)) * 180.0 / math.pi
    if ls >= 0 and ls < 180 :
        while al < 0 :
            al += 180
        while al >= 180 :
            al -= 180
    else :
        while al < 180 :
            al += 180
        while al >= 360 :
            al -= 180
    return al

# 太陽位置4 (the right ascension, degree)
def spdl(t):  # t Julius
    ls = spls(t)
    ep = 23.439291 - 0.000130042 * t
    dl = math.asin(sind(ls) * sind(ep)) * 180.0 / math.pi
    return dl

# Calculate sidereal hour (degree)
def sh(t, h, m, s, l, tdiff):  # t julius, h hour, m minute, s second, l longitude, tdiff time difference
    d = ((s / 60.0 + m) / 60.0 + h) / 24.0  # elapsed hour (from 0:00 a.m)
    th = 100.4606 + 360.007700536 * t + 0.00000003879 * t * t - 15.0 * tdiff
    th += l + 360.0 * d
    while th >= 360 :
        th -= 360
    while th < 0 :
        th += 360
    return th

# Calculating the seeming horizon altitude "sa"(degree)
def eandp(alt, ds):  # subfunction for altitude and parallax
    e = 0.035333333 * math.sqrt(alt)
    p = 0.002442818 / ds
    return p - e

def sa(alt, ds):  # alt: altitude (m), ds: solar distance (AU)
    s = 0.266994444 / ds
    r = 0.585555555
    k = eandp(alt, ds) - s - r
    return k

# Calculating solar alititude (degree)
def soal(la, th, al, dl):  # la latitude, th sidereal hour, al solar declination, dl right ascension
    h = sind(dl) * sind(la) + cosd(dl) * cosd(la) * cosd(th - al)
    h = math.asin(h) * 180.0 / math.pi
    return h

# Calculating solar direction (degree)
def sodr(la, th, al, dl):  # la latitude, th sidereal hour, al solar declination, dl right ascension
    t = th - al
    dc = - cosd(dl) * sind(t)
    dm = sind(dl) * sind(la) - cosd(dl) * cosd(la) * cosd(t)
    if dm == 0 :
        st = sind(t)
        if st > 0 :
            dr = -90
        if st == 0 :
            dr = 9999
        if st < 0 :
            dr = 90
    else :
        dr = math.atan(dc / dm) * 180.0 / math.pi
        if dm < 0 :
            dr += 180
    if dr < 0 :
        dr += 360
    return dr

# 今日の日の出日没時刻計算(sunrize_h 日の出時, sunrize_m 日の出分, sunset_h 日没時, sunset_m 日没分,
#                            meridian_h 南中時, meridiann_m 南中分)
def sunpos():
    global la, lo, alt, tdiff
    # 今日の年月日
    td = datetime.datetime.today()
    yy = td.year
    mm = td.month
    dd = td.day
   
    # 日の出、日没の計算
    t = jy(yy, mm, dd-1, 23, 59, 0, tdiff)
    th = sh(t, 23, 59, 0, lo, tdiff)
    ds = spds(t)
    ls = spls(t)
    alp = spal(t)
    dlt = spdl(t)
    pht = soal(la, th, alp, dlt)
    pdr = sodr(la, th, alp, dlt)
   
    for hh in range(0, 24):
        for m in range(0, 60):
            t = jy(yy, mm, dd, hh, m, 0, tdiff)
            th = sh(t, hh, m, 0, lo, tdiff)
            ds = spds(t)
            ls = spls(t)
            alp = spal(t)
            dlt = spdl(t)
            ht = soal(la, th, alp, dlt)
            dr = sodr(la, th, alp, dlt)
            tt = eandp(alt, ds)
            ts = sa(alt, ds)
            if pht < ts and ht > ts :
                sunrize_h = hh
                sunrize_m = m
            if pdr < 180 and dr > 180 :
                meridian_h = hh
                meridian_m = m
            if pht > ts and ht < ts :
                sunset_h = hh
                sunset_m = m
            pht = ht
            pdr = dr
    return (sunrize_h, sunrize_m, sunset_h, sunset_m, meridian_h, meridian_m)

# 24時時計
# メインウィンドウ
root = tk.Tk()
root.title(u'  和時計')
root.minsize(160, 160)
root.maxsize(400, 400)

# キャンバス
c0 = tk.Canvas(root, width = 200, height = 200, bg = 'lightblue')
c0.pack(expand = True, fill = tk.BOTH)

    # 時計下地
circle = c0.create_oval(5, 5, 195, 195, fill = 'lightgray', outline = 'lightgray')

    # 太陽マーク
def sun_mark():
    sun_r_h, sun_r_m, sun_s_h, sun_s_m, sun_me_h, sun_me_m = sunpos()
    # 夜
    sun_r = 18 - sun_r_h - sun_r_m / 60.0
    sunrize = sun_r * 15
    sun_s = 18 - sun_s_h - sun_s_m / 60.0
    sunset = sun_s * 15
    night = c0.create_arc(5, 5, win_size - 5, win_size - 5, start = sunrize, extent = 360 - sunrize + sunset, fill = 'darkgray', outline = 'darkgray')
    c0.tag_raise(night, circle)
    # 南中太陽
    k = 5 # 太陽マークの半径
    n = (24 - sun_me_h - sun_me_m / 60.0) * 15
    x = 100 * math.sin(rad * n)
    y = 100 * math.cos(rad * n)
    sun = c0.create_oval(x - k, y + k, x + k, y - k, fill = 'yellow', outline = 'yellow')
    return night, sun

    # 文字盤
dial0 = c0.create_text(100, 195, text = u'子', anchor = 's')
dial6 = c0.create_text(5, 100, text = u'卯', anchor = 'w')
dial12 = c0.create_text(100, 5, text = u'午', anchor = 'n')
dial18 = c0.create_text(195, 100, text = u'酉', anchor = 'e')

    # 目盛り
for i in range( 12 ):
    backboard.append(c0.create_line(i, i, 135, 135, width = 2.0))

    # 針
hour = c0.create_line(100, 100, 100, 60, fill = 'blue', width = 4.0)
min  = c0.create_line(100, 100, 100, 50, fill = 'green', width = 3.0)
sec  = c0.create_line(100, 100, 100, 45, fill = 'red', width = 2.0)

# 背景の描画
def draw_backboard():
    sun_r_h, sun_r_m, sun_s_h, sun_s_m, sun_me_h, sun_me_m = sunpos()
    r = win_size / 2
    # 円
    c0.coords(circle, 5, 5, win_size - 5, win_size - 5)
    # 夜
    c0.coords(night, 5, 5, win_size - 5, win_size - 5)
    # 南中太陽
    k = 5 # 太陽マークの半径
    n = (24 - sun_me_h - sun_me_m / 60.0) * 15
    x = r + (r - 30) * math.sin(rad * n)
    y = r + (r - 30) * math.cos(rad * n)
    c0.coords(sun, x - k, y + k, x + k, y - k)
        # 目盛(30度ピッチ)
    for i in range(12):
        n = i * 30
        x1 = r + (r - 25) * math.sin(rad * n)
        y1 = r + (r - 25) * math.cos(rad * n)
        x2 = r + (r - 25) * 4 / 5 * math.sin(rad * n)
        y2 = r + (r - 25) * 4 / 5 * math.cos(rad * n)
        c0.coords(backboard[i], x1, y1, x2, y2)
    # 文字盤
    c0.coords(dial0, r, win_size - 8)
    c0.coords(dial6, 10, r)
    c0.coords(dial12, r, 8)
    c0.coords(dial18, win_size - 8, r)

# 針を描く
def draw_hand():
    global win_size, rad, h, h_old
    t = time.localtime()
    r = win_size / 2
    rs = r * 7 / 10
    rm = r * 6 / 10
    rh = r * 5 / 10
    # 秒(1秒で6度)
    n = t[5] * 6
    x = r + rs * math.sin(rad * n)
    y = r - rs * math.cos(rad * n)
    c0.coords(sec, r, r, x, y)
    # 分(1分で6度+1秒で0.1度)
    n = t[4] * 6 + t[5] * 0.1
    x = r + rm * math.sin(rad * n)
    y = r - rm * math.cos(rad * n)
    c0.coords(min, r, r, x, y)
    # 時 (24時間表示で下が0時)
    h = t[3]
    h1 = h + 12
    n = h1 * 15 + t[4] * 0.25
    x = r + rh * math.sin(rad * n)
    y = r - rh * math.cos(rad * n)
    c0.coords(hour, r, r, x, y)
   
    sun = 0
    if h != h_old :
        sun = 1
    h_old = h
    return sun
   

# 大きさの変更
def change_size(event):
    global win_size
    w = c0.winfo_width()
    h = c0.winfo_height()
    if w < h:
        win_size = w
    else:
        win_size = h
    draw_backboard()
    sun_flag = draw_hand()

# 表示
def show_time():
    sun_flag = draw_hand()
    if sun_flag == 1 :
        night, sun = sun_mark()
        draw_backboard()
    root.after(1000, show_time)

# バインディング
root.bind('', change_size)

# 最初の起動

night, sun = sun_mark()
draw_backboard()
show_time()

# メインループ
root.mainloop()

# (C) 2015 SiRaKaWa
# 参考文献
# 太陽位置計算部分: www.hoshi-lab.info/env/solar.html
# 時計部分: www.geocities.jp/m_hiroi/light/pytk07.html
 


No comments: