原文链接:https://blog.qyadbr.top/2024/12/08/hexo9/
前言
我使用Qt制作了一个时钟应用,现在来分享一下实现的过程。
这个小项目包括了四个部分:
- 时钟(显示当前时间)
- 秒表(正向计时器,支持记录时间)
- 倒计时
- 闹钟
各种准备工作
导入
制作这个应用,我使用了一大堆QtWidgets
组件。同时,还有至关重要的time
模块。
from PySide6.QtWidgets import (QApplication,QWidget,QPushButton,QGroupBox,QLabel,
QTableWidget,QTableWidgetItem,QProgressBar,QScrollArea,
QDialog,QGridLayout,QVBoxLayout,QHBoxLayout,QSpinBox,QLineEdit,QComboBox,QSystemTrayIcon,QCheckBox)
from PySide6.QtGui import QIcon,QFont
from PySide6.QtCore import QObject,QTimer,Qt,QSize
import time
然后,建立一个QApplication
,定义一些常量。
qapp=QApplication([])
icon=QIcon('../media/images/1.png') #窗口图标
widgetslist=[] #每个模式对应一个窗格
musicUrlList=['../media/audio/1.wav'] #闹铃音乐
musicNameList=['音乐1'] #音乐名称
menubuttonnamelist=['时钟','秒表','倒计时','闹钟']
menubuttonlist=[]
mode=0 #当前的模式
#下面这段永远放在最后
qapp.exec()
建立窗口
建立一个类,继承QtWidgets.QWidget
。
class Window(QWidget):
def __init__(self,width,height,title,icon):
super().__init__()
self.setWindowTitle(QObject.tr(title))
self.resize(width,height)
self.setWindowIcon(icon)
实例化窗口对象。
mainWindow=Window(1000,600,'时钟',icon)
mainWindow.show()
功能窗格与切换按钮
我在窗口左侧做了四个按钮,分别对应四种功能。点击按钮就可以切换窗格的显示状态。
#定义按钮类
class MenuButton(QPushButton):
def __init__(self,parent,num):
super().__init__(parent)
self.num=num
self.clicked.connect(self.toggleMode)
def toggleMode(self):
global mode
widgetslist[mode].hide()
widgetslist[self.num].show()
mode=self.num
#显示
for i in range(len(menubuttonnamelist)):
button=MenuButton(mainWindow,i)
button.setText(menubuttonnamelist[i])
button.move(10,10+i*30)
button.show()
menubuttonlist.append(button)
group=QGroupBox(parent=mainWindow,title=menubuttonnamelist[i])
group.resize(890,580)
group.move(100,10)
widgetslist.append(group)
窗格尺寸自适应
当缩放窗口时,窗格的大小不会改变,因此我们要自动设置窗格的尺寸。
在Qt
中,QWidget
被缩放大小就会自动执行resizeEvent
函数,重写其内容即可。
# class Window(QWidget):
def resizeEvent(self,event):
size=self.size()
for i in widgetslist:
i.resize(size.width()-110,size.height()-20)
时间显示
这是整个项目最最简单的一部分。
组件
nowtime=time.strftime('%H:%M:%S')
labelTime=QLabel(widgetslist[0])
font=QFont('Arian',100,10)
labelTime.setFont(font)
labelTime.setText(nowtime)
更新显示
除了刷新时间,还设置了窗口自适应
def update():
size1_1=widgetslist[0].size()
nowtime=time.strftime('%H:%M:%S')
labelTime.setText(nowtime)
labelTime.move(size1_1.width()/2-size2.width()/2,size1_1.height()/2-size2.height()/2)
timer=QTimer()
timer.start(0.05)
timer.timeout.connect(update)
效果
正向计时器
这是第二个功能,需要实现一个可以开始、暂停的计时器,计时状态下可以记录,非计时状态下可以清零。
创建组件
实现它,需要一个文本显示,三个按钮,以及一个表格。
recorder=QLabel(widgetslist[1])
recorder.setText('00:00:00.000')
recorder.setFont(font)
startStopButton=QPushButton('开始',widgetslist[1])
recordButton=QPushButton('记录',widgetslist[1])
recordButton.setDisabled(True)
clearButton=QPushButton('清零',widgetslist[1])
clearButton.setDisabled(True)
recordTable=QTableWidget(0,3,widgetslist[1])
recordTable.setHorizontalHeaderLabels(['序次','间隔','总计'])
recordTable.verticalHeader().setVisible(False)
recordTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
recordTable.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
变量
startTimeStamp=0
lastEndStamp=0
lastRecordStamp=0
running=False
timeDelta=0
startTimeStamp
:计时开始的时间戳lastEndStamp
:上次暂停时的时间戳lastRecordStamp
:上次记录时的时间戳,用于计算记录间隔running
:是否正在计时timeDelta
:暂停后,将产生时间差,在计算当前时间是要减去这个时间差
获取与格式化时间戳
变量已经齐全,下一步就是获取当前的时间。获取时间可以使用下面简单的公式:
r e s = n o w − s t a r t T i m e S t a m p − t i m e D e l t a res=now-startTimeStamp-timeDelta res=now−startTimeStamp−timeDelta
这个公式要使用多次,所以封装成函数:
def getTime():
global startTimeStamp,timeDelta
return time.time()-startTimeStamp-timeDelta
获取到时间之后,将时间戳直接展示给用户肯定不现实,因此将其转换成时间字符串。同时,要传入一个参数,表示取整还是精确到小数点后三位。(后续倒计时和闹钟都不取后三位。)
def formatTime(data,round=False):
return '{:0>2d}'.format(int(data//3600))+':'+'{:0>2d}'.format((int(data)%3600)//60)+':'+'{:0>2d}'.format(int(data)%60)+('.'+'{:0>3d}'.format(int((data%1)*1000)) if not round else '')
计时控制
刚刚我创建了三个按钮组件,现在在要对应地写着三个按钮被点击是执行的函数。
开始和停止
这个函数可以控制running
变量,以及做出对应的操作:
- 设置按钮位置;
- 改变
timeDelta
- 设置按钮
def startStop():
global running,startTimeStamp,lastEndStamp,timeDelta,lastRecordStamp
if running:
lastEndStamp=time.time()
startStopButton.setText('开始')
else:
if startTimeStamp==0:
startTimeStamp=lastEndStamp=time.time()
timeDelta+=time.time()-lastEndStamp
startStopButton.setText('停止')
recordButton.setDisabled(running)
running=not running
clearButton.setDisabled(running)
记录
记录按钮被按下时,就在表格中插入一行,第一列是次数,第二列是间隔,第三列是总计时间。最后,还要设置这次记录的时间戳,以计算这一次到下一次记录的时间间隔。
def writeRecord():
global lastRecordStamp
recordTable.insertRow(0)
recordTable.setItem(0,0,QTableWidgetItem(str(recordTable.rowCount())))
if recordTable.rowCount()>1:
recordTable.setItem(0,1,QTableWidgetItem(formatTime(getTime()-lastRecordStamp)))
recordTable.setItem(0,2,QTableWidgetItem(formatTime(getTime())))
lastRecordStamp=getTime()
清除记录
清除,要把各种变量都初始化,设置时间文本的文字也为初始的。因为表格控件没有提供清空函数,所以只能逐行删除直至表格为空为止。
def clearRecord():
global startTimeStamp,lastEndStamp,timeDelta,recorder
startTimeStamp=lastEndStamp=timeDelta=0
recorder.setText('00:00:00.000')
clearButton.setDisabled(True)
while(recordTable.rowCount()>0):
recordTable.removeRow(0)
连接按钮与函数
使用按钮控件.clicked.connect(函数名)
就可以在按钮被点击时调用函数。