用 PyQt5 制作動態(tài)鐘表

前言

大家好获雕,我是小張~

記得小時候,家里只有一個鐘表用來看時間(含有時針收捣、分針届案、秒針的那種),掛在墻上噠噠噠響個不停罢艾,現(xiàn)在生活條件好了楣颠、基本人手一部手機图焰,看時間也不再依靠表了奋隶,而今天的文章內(nèi)容就是與這類鐘表相關(guān);

本篇文章將用 Python 同時繪制兩種類型的表沟蔑;一個是上面提到的含有時春锋、分矫膨、秒針的鐘表(為了方便,下面統(tǒng)稱為老式鐘表),一個是電子表侧馅,最終運行效果如下

Clock

環(huán)境配置

程序中用到的Python包

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n9" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">PyQt5
math
sys</pre>

實現(xiàn)思路

實現(xiàn)思路分為大致分為三個部分:老式鐘表制作危尿、電子表制作、兩表合并為一個界面

老式鐘表制作

整體來看老式鐘表界面馁痴,有以下幾種圖案:時針谊娇、分針、秒針罗晕、1-12小時刻度济欢、每格小刻度、每5格一大刻度

image-20210226225519399

在鐘表運行時小渊,時針法褥、分針、秒針 隨時間變化以界面中心為圓點逆時針旋轉(zhuǎn)一定的角度粤铭,這個角度可以計算出來的

為了方便挖胃,整個界面效果我用 PyQt5 中的 QPainter 控件來實現(xiàn),這個控件其實就是一個繪制器梆惯,用來繪制界面上的元素

時鐘運行軌跡其實就是把每一秒的畫面拼接在一起酱鸭,最后組成一個連貫的時間序列圖像,因此重寫了 paintEvent 函數(shù)垛吗,繪制每一秒實時圖像凹髓;

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n18" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> def paintEvent(self, event):

hour_points = [QPoint(5,8),QPoint(-5,8),QPoint(0,-30)]
minute_points = [QPoint(5,8),QPoint(-5,8),QPoint(0,-65)]
second_points = [QPoint(5,8),QPoint(-5,8),QPoint(0,-80)]

hour_color = QColor(200,100,0,200)
minute_color = QColor(0,127,127,150)
second_color = QColor(0,160,230,150)

min_len = min(self.width(),self.height())
time = QTime.currentTime() #獲取當前時間
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.translate(self.width()/2,self.height()/2)#平移到窗口中心
painter.scale(min_len/200.0,min_len/200.0) #進行尺度縮放

----------繪制時針------------

painter.setPen(Qt.NoPen)
painter.setBrush(hour_color)#顏色
painter.save()

根據(jù) 1小時時= 30°,水品方向逆時針旋轉(zhuǎn)時針

painter.rotate(30.0*((time.hour()+time.minute()/60.0)))
painter.drawConvexPolygon(QPolygon(hour_points))
painter.restore() # save 退出怯屉,可重新設(shè)置畫筆

painter.setPen(hour_color)

繪制小時線(360/12 = 30度)

for i in range(12):
painter.drawLine(88,0,96,0)#繪制水平線
painter.rotate(30.0)# 原有旋轉(zhuǎn)角度上進行旋轉(zhuǎn)蔚舀;

radius = 100 # 半徑
font = painter.font()
font.setBold(True)
painter.setFont(font)
pointSize = font.pointSize()#字體大小

print(pointSize)

繪制小時文本

for i in range(12):
nhour = i + 3 # 從水平 3 點進行繪制
if(nhour>12):
nhour -= 12
painter.drawText(self.textRectF(radius0.8,pointSize,i30),Qt.AlignCenter,str(nhour))

繪制分針;

painter.setPen(Qt.NoPen)
painter.setBrush(minute_color)
painter.save()

1分鐘為6°,

painter.rotate(6.0*(time.minute()+time.second()/60.0))
painter.drawConvexPolygon(QPolygon(minute_points))
painter.restore()

繪制分針線

painter.setPen(minute_color)
for i in range(60):
if(i%5 !=0):
painter.drawLine(92,0,96,0)
painter.rotate(6.0)

繪制秒針

painter.setPen(Qt.NoPen)
painter.setBrush(second_color)
painter.save()

繪制秒線

painter.rotate(6.0*time.second())
painter.drawConvexPolygon(QPolygon(second_points))
painter.restore()

painter.setPen(second_color)
for i in range(360):
if(i%5!=0 or i%30!=0):#繪制
painter.drawLine(94,0,96,0)
painter.rotate(1.0)#旋轉(zhuǎn)</pre>

再設(shè)置一個時間計時控件锨络,利用信號槽機制連接界面赌躺,每一秒更新一次界面

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n20" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> self.timer = QTimer() # 定時器
self.timer.timeout.connect(self.update)
self.timer.start(1000) # 每1s 更新一次</pre>

電子表制作

