• 基于 QT 实现 Task Timer,高效利用时间


    一、开发环境

    1. Ubuntu 20.04
    2. QT6.0

    二、新建 Qt Wigets Application 

    这里的基类选择 Wigets,

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

    QT += sql

    三、添加数据库连接头文件

    1. // connection.h
    2. #ifndef CONNECTION_H
    3. #define CONNECTION_H
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. static bool createConnection()
    10. {
    11. QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    12. db.setDatabaseName(":memory:");
    13. //db.setDatabaseName("ikun.db");
    14. bool res = db.open();
    15. if (res)
    16. {
    17. qDebug() << "open ikun.db ok !";
    18. }
    19. else
    20. {
    21. qDebug() << "open ikun.db fail: " << db.lastError().text();
    22. }
    23. QSqlQuery query;
    24. 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)"))
    25. {
    26. qDebug() << "create table task_history error: " << query.lastError().text();
    27. }
    28. return true;
    29. }
    30. #endif // CONNECTION_H

    初始化连接到 sqlite 数据库,这里使用的内存数据库,没有持久化到磁盘,连接数据库之后创建了一个表 task_history  ,主键为 id 自增,任务事项 task_name 以及消耗时间 cost_time,

    四、布局设计

    一个 QLineEdit 用于输入任务事项,一个 QLCDNumber 用于计时,一个 QPlainTextEdit 用于输出日志,两个 QPushButton 用于操作开始计时和终止计时,

    除了组件的布局调整,还需要在主窗体新增一个槽函数 on_Widget_customContextMenuRequested ,在添加右键菜单功能时需要用到,

    五、修改 widget.h

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. QT_BEGIN_NAMESPACE
    11. namespace Ui { class Widget; }
    12. QT_END_NAMESPACE
    13. class Widget : public QWidget
    14. {
    15. Q_OBJECT
    16. public:
    17. Widget(QWidget *parent = nullptr);
    18. ~Widget();
    19. protected:
    20. void mousePressEvent(QMouseEvent *event);
    21. void mouseMoveEvent(QMouseEvent *event);
    22. void paintEvent(QPaintEvent *);
    23. protected:
    24. QPoint pos;
    25. QTimer * timer;
    26. QTime * timeRecord;
    27. bool isStart;
    28. private slots:
    29. void update_time();
    30. void on_start_btn_clicked();
    31. void get_todo_summary();
    32. void on_end_btn_clicked();
    33. void on_Widget_customContextMenuRequested(const QPoint &pos);
    34. private:
    35. Ui::Widget *ui;
    36. };
    37. #endif // WIDGET_H

    头文件中主要定义了计时器的成员属性与成员方法、鼠标事件、绘画事件以及对应的槽方法,

    六、修改 widget.cpp

    1. #include "widget.h"
    2. #include "ui_widget.h"
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. Widget::Widget(QWidget *parent)
    13. : QWidget(parent)
    14. , ui(new Ui::Widget)
    15. {
    16. ui->setupUi(this);
    17. this->setWindowFlag(Qt::FramelessWindowHint);
    18. this->setAttribute(Qt::WA_TranslucentBackground);
    19. this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
    20. // init
    21. this->isStart = false;
    22. this->timer = new QTimer;
    23. this->timeRecord = new QTime(0, 0, 0);
    24. // connect timer and SLOT function
    25. connect(this->timer,SIGNAL(timeout()),this,SLOT(update_time()));
    26. // menu
    27. this->setContextMenuPolicy(Qt::CustomContextMenu);
    28. }
    29. Widget::~Widget()
    30. {
    31. delete ui;
    32. }
    33. void Widget::mousePressEvent(QMouseEvent* ev)
    34. {
    35. if(ev->button()==Qt::LeftButton)
    36. {
    37. pos=ev->pos();
    38. }
    39. }
    40. void Widget::mouseMoveEvent(QMouseEvent*ev)
    41. {
    42. if(ev->buttons()==Qt::LeftButton)
    43. {
    44. int x,y;
    45. x=ev->pos().x()-pos.x();
    46. y=ev->pos().y()-pos.y();
    47. this->move(this->x()+x,this->y()+y);
    48. }
    49. }
    50. void Widget::paintEvent(QPaintEvent *)
    51. {
    52. QPainter painter(this);
    53. QPixmap pixmap;
    54. pixmap.load(QString("../MyPet/image/pikakun.png"));
    55. painter.drawPixmap(0, 0, 128, 128, pixmap);
    56. }
    57. void Widget::on_start_btn_clicked()
    58. {
    59. if(this->ui->todo_line->text().trimmed().length() == 0 )
    60. {
    61. QMessageBox::warning(this, tr("warning"), tr("please input todo first!"), QMessageBox::Yes);
    62. this->ui->todo_line->setFocus();
    63. return;
    64. }
    65. this->ui->todo_line->setEnabled(false);
    66. this->ui->last_log->setPlainText(QString("Mission " + this->ui->todo_line->text().trimmed() + " is timing..."));
    67. if(!this->isStart)
    68. {
    69. this->timer->start(128);
    70. this->ui->start_btn->setText(QString("Pause"));
    71. }
    72. else
    73. {
    74. this->timer->stop();
    75. this->ui->start_btn->setText(QString("Continue"));
    76. }
    77. this->isStart = !this->isStart;
    78. }
    79. void Widget::update_time()
    80. {
    81. // timer add 1 secs, and then display
    82. *this->timeRecord = this->timeRecord->addMSecs(128);
    83. this->ui->timer->display(this->timeRecord->toString(QString("hh:mm:ss.zzz")));
    84. }
    85. void Widget::on_end_btn_clicked()
    86. {
    87. // stop timer
    88. this->timer->stop();
    89. // show log
    90. this->ui->last_log->setPlainText(
    91. QString(
    92. this->ui->todo_line->text() +
    93. " Cost Time " +
    94. this->timeRecord->toString(QString("hh:mm:ss.zzz"))
    95. )
    96. );
    97. // save db
    98. QSqlDatabase::database().transaction();
    99. QSqlQuery query;
    100. query.prepare(QString("insert into task_history(task_name, cost_time, create_time ) "
    101. "values(:task_name, :cost_time,:create_time)"));
    102. query.bindValue(":task_name", this->ui->todo_line->text());
    103. query.bindValue(":cost_time", this->timeRecord->toString(QString("hh:mm:ss.zzz")));
    104. QDateTime dt =QDateTime::currentDateTime();
    105. // format yyyy//MM/dd hh:mm:s.zzz
    106. QString create_time = dt.toString("yyyy-MM-dd hh:mm:ss.zzz");
    107. query.bindValue(":create_time", create_time);
    108. if(!query.exec())
    109. {
    110. qDebug() << "insert into task_history error: " << query.lastError().text();
    111. }
    112. QSqlDatabase::database().commit();
    113. // reset display
    114. this->timeRecord->setHMS(0,0,0);
    115. this->ui->timer->display(this->timeRecord->toString(QString("hh:mm:ss.zzz")));
    116. this->isStart = false;
    117. this->ui->start_btn->setText(QString("Start"));
    118. // reset todo_line
    119. this->ui->todo_line->clear();
    120. this->ui->todo_line->setEnabled(true);
    121. this->ui->todo_line->setFocus();
    122. }
    123. void Widget::get_todo_summary()
    124. {
    125. QSqlQueryModel *model = new QSqlQueryModel(this);
    126. model->setQuery("select * from task_history");
    127. model->setHeaderData(0, Qt::Horizontal, tr("ID"));
    128. model->setHeaderData(1, Qt::Horizontal, tr("Task"));
    129. model->setHeaderData(2, Qt::Horizontal, tr("Time"));
    130. model->setHeaderData(3, Qt::Horizontal, tr("LogTime"));
    131. QTableView *view = new QTableView;
    132. view->setWindowTitle(QString("summary"));
    133. view->setModel(model);
    134. // must after setModel
    135. view->setColumnWidth(1,250);
    136. view->setColumnWidth(3,150);
    137. QSize *min_size = new QSize;
    138. min_size->setWidth(620);
    139. min_size->setHeight(400);
    140. view->setMinimumSize(*min_size);
    141. QScreen *screen = QApplication::primaryScreen();
    142. QSize screenSize = screen->size();
    143. view->move(( screenSize.width() - min_size->width() ) / 2 , ( screenSize.height() - view->height() ) / 2 );
    144. view->show();
    145. // free mem
    146. delete min_size;
    147. }
    148. void Widget::on_Widget_customContextMenuRequested(const QPoint &pos)
    149. {
    150. QMenu *menu = new QMenu(this);
    151. QAction *summary = new QAction( QIcon(QString("../MyPet/image/pikakun.png")),tr("summary"), this);
    152. menu->addAction(summary);
    153. connect(summary, SIGNAL(triggered()), this, SLOT(get_todo_summary()));
    154. menu->exec(cursor().pos());
    155. }

    七、修改 main.cpp

    初始化数据库连接,并将窗口移动至屏幕的中间位置,

    1. #include "widget.h"
    2. #include "connection.h"
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main(int argc, char *argv[])
    8. {
    9. QApplication a(argc, argv);
    10. if (!createConnection())
    11. {
    12. return 1;
    13. }
    14. QTranslator translator;
    15. const QStringList uiLanguages = QLocale::system().uiLanguages();
    16. for (const QString &locale : uiLanguages) {
    17. const QString baseName = "MyPet_" + QLocale(locale).name();
    18. if (translator.load(":/i18n/" + baseName)) {
    19. a.installTranslator(&translator);
    20. break;
    21. }
    22. }
    23. Widget w;
    24. QScreen *screen = QApplication::primaryScreen();
    25. QSize screenSize = screen->size();
    26. w.move(( screenSize.width() - w.width() ) / 2 , ( screenSize.height() - w.height() ) / 2 );
    27. w.show();
    28. return a.exec();
    29. }

    八、效果演示

    1、输入需要计时的事项后,点击“Start”按钮开始计时

    2、开始计时后,“Start”按钮会变成“Pause”按钮,点击该按钮会暂停计时

    3、暂停后,“Pause”按钮会变成“Continue”按钮,点击该按钮会继续计时

    4、点击“End”按钮后,停止计时,并记录事项耗时到 sqlite 数据库中

    5、右键有“Summary”菜单,点击后可以看到事项汇总

    特别注意,如果需要将数据从内存中持久化到硬盘,则修改 connection.h,

    1. # 保存在内存,注释这行
    2. // db.setDatabaseName(":memory:");
    3. # 保存在 ikun.db,添加这行
    4. db.setDatabaseName("ikun.db");
  • 相关阅读:
    PDF文件在线预览
    Mysql-题目02
    QT内存管理
    Android 开发学习(三)
    SSER服务器请求伪造
    Doris数据库使用记录
    采购自动化如何帮助采购转型?
    编写一个方法,去掉一个数组的重复元素。
    【java】多线程
    【windows】如何全部关闭360安全的弹出guang告?(已解决)
  • 原文地址:https://blog.csdn.net/weixin_47560078/article/details/133496146