环境:win7_x64 Qt5.9.1 Qt Creator4.3.1
1、打开Qt Creator,创建一个GUI应用,我一般喜欢使用Dialog做基类。
2、构造函数中加入:setWindowFlags(Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::Dialog);
是的窗口有最小化按钮和关闭按钮。
3、打开ui文件来设计界面:需要拖入三个QSpinBox控件来分别表示时分秒三个数字。各自选中这些控件,在右下角属性中找到最大值和最小值的属性分别设置这些控件的最大值和最小值。maximum和minimum。我这里设置时控件是0-23,分控件是0-59,秒控件是0-59。
4、拖入一个按钮控件QPushButton。按钮默认文字为start,右键-转到槽。编辑clicked信号槽。来实现计时器的开始和取消功能。
if (ui->spinBox->value() == 0 &&
ui->spinBox_2->text() == "0" &&
ui->spinBox_3->text() == "0")
{
return;
}
int nH = ui->spinBox->text().toInt();
int nM = ui->spinBox_2->text().toInt();
int nS = ui->spinBox_3->text().toInt();
if (m_bRunTask == false)
{
m_bRunTask = true;
ui->pushButton->setText("CANCEL");
if (m_ptrTimer == NULL)
{
m_ptrTimer = new CTimerThread(nH,nM,nS);
m_ptrTimer->start();
connect(m_ptrTimer,SIGNAL(timeOut(int,int,int)),this,SLOT(on_timeout(int,int,int)));
}
}
else
{
ui->pushButton->setText("START");
m_bRunTask = false;
m_ptrTimer->terminate();
m_ptrTimer->wait();
delete m_ptrTimer;
m_ptrTimer = NULL;
ui->spinBox->setValue(0);
ui->spinBox_2->setValue(0);
ui->spinBox_3->setValue(0);
}
其中CTimerThread为一个继承类,代码如下:
class CTimerThread : public QThread
{
Q_OBJECT
public:
CTimerThread(int nH,int nM,int nS):m_nH(nH),m_nM(nM),m_nS(nS){}
virtual void run() {
while (true)
{
QThread::sleep(1);
if (m_nS > 0)
{
m_nS--;
}
else
{
if (m_nM > 0)
{
m_nM--;
m_nS = 59;
}
else
{
if (m_nH > 0)
{
m_nH--;
m_nM = 59;
m_nS = 59;
}
else
{
//都变成0了,时间到了,线程退出
break;
}
}
}
emit timeOut(m_nH,m_nM,m_nS);
}
}
signals:
void timeOut(int nH,int nM,int nS);
private:
int m_nH;
int m_nM;
int m_nS;
};
可以看到线程中每次时间变化都会触发一个timeOut的自定义信号,信号中会带上现在的时间数值。那么这个信号肯定是要主界面利用槽函数接收然后来更新数字到响应的三个QSpinBox控件上的。
槽函数如下:
void Dialog::on_timeout(int nH, int nM, int nS)
{
if (nH == 0 &&
nM == 0 &&
nS == 0) {
activateWindow();
showNormal();
raise();
ui->spinBox_3->setValue(nS);
ui->pushButton->setText("START");
m_bRunTask = false;
m_ptrTimer = NULL;
QMessageBox::information(NULL,"","TIME'S UP!",QMessageBox::Ok);
return;
}
ui->spinBox->setValue(nH);
ui->spinBox_2->setValue(nM);
ui->spinBox_3->setValue(nS);
}
5、构造函数中加入以下代码来实现托盘功能:
QSystemTrayIcon* ptrTrayIcon = new QSystemTrayIcon(this);//托盘
connect(ptrTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
QMenu* ptrMenu = new QMenu(this);//菜单
QAction* ptrAction = new QAction(this);//菜单项
ptrAction->setText("QUIT");
connect(ptrAction,SIGNAL(triggered()),this,SLOT(on_quit()));
ptrMenu->addAction(ptrAction);
ptrTrayIcon->setContextMenu(ptrMenu);
QIcon icon(":/timer/ooopic_1521598817.png");
ptrTrayIcon->setToolTip("timer is running");
ptrTrayIcon->setIcon(icon);
ptrTrayIcon->show();
m_ptrTray = ptrTrayIcon;
6、重载关闭窗口事件:
void Dialog::closeEvent(QCloseEvent* event)
{
event->ignore();
this->hide();
m_ptrTray->showMessage("Timer","timer is running");
}
这样点击关闭的叉叉按钮就不会退出了。只是退到托盘状态
7、实现托盘菜单的信号槽函数:
void Dialog::on_quit()
{
qApp->quit();
}
8、实现托盘的信号槽函数:
void Dialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
{
switch (reason)
{
case QSystemTrayIcon::Trigger:
showNormal();
break;
case QSystemTrayIcon::DoubleClick:
break;
case QSystemTrayIcon::MiddleClick:
break;
default:
break;
}
}
头文件预览:
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void closeEvent(QCloseEvent* event);
private slots:
void on_pushButton_clicked();
void on_timeout(int nH,int nM,int nS);
void on_quit();
void iconActivated(QSystemTrayIcon::ActivationReason reason);
private:
Ui::Dialog *ui;
bool m_bRunTask;
CTimerThread* m_ptrTimer;
QSystemTrayIcon* m_ptrTray;
};