HSPでアナログ時計を作る

1.概要

別記事で「HSPで万年カレンダを作る」を 公開していますが、今回もプログラミングの題材としては定番である「アナログ時計」をHSPスクリプト言語で作ります。 HSP言語について、興味のある人は公式サイトで確認して見て下さい。 今回紹介する時計は、何か特別な機能があるものではなく、ただ単に時計用の文字盤画像に針がついて時を刻むだけのものです。


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アイコンなども同梱しています。

ダウンロード

コメント

このブログの人気の投稿

Excelアドインで日本語形態素解析

階層構造JSONファイルの作成

HSPでコマンドプロンプトを制御する

TOP