- Ubuntu 20.04
- QT6.0
这里的基类选择 Wigets,


pro 配置文件添加 sql 模块,需要用到 sqlite,
QT += sql


- // connection.h
- #ifndef CONNECTION_H
- #define CONNECTION_H
-
- #include
- #include
- #include
- #include
- #include
-
- static bool createConnection()
- {
- QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
- db.setDatabaseName(":memory:");
- //db.setDatabaseName("ikun.db");
-
- bool res = db.open();
- if (res)
- {
- qDebug() << "open ikun.db ok !";
- }
- else
- {
- qDebug() << "open ikun.db fail: " << db.lastError().text();
- }
-
- QSqlQuery query;
- if(!query.exec("create table task_history(id INTEGER PRIMARY KEY AUTOINCREMENT,task_name varchar(200) not null, cost_time varchar(20) not null, create_time varchar(25) not null)"))
- {
- qDebug() << "create table task_history error: " << query.lastError().text();
- }
-
- return true;
- }
- #endif // CONNECTION_H
初始化连接到 sqlite 数据库,这里使用的内存数据库,没有持久化到磁盘,连接数据库之后创建了一个表 task_history ,主键为 id 自增,任务事项 task_name 以及消耗时间 cost_time,
一个 QLineEdit 用于输入任务事项,一个 QLCDNumber 用于计时,一个 QPlainTextEdit 用于输出日志,两个 QPushButton 用于操作开始计时和终止计时,


