WinForm 畫個時鐘

2022-01-16 15:49:05 字數 4425 閱讀 9485

今天學習gdi+,試著想寫乙個模擬時鐘的小程式,原以為很簡單實現;但其實還有些複雜,特別是利用三角函式的那部分,讓我四處找資料惡補了一下高中數學才算弄清楚,現在就回顧一下這個程式吧.

程式的目的是要模擬出時鐘的效果,那首先就是要畫出這個時鐘的樣子。不考慮美觀,乙個時鐘最簡單的組成是乙個圓形的表盤,三根直線代表的時針、分針和秒針。

看起來很簡單吧,但要怎麼樣畫呢?讓我們一步一步來吧:

1.畫表盤

graphics g = this.creategraphics(); //建立乙個graphics物件

pen mypen = new pen(color.blue,1); //建立乙個自定義畫筆物件

//建立乙個正方形rect

int myrect_x = this.clientrectangle.right/4;

int myrect_y = this.clientrectangle.bottom/4;

int myrect_width;

int myrect_height;

if(this.clientrectangle.height< this.clientrectangle.width)

else

rectangle rect = new rectangle(myrect_x,myrect_y,myrect_width,myrect_height);

g.drawellipse(mypen,rect); //畫出乙個內切於矩形rect的圓

graphics.drawellipse方法有幾種過載方式,其中較常用的就是graphics.drawellipse(pen,rectangle),這個過載方法需要提供乙個rectangle引數,然後根據這個矩形的形狀內切出橢圓,若這個矩形是正方形,則切出來的是乙個正圓.

這段程式最關鍵的地方就在於得到這個矩形了,因為它間接決定了表盤的位置與大小,rectangle的建構函式需要四個引數,矩形左上角的x、y座標,寬度與高度.

這裡的clientrectangle所代表的是控制項的工作區,它也是乙個rectangle型別的物件,是指控件的邊界減去非工作區元素(如滾動條、邊框、標題欄和選單)。相應的right,bottom,width,height分別代表了右邊緣的x座標,下邊緣的y座標,寬度與高度.

為了使這個表盤適應視窗的變化,這裡將這些值全部設定為相對與clientrectangle相關屬性的值,如下圖:

這樣當視窗大小變化時,這個圓的外切矩形也隨之改變,假如這個視窗的寬度比高度大太多,或反之,則可能會出現表盤的部分超出工作區域無法顯示,為了防止這種情況,加入了條件判斷語句。如果寬度大於高度,則圓的直徑取高度值;反之則取寬度值。當然高度等於寬度時,圓是在工作區域正中間。

2.畫表針

將表盤畫好後,就可以開始畫表針了,這也是最複雜的一步

//得到中心點座標

int centerpoint_x = rect.right - rect.width/2;

int centerpoint_y = rect.bottom - rect.height/2;

point centerpoint = new point(centerpoint_x,centerpoint_y);

int s_len = rect.width*0.45; //秒針長度

int m_len = rect.width*0.35//分針長度

int l_len = rect.width*0.25 //時針長度

//得到當前時間

int h = datetime.now.hour;

int m = datetime.now.minute;

int s = datetime.now.second;

//得到秒針頂點座標

int sec_x = (int)(centerpoint.x + math.sin(6*math.pi/180*s)*s_len);

int sec_y = (int)(centerpoint.y - math.cos(6*math.pi/180*s)*s_len);

point secpoint = new point(sec_x,sec_y);

//得到分針頂點座標

int min_x = (int)(centerpoint.x + math.sin(6*math.pi/180*m)*m_len);

int min_y = (int)(centerpoint.y - math.cos(6*math.pi/180*m)*m_len);

point minpoint = new point(min_x,min_y);

//得到時針頂點座標

int hour_x = (int)(centerpoint.x + math.sin(h*30+m/2)*math.pi/180*h_len);

int hour_y = (int)(centerpoint.y - math.cos(h*30+m/2)*math.pi/180*h_len);

point hourpoint = new point(hour_x,hour_y);

//以不同的顏色連線

mypen = new pen(color.blue,1);

g.drawline(mypen,centerpoint,secpoint); //連線原點和秒針頂點

mypen = new pen(color.green,2);