電子表制作相對要比 老式鐘表制作 要簡單地多,制作過程中用到 QLCDNumber 控件

image-20210226230452359

QLDNumber 控件用來預(yù)覽數(shù)字羡儿,但上面的樣式讓數(shù)字看起來科技感更強礼患,作為電子表預(yù)覽是非常不錯的選擇!

image-20210226222436801

使用之前需對該控件的屬性做一下微調(diào)掠归,例如字體樣式缅叠,控件內(nèi)占據(jù)字符個數(shù)、邊框?qū)傩缘龋?/p>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n27" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">self.lcdNumber = QtWidgets.QLCDNumber(Form)
self.lcdNumber.setGeometry(QtCore.QRect(0, 0, 250, 50))
self.lcdNumber.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
self.lcdNumber.setFrameShape(QtWidgets.QFrame.NoFrame)
self.lcdNumber.setSmallDecimalPoint(False)
self.lcdNumber.setDigitCount(8)
self.lcdNumber.setSegmentStyle(QtWidgets.QLCDNumber.Flat)
self.lcdNumber.setProperty("value", 2021.0)
self.lcdNumber.setObjectName("lcdNumber")</pre>

電子表運行效果也是借助于計時控件虏冻,每秒更新一次當前界面的時間肤粱,但這里沒用到 QPainter

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n29" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> self.lcdNumber.display('00:00:00')
time_slot =QTimer(self)

time_slot.setInterval(1000)

time_slot.start()

time_slot.timeout.connect(self.event_1)
time_slot.start(1000)
def event_1(self):
time_format = QTime.currentTime()
time_format = time_format.toString("hh:mm:ss")
self.lcdNumber.display(time_format)
QApplication.processEvents()</pre>

合并兩表界面

兩個表界面創(chuàng)建完畢之后,最后用一個 Widget 作為基類厨相,利用 Qt 的水平布局把兩個表水平拼接在一起领曼,形成了最終的效果

image-20210226225435210

核心代碼

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n34" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> self.label1 = Clock_paint()
self.label2 = MyWidget()
self.horizon_layout = QHBoxLayout()
self.horizon_layout.addWidget(self.label1)
self.horizon_layout.addWidget(self.label2)
self.setLayout(self.horizon_layout)
self.setWindowTitle('時鐘--《公號:小張Python》')
self.setWindowIcon(QIcon('clock.jpg'))</pre>

總結(jié)

本文中的案例算是 PyQt5 的一個小應(yīng)用鸥鹉,PyQt5 是 Qt 在 Python 語言中的封裝,制作 GUI界面時 個人非常推薦這個庫悯森,相對于 tk 我更喜歡前者宋舷,因為它GUI制作靈活性更強绪撵,后續(xù)打算出更多小案例來介紹 PyQt5 的相關(guān)用法瓢姻,敬請期待!

好了以上就是本篇文章的全部內(nèi)容音诈,最后感謝大家的閱讀幻碱,我們下期見~

源碼獲取

本文涉及到的源碼獲取方式,關(guān)注微信公號细溅,小張Python褥傍,后臺回復(fù)關(guān)鍵字 210227 即可

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市喇聊,隨后出現(xiàn)的幾起案子恍风,更是在濱河造成了極大的恐慌,老刑警劉巖誓篱,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朋贬,死亡現(xiàn)場離奇詭異,居然都是意外死亡窜骄,警方通過查閱死者的電腦和手機锦募,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來邻遏,“玉大人糠亩,你說我怎么就攤上這事∽佳椋” “怎么了赎线?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長糊饱。 經(jīng)常有香客問我垂寥,道長,這世上最難降的妖魔是什么济似? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任矫废,我火速辦了婚禮,結(jié)果婚禮上砰蠢,老公的妹妹穿的比我還像新娘蓖扑。我一直安慰自己,他們只是感情好台舱,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布律杠。 她就那樣靜靜地躺著潭流,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柜去。 梳的紋絲不亂的頭發(fā)上灰嫉,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音嗓奢,去河邊找鬼讼撒。 笑死,一個胖子當著我的面吹牛股耽,可吹牛的內(nèi)容都是我干的根盒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼物蝙,長吁一口氣:“原來是場噩夢啊……” “哼炎滞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起诬乞,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤册赛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后震嫉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體森瘪,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年责掏,在試婚紗的時候發(fā)現(xiàn)自己被綠了柜砾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡换衬,死狀恐怖痰驱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瞳浦,我是刑警寧澤担映,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叫潦,受9級特大地震影響蝇完,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矗蕊,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一短蜕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧傻咖,春花似錦朋魔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孙援。三九已至,卻和暖如春扇雕,著一層夾襖步出監(jiān)牢的瞬間拓售,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工镶奉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留础淤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓腮鞍,卻偏偏與公主長得像值骇,于是被迫代替她去往敵國和親莹菱。 傳聞我的和親對象是個殘疾皇子移国,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容