除了组件的布局调整,还需要在主窗体新增一个槽函数 on_Widget_customContextMenuRequested ,在添加右键菜单功能时需要用到,
- #ifndef WIDGET_H
- #define WIDGET_H
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- QT_BEGIN_NAMESPACE
- namespace Ui { class Widget; }
- QT_END_NAMESPACE
-
- class Widget : public QWidget
- {
- Q_OBJECT
-
- public:
- Widget(QWidget *parent = nullptr);
- ~Widget();
- protected:
- void mousePressEvent(QMouseEvent *event);
- void mouseMoveEvent(QMouseEvent *event);
- void paintEvent(QPaintEvent *);
- protected:
- QPoint pos;
- QTimer * timer;
- QTime * timeRecord;
- bool isStart;
- private slots:
- void update_time();
- void on_start_btn_clicked();
- void get_todo_summary();
- void on_end_btn_clicked();
-
- void on_Widget_customContextMenuRequested(const QPoint &pos);
-
- private:
- Ui::Widget *ui;
- };
- #endif // WIDGET_H
头文件中主要定义了计时器的成员属性与成员方法、鼠标事件、绘画事件以及对应的槽方法,
- #include "widget.h"
- #include "ui_widget.h"
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- this->setWindowFlag(Qt::FramelessWindowHint);
- this->setAttribute(Qt::WA_TranslucentBackground);
- this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
- // init
- this->isStart = false;
- this->timer = new QTimer;
- this->timeRecord = new QTime(0, 0, 0);
- // connect timer and SLOT function
- connect(this->timer,SIGNAL(timeout()),this,SLOT(update_time()));
- // menu
- this->setContextMenuPolicy(Qt::CustomContextMenu);
- }
-
- Widget::~Widget()
- {
- delete ui;
- }
-
- void Widget::mousePressEvent(QMouseEvent* ev)
- {
- if(ev->button()==Qt::LeftButton)
- {
- pos=ev->pos();
- }
- }
-
- void Widget::mouseMoveEvent(QMouseEvent*ev)
- {
-
-
- if(ev->buttons()==Qt::LeftButton)
- {
- int x,y;
- x=ev->pos().x()-pos.x();
- y=ev->pos().y()-pos.y();
- this->move(this->x()+x,this->y()+y);
- }
- }
-
- void Widget::paintEvent(QPaintEvent *)
- {
- QPainter painter(this);
-
- QPixmap pixmap;
-
- pixmap.load(QString("../MyPet/image/pikakun.png"));
-
- painter.drawPixmap(0, 0, 128, 128, pixmap);
- }
-
- void Widget::on_start_btn_clicked()
- {
-
- if(this->ui->todo_line->text().trimmed().length() == 0 )
- {
- QMessageBox::warning(this, tr("warning"), tr("please input todo first!"), QMessageBox::Yes);
- this->ui->todo_line->setFocus();
- return;
- }
-
- this->ui->todo_line->setEnabled(false);
- this->ui->last_log->setPlainText(QString("Mission " + this->ui->todo_line->text().trimmed() + " is timing..."));
-
- if(!this->isStart)
- {
- this->timer->start(128);
- this->ui->start_btn->setText(QString("Pause"));
- }
- else
- {
- this->timer->stop();
- this->ui->start_btn->setText(QString("Continue"));
- }
- this->isStart = !this->isStart;
- }
-
- void Widget::update_time()
- {
- // timer add 1 secs, and then display
- *this->timeRecord = this->timeRecord->addMSecs(128);
- this->ui->timer->display(this->timeRecord->toString(QString("hh:mm:ss.zzz")));
- }
-
-
- void Widget::on_end_btn_clicked()
- {
- // stop timer
- this->timer->stop();
-
- // show log
- this->ui->last_log->setPlainText(
- QString(
- this->ui->todo_line->text() +
- " Cost Time " +
- this->timeRecord->toString(QString("hh:mm:ss.zzz"))
- )
- );
-
- // save db
-
- QSqlDatabase::database().transaction();
- QSqlQuery query;
-
- query.prepare(QString("insert into task_history(task_name, cost_time, create_time ) "
- "values(:task_name, :cost_time,:create_time)"));
-
- query.bindValue(":task_name", this->ui->todo_line->text());
- query.bindValue(":cost_time", this->timeRecord->toString(QString("hh:mm:ss.zzz")));
-
- QDateTime dt =QDateTime::currentDateTime();
- // format yyyy//MM/dd hh:mm:s.zzz
- QString create_time = dt.toString("yyyy-MM-dd hh:mm:ss.zzz");
-
- query.bindValue(":create_time", create_time);
-
- if(!query.exec())
- {
- qDebug() << "insert into task_history error: " << query.lastError().text();
- }
-
- QSqlDatabase::database().commit();
-
-
- // reset display
- this->timeRecord->setHMS(0,0,0);
- this->ui->timer->display(this->timeRecord->toString(QString("hh:mm:ss.zzz")));
-
- this->isStart = false;
- this->ui->start_btn->setText(QString("Start"));
-
- // reset todo_line
- this->ui->todo_line->clear();
- this->ui->todo_line->setEnabled(true);
- this->ui->todo_line->setFocus();
- }
-
- void Widget::get_todo_summary()
- {
- QSqlQueryModel *model = new QSqlQueryModel(this);
-
- model->setQuery("select * from task_history");
-
- model->setHeaderData(0, Qt::Horizontal, tr("ID"));
- model->setHeaderData(1, Qt::Horizontal, tr("Task"));
- model->setHeaderData(2, Qt::Horizontal, tr("Time"));
- model->setHeaderData(3, Qt::Horizontal, tr("LogTime"));
-
- QTableView *view = new QTableView;
- view->setWindowTitle(QString("summary"));
- view->setModel(model);
- // must after setModel
- view->setColumnWidth(1,250);
- view->setColumnWidth(3,150);
- QSize *min_size = new QSize;
- min_size->setWidth(620);
- min_size->setHeight(400);
- view->setMinimumSize(*min_size);
-
- QScreen *screen = QApplication::primaryScreen();
- QSize screenSize = screen->size();
- view->move(( screenSize.width() - min_size->width() ) / 2 , ( screenSize.height() - view->height() ) / 2 );
-
- view->show();
-
- // free mem
- delete min_size;
- }
-
- void Widget::on_Widget_customContextMenuRequested(const QPoint &pos)
- {
- QMenu *menu = new QMenu(this);
- QAction *summary = new QAction( QIcon(QString("../MyPet/image/pikakun.png")),tr("summary"), this);
- menu->addAction(summary);
-
- connect(summary, SIGNAL(triggered()), this, SLOT(get_todo_summary()));
-
- menu->exec(cursor().pos());
- }
-
初始化数据库连接,并将窗口移动至屏幕的中间位置,
- #include "widget.h"
- #include "connection.h"
- #include
- #include
- #include
- #include
-
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
-
- if (!createConnection())
- {
- return 1;
- }
-
- QTranslator translator;
- const QStringList uiLanguages = QLocale::system().uiLanguages();
- for (const QString &locale : uiLanguages) {
- const QString baseName = "MyPet_" + QLocale(locale).name();
- if (translator.load(":/i18n/" + baseName)) {
- a.installTranslator(&translator);
- break;
- }
- }
- Widget w;
- QScreen *screen = QApplication::primaryScreen();
- QSize screenSize = screen->size();
- w.move(( screenSize.width() - w.width() ) / 2 , ( screenSize.height() - w.height() ) / 2 );
- w.show();
- return a.exec();
- }

1、输入需要计时的事项后,点击“Start”按钮开始计时
2、开始计时后,“Start”按钮会变成“Pause”按钮,点击该按钮会暂停计时
3、暂停后,“Pause”按钮会变成“Continue”按钮,点击该按钮会继续计时
4、点击“End”按钮后,停止计时,并记录事项耗时到 sqlite 数据库中
5、右键有“Summary”菜单,点击后可以看到事项汇总
特别注意,如果需要将数据从内存中持久化到硬盘,则修改 connection.h,
- # 保存在内存,注释这行
- // db.setDatabaseName(":memory:");
-
- # 保存在 ikun.db,添加这行
- db.setDatabaseName("ikun.db");