HSPでアナログ時計を作る
1.概要
別記事で「HSPで万年カレンダを作る」を
公開していますが、今回もプログラミングの題材としては定番である「アナログ時計」をHSPスクリプト言語で作ります。
HSP言語について、興味のある人は公式サイトで確認して見て下さい。
今回紹介する時計は、何か特別な機能があるものではなく、ただ単に時計用の文字盤画像に針がついて時を刻むだけのものです。
2.ソースコード プログラミングで時計を作る場合は、デジタル時計とアナログ時計の二種類があります。デジタル時計は、テキスト数字のものと数字画像を利用した ものがあります。
まずは、時計を作るための基本となるデジタル(テキスト)時計を参考として紹介します。 ソースコードは、下記の通りですが実際に実用的なものとして筆者が利用しています。そのため、コードがやや長めとなっています。 前半部分は、画面の移動、終了処理をするためのポップアップメニューの表示、タイマー処理、二重起動防止のために必要なWindowsAPIの定義部分で、 時計として必要な部分は、85行目から111行目までと、画面をマウスの左クリックで移動するための処理としての、133行目から149行目までです。 もうひとつのポイントとしては、タイマー割り込み処理で、マウスの左ボタンクリックで画面移動時でも時計が止まらないようにする処理部分です。 アナログ時計も時計の描画部分を除けば処理ルーチンは、ほぼ同じです。 あと、画面の移動位置を保存して起動時に反映させるためにSQLiteを使っています。終了は、マウスを右クリックするとポップアップメニューで終了できます。
HSP言語は、長年愛用して利用していますが、最新のバージョンなどに目を通していないので新しい機能アップした命令など は利用していません。昔からある命令での記述しかできません。進化し過ぎてだんだん、取り残されつつあります。 5.ダウンロード 提供するソースコードのライセンスは、CC0 (クレジット表示不要、改変可、商用可) とします。自由に利用して頂いてかまいません。 アナログ時計(agclock.exe)は下記からダウンロードして下さい。 そのまま実行形式(exe)ファイルが作成されるように画像やsampleアイコンなども同梱しています。
ダウンロード
2.ソースコード プログラミングで時計を作る場合は、デジタル時計とアナログ時計の二種類があります。デジタル時計は、テキスト数字のものと数字画像を利用した ものがあります。
まずは、時計を作るための基本となるデジタル(テキスト)時計を参考として紹介します。 ソースコードは、下記の通りですが実際に実用的なものとして筆者が利用しています。そのため、コードがやや長めとなっています。 前半部分は、画面の移動、終了処理をするためのポップアップメニューの表示、タイマー処理、二重起動防止のために必要なWindowsAPIの定義部分で、 時計として必要な部分は、85行目から111行目までと、画面をマウスの左クリックで移動するための処理としての、133行目から149行目までです。 もうひとつのポイントとしては、タイマー割り込み処理で、マウスの左ボタンクリックで画面移動時でも時計が止まらないようにする処理部分です。 アナログ時計も時計の描画部分を除けば処理ルーチンは、ほぼ同じです。 あと、画面の移動位置を保存して起動時に反映させるためにSQLiteを使っています。終了は、マウスを右クリックするとポップアップメニューで終了できます。
;****************************************************************************
;*
;* 文字列型のデジタル時計 (strtokei.hsp) Ver1.0
;*
;****************************************************************************
#include "sqlele.hsp"
;***** 実行ファイル自動作成 ****
#packopt type 0
#packopt name "strtokei"
#packopt runtime "hsprt"
#packopt manifest "app.manifest"
#packopt icon "script.ico"
#packopt hide 1
#uselib "user32.dll"
#func GetWindowRect "GetWindowRect" int,int
#func GetSystemMetrics "GetSystemMetrics" int
#func MoveWindow "MoveWindow" int,int,int,int,int,int
#func CreatePopupMenu "CreatePopupMenu"
#func AppendMenu "AppendMenuA" sptr,sptr,sptr,sptr
#func TrackPopupMenu "TrackPopupMenu" sptr,sptr,sptr,sptr,sptr,sptr,sptr
#func DestroyMenu "DestroyMenu" sptr
#func global SetWindowLong "SetWindowLongA" int , int , int
#cfunc global GetWindowLong "GetWindowLongA" int , int
#func SetTimer "SetTimer" int, int, int, int
#func KillTimer "KillTimer" int, int
#define WM_TIMER 0x0113
#define ID_TIMER 1
#define global GWL_STYLE 0xFFFFFFF0
#define global WS_SYSMENU 0x00080000
#define global WS_MAXIMIZEBOX 0x00010000
#define global WS_MINIMIZEBOX 0x00020000
#enum IDM_ITEM1 = 1
#define SM_CXSCREEN $00000000
#define SM_CYSCREEN $00000001
#define obj_wmnclbtndown sendmsg hwnd,$00A1,2,0
#module
#uselib "kernel32.dll"
#cfunc CreateMutex "CreateMutexA" int,int,sptr
#cfunc GetLastError "GetLastError"
;***** 二重起動防止 *****
#deffunc wexapend str prm1
strname=prm1
;***** 名前の文字列が省略された場合 *****
if strlen(strname)==0 : strname="HSP340ONIWND" ;Default String
ret=CreateMutex(0,1,strname)
;二重起動か?
if GetLastError()==0 : return 0 ;同じジョブが起動していない
if GetLastError()==183 : return 1 ;既に起動している
return -1
#global
;***** 起動ディレクトリ取得 *****
sdim Startdir,512
if hspstat&1=0 { Startdir=dir_exe+"¥¥" : chdir dir_exe
} else {
Startdir=dir_cur+"¥¥"
}
chdir Startdir
;***** データベース存在確認 *****
exist Startdir+"setting¥¥strtokei.ini"
if strsize ==-1 {
dialog "データベースファイルが見つかりません。",0 : end
}
await
wexapend "strtokei" : if stat : end
;***** データベースオープン *****
sql_open Startdir+"setting¥¥strtokei.ini"
;***** 画面表示位置設定 *****
sql_q "SELECT * FROM TValu WHERE ID = "+1
xpos=sql_i("wxpos") : ypos=sql_i("wypos")
if (xpos<0 or xpos>ginfo_dispx) : xpos=0
if (ypos<0 or ypos>ginfo_dispy) : ypos=0
bgscr 0,300,80,,xpos,ypos
SetWindowLong hwnd,GWL_STYLE,$B0000|GetWindowLong(hwnd,GWL_STYLE)
color 0,180,200 : boxf
title "デジタル時計" : font "メイリオ",50,1
gsel 0,1
CreatePopupMenu
hMenu.1 = stat
AppendMenu hMenu.1, 0, IDM_ITEM1, "終了"
onexit *owari
oncmd gosub *OnTimer, WM_TIMER
onclick gosub *move
SetTimer hwnd, ID_TIMER, 500, 0
stop
*OnTimer
color 0,180,200 : boxf 9,15,290,70
hour=strf("%02d",gettime(4)) ;時
min =strf("%02d",gettime(5)) ;分
sec =strf("%02d",gettime(6)) ;秒
redraw 0
color 255,255,255
pos 30,6 : mes ""+hour+":"+min+":"+sec
hour=0 : min=0 : sec=0
redraw 1,5,15,290,80
await 50
redraw 0,5,15,290,80 ;画面ちらつき防止対策
return
*owari
await
KillTimer hwnd, ID_TIMER
;***** 終了処理 (DB CLOSE) *****
wx=str(ginfo_wx1) ; 現在の画面左上 X 座標取得
wy=str(ginfo_wy1) ; 現在の画面左上 y 座標取得
;***** 終了位置をDBへ保存 *****
sql_q "UPDATE TValu SET wxpos=" + prm_text(wx) + ", wypos=" + prm_text(wy)+ " WHERE ID="+1
sql_close
end
*move
;***** マウスの右クリックボタンで終了 *****
if wparam==2 {
;***** ポップアップメニュー表示 *****
TrackPopupMenu hMenu.1, $100, ginfo(0), ginfo(1), 0, hwnd, 0
if stat = 0 : return
if stat = 1 : goto *owari
return
}
;***** マウスの左クリックボタンで移動 *****
if wparam == 1 {
obj_wmnclbtndown
;***** 画面終端位置の取得と制御 *****
dim rc,4
GetWindowRect hwnd,varptr(rc)
w = rc(2)-rc(0)
h = rc(3)-rc(1)
GetSystemMetrics SM_CXSCREEN : CSX=stat
GetSystemMetrics SM_CYSCREEN : CSY=stat
if rc(0)<0 : rc(0)=0
if rc(1)<0 : rc(1)=0
if rc(2)>CSX : rc(0)=CSX-w
if rc(3)>CSY : rc(1)=CSY-h-30
MoveWindow hwnd,rc(0),rc(1),w,h,1
}
return
次は、今回のテーマであるアナログ時計です。ソースコードは、デジタル時計とほぼ同じです。メイン部分は、
146行目から182行目までです。時計用の文字盤画像サイズは、200px×200pxとして作成しています。このサイズがウィンドウ画面となります。
文字盤画像を変更したい場合は、129行目と130行目に文字盤サイズと画像の読み込み部分がありますので、画像ファイル名等を変更して下さい。
文字盤画像を複数枚準備して、好きなものに変更できるように改造すれば、もう少し見栄えがするもになると思います。
また、応用として、文字盤画像の代わりにマスコットなどの画像に差し換えてメインの時計処理部分などを変更すれば、デスクトップマスコットの
ようなものも作成できると思います。尚、アナログ時計の終了は、マウスを右クリックするとポップアップメニューで終了できます。
;****************************************************************************
;*
;* アナログ時計 (agclock.hsp) Ver1.0
;*
;* <処理の概要>
;* 本プログラムは、デスクトップアクセサリとしてのアナログ時計である。
;* 文字盤に画像を利用して、透明ウィンドウで作成している。
;*
;* 参考サイト : HSP プログラミング入門 - アナログ時計
;* アナログ時計 Ver 1.0 H.I. at Meikai
;* ※アナログ時計の基本部分は、下記サイト様のサンプルを流用させて
;* 頂きましたが、サイトは閉鎖されていて見られません。
;* 有用なサンプルの公開に感謝致します。
;* http://www.geocities.co.jp/SiliconValley-Cupertino/7774/HSPlesson/index.html
;*
;****************************************************************************
#include "sqlele.hsp"
#packopt type 0
#packopt name "agclock"
#packopt runtime "hsprt"
#packopt manifest "app.manifest"
#packopt icon "sample.ico"
#packopt hide 1
#pack "mojiban.png"
#uselib "user32.dll"
#func SetWindowLong "SetWindowLongA" int,int,int
#cfunc GetWindowLong "GetWindowLongA" int,int,int
#func SetLayeredWindowAttributes "SetLayeredWindowAttributes" int,int,int,int
#func GetWindowRect "GetWindowRect" int,int
#func GetSystemMetrics "GetSystemMetrics" int
#func MoveWindow "MoveWindow" int,int,int,int,int,int
#func CreatePopupMenu "CreatePopupMenu"
#func AppendMenu "AppendMenuA" sptr,sptr,sptr,sptr
#func TrackPopupMenu "TrackPopupMenu" sptr,sptr,sptr,sptr,sptr,sptr,sptr
#func DestroyMenu "DestroyMenu" sptr
#const LWA_COLORKEY 1 ; 透明色を指定
#const LWA_ALPHA 2 ; アルファー値を指定
#const GWL_EXSTYLE -20 ; 拡張ウィンドウスタイル
#const WS_EX_LAYERED $00080000 ; 透明なウィンドウ属性
#define global GWL_STYLE 0xFFFFFFF0
#define global WS_SYSMENU 0x00080000
#define global WS_MAXIMIZEBOX 0x00010000
#define global WS_MINIMIZEBOX 0x00020000
#enum IDM_ITEM1 = 1
#define SM_CXSCREEN $00000000
#define SM_CYSCREEN $00000001
#define obj_wmnclbtndown sendmsg hwnd,$00A1,2,0
#module
#uselib "kernel32.dll"
#cfunc CreateMutex "CreateMutexA" int,int,sptr
#cfunc GetLastError "GetLastError"
;***** 二重起動防止 *****
#deffunc wexapend str prm1
strname=prm1
;***** 名前の文字列が省略された場合 *****
if strlen(strname)==0 : strname="HSP340ONIWND" ;Default String
ret=CreateMutex(0,1,strname)
;二重起動か?
if GetLastError()==0 : return 0 ;同じジョブが起動していない
if GetLastError()==183 : return 1 ;既に起動している
return -1
#global
;***** 線(line)の太さを変更 *****
#define pLine(%1=0, %2=0, %3=ginfo_cx, %4=ginfo_cy, %5=1) _pLine %1, %2, %3, %4, %5
#module
#uselib "gdi32.dll"
#func CreatePen "CreatePen" int, int, int
#func SelectObject "SelectObject" int,int
#func Polyline "Polyline" int, var, int
#func DeleteObject "DeleteObject" int
#define ctype RGB(%1,%2,%3) ((%3*65536)+(%2*256)+%1)
#deffunc _pLine int _ex, int _ey, int _sx, int _sy, int _size
CreatePen 0, _size, RGB(ginfo_r, ginfo_g, ginfo_b)
hPen = stat
if( stat == 0 ){
dialog "ペン設定に失敗しました。"
return 1
}
dim p, 4
p = _ex, _ey, _sx, _sy
SelectObject hdc, hPen
hOldPen = stat
Polyline hdc, p, 2
if( stat == 0 ){
dialog "描画に失敗しました。"
return 1
}
DeleteObject hPen
SelectObject hdc, hOldPen
pos _ex, _ey
return 0
#global
;***** 起動ディレクトリ取得 *****
sdim Startdir,512
if hspstat&1=0 { Startdir=dir_exe+"¥¥" : chdir dir_exe
} else {
Startdir=dir_cur+"¥¥"
}
chdir Startdir
;***** データベース存在確認 *****
exist Startdir+"setting¥¥agclock.ini"
if strsize ==-1 {
dialog "データベースファイルが見つかりません。",0 : end
}
await
wexapend "agclock" : if stat : end
;***** データベースオープン *****
sql_open Startdir+"setting¥¥agclock.ini"
;***** 画面表示位置設定 *****
sql_q "SELECT * FROM TValu WHERE ID = "+1
xpos=sql_i("wxpos") : ypos=sql_i("wypos")
if (xpos<0 or xpos>ginfo_dispx) : xpos=0
if (ypos<0 or ypos>ginfo_dispy) : ypos=0
W=200 : H=200
buffer 2,W,H,0 : picload "mojiban.png",0
bgscr 0,W,H,,xpos,ypos
;***** 透明ウィンドウを作成 *****
SetWindowLong hWnd,-20,GetWindowLong(hwnd,-20)|$80000
;***** 最小化できるようにスタイル変更 *****
SetWindowLong hwnd,GWL_STYLE,$B0000|GetWindowLong(hwnd,GWL_STYLE)
SetLayeredWindowAttributes hwnd,$010101,$FF,1
color$01,$01,$01:boxf
syscolor 1 : boxf : title "アナログ時計"
gsel 0,1
CreatePopupMenu
hMenu.1 = stat
AppendMenu hMenu.1, 0, IDM_ITEM1, "終了"
onexit *owari
onclick gosub *move
;***** 時計本体の描画 *****
pi = 3.141592653
dsita = 2.0*pi/60.0
xc=W/2
yc=H/2
rx=100
ry=100
repeat
;***** 文字盤画像コピー *****
pos 0, 0 : gmode 2 : gcopy 2,0,0,200,200
color 255,255,255
circle xc-8,yc-8,xc+8,yc+8,1
;***** 分針 *****
min=gettime(5)
nmind=double(min)
x=xc+double(rx-15)*sin(nmind*dsita)
y=yc-double(ry-15)*cos(nmind*dsita)
redraw 0:_pLine xc,yc,x,y,3 : redraw 1
;***** 時針 *****
hour=gettime(4)
nhourd=double(hour)+nmind/60
x=xc+double(rx-40)*sin(nhourd*dsita*5.0)
y=yc-double(ry-40)*cos(nhourd*dsita*5.0)
redraw 0:_pLine xc,yc,x,y,6 : redraw 1
;***** 秒針 *****
sec=gettime(6)
nsecd=double(sec)
x=xc+double(rx-5)*sin(nsecd*dsita)
y=yc-double(ry-5)*cos(nsecd*dsita)
line xc,yc,x,y
redraw 1 : wait 50 : redraw 0
;***** 時計盤の内部を塗りつぶす *****
syscolor 1
circle xc-rx,yc-ry,xc+rx,yc+ry,1
loop
stop
*owari
await
;***** 終了処理 (DB CLOSE) *****
wx=str(ginfo_wx1) ; 現在の画面左上 X 座標取得
wy=str(ginfo_wy1) ; 現在の画面左上 y 座標取得
;***** 終了位置をDBへ保存 *****
sql_q "UPDATE TValu SET wxpos=" + prm_text(wx) + ", wypos=" + prm_text(wy)+ " WHERE ID="+1
sql_close
end
*move
;***** マウスの右クリックボタンで終了 *****
if wparam==2 {
;***** ポップアップメニュー表示 *****
TrackPopupMenu hMenu.1, $100, ginfo(0), ginfo(1), 0, hwnd, 0
if stat = 0 : return
if stat = 1 : goto *owari
return
}
;***** マウスの左クリックボタンで移動 *****
if wparam == 1 {
obj_wmnclbtndown
;***** 画面終端位置の取得と制御 *****
dim rc,4
GetWindowRect hwnd,varptr(rc)
w = rc(2)-rc(0)
h = rc(3)-rc(1)
GetSystemMetrics SM_CXSCREEN : CSX=stat
GetSystemMetrics SM_CYSCREEN : CSY=stat
if rc(0)<0 : rc(0)=0
if rc(1)<0 : rc(1)=0
if rc(2)>CSX : rc(0)=CSX-w
if rc(3)>CSY : rc(1)=CSY-h-30
MoveWindow hwnd,rc(0),rc(1),w,h,1
}
return
HSP言語は、長年愛用して利用していますが、最新のバージョンなどに目を通していないので新しい機能アップした命令など は利用していません。昔からある命令での記述しかできません。進化し過ぎてだんだん、取り残されつつあります。 5.ダウンロード 提供するソースコードのライセンスは、CC0 (クレジット表示不要、改変可、商用可) とします。自由に利用して頂いてかまいません。 アナログ時計(agclock.exe)は下記からダウンロードして下さい。 そのまま実行形式(exe)ファイルが作成されるように画像やsampleアイコンなども同梱しています。
ダウンロード


コメント
コメントを投稿