g.drawline(mypen,centerpoint,minpoint); //連線原點和分針頂點

mypen = new pen(color.red,3);

g.drawline(mypen,centerpoint,hourpoint); //連線原點和時針頂點

g.dispose(); //釋放graphics物件使用的所有資源

要畫出一條直線,必須要得到這個直線的兩個頂點座標,在這個程式裡,三根表針有乙個共同的原點(表盤中心點),然後如何得到另外乙個點就是我們接下來要做的工作了

我們先來回顧一下三角函式的概念,sin值是 對邊/斜邊 得到,cos值是 鄰邊/斜邊得到,要求出這個點的座標就是求出這個三角形的兩條邊,一條邊的長度為x,另一條的長度為y。現在我們已經知道的是斜邊的長度,要求出這兩條邊,就要先知道弧度值

我們先來考慮最長的秒針,它每一秒都會移動乙個角度,這個角度我們可以計算出來:

360/60=6

得到6度,這不難理解,因為整個圓是360度,一般的時鐘在相鄰小時之間又有5個刻度,所以一共是12*5=60個刻度,然後用360/60=6得到秒針一秒走6度,在程式裡必須要將角度轉化成弧度才能進行sin運算,所以還要乘以 pi/180(pi為圓周率)

有了這個弧度值,我們就可以得到每一秒秒針的頂點座標了(以在第一區間考慮):

sec_x = centerpoint.x + sin(6*pi/180)*當前秒鐘值*s_len

sce_y = centerpoint.y - cos(6*pi/180)*當前秒鐘值*s_len

接下來處理時針,因為我們這只是個很簡單的模擬,所以就不需要對細節作過多的處理,就讓它每過一分鐘就跳到下乙個刻度,這樣求時針的頂點座標和求秒針的就是一樣的演算法

min_x = centerpoint.x + sin(6*pi/180)*當前分鐘值*m_len

min_y = centerpoint.y - cos(6*pi/180)*當前分鐘值*m_len

時針的處理方式要不同,因為時針的取值只有12個數字,而分鐘和秒針都有60個數字,如果以之前的方式處理時針,效果就是時針到點的那一瞬間它一下子轉30度,當然不會有這樣效果的時鐘。那麼我們該如何處理這個棘手的時針呢?

假如現在是06:45:30,在整6點時它的度數是30*6,整7點是30*7,為了不至於將改變全在最後刻發生,我們加入時針值來參與計算,這樣當前時間就是h*30+m/2,m為60時 m/2=30 ,也就是 m/2這個值在0--30之間,當角度變化為30度時,就指到了下乙個整點

這樣我們得到了時針的演算法

hour_x = centerpoint.x + sin((h*30+m/2)*pi/180)*h_len

hour_y = centerpoint.y - cos((h*30+m/2)*pi/180)*h_len

最關鍵的**處理了,剩下的工作就簡單了,我們在程式裡加入乙個標籤lbltime,來顯示當前時間

string timeinstring = "";

int hour = datetime.now.hour;

int min = datetime.now.minute;

int sec = datetime.now.second;

timeinstring = (hour<10)? "0"+hour.tostring():hour.tostring();

timeinstring += (min<10)? "0"+min.tostring():min.tostring();

timeinstring == (sec<10)? "0"+sec.tostring():sec.tostring();

lbltime.text = timeinstring;

畫動態時鐘

import cv2 import math import datetime import numpy as np margin 5 上下左右邊距 radius 220 圓的半徑 center center x,center y 225,225 圓心 1.新建乙個畫板並填充成白色 img np.ze...

WinForm畫網格並填充顏色

因為研究codecombat上的最後一題,自己嘗試分解題目,然後想到需要畫網格,還有最優化的方法 源 如下 using system using system.collections.generic using system.componentmodel using system.data usin...

通過canvas畫布畫動態時鐘

效果圖 步驟 1.canvas是乙個畫圖的容器,裡面沒有圖形,需要你自己加上圖形 先定義好canvas的寬 高 2.定義js函式實現時鐘動畫,具體步驟如下 1 獲取畫布的id,然後通過getcontext 方法返回乙個用於在畫布上繪圖的環境。語法 canvas.getcontext contexti...