# todo **Repository Path**: hubu0915/todo ## Basic Information - **Project Name**: todo - **Description**: 一款轻量化基于C++、Qt开发的待办软件 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-06-10 - **Last Updated**: 2023-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一、界面布局 ​ 整个界面模仿金山待办的布局。首先将整个界面垂直布局,其内部放置两个水平布局,上方水平布局展示系统logo与标题,下方水平布局从左往右依次为:左侧待办分组、中部待办列表(QStackedWidget实现界面跳转)、右侧待办详情(QStackedWidget实现界面跳转)。其中,系统的相关图片浏览器的NetWork获取;相关颜色通过取色器获取。 ## 二、架构描述 ​ 整个系统通过自定义组件并重写相关的事件来完成特定的功能,同时主类、自定义类以及数据库工具类、实现不同模块之间的解耦。使得程序的可拓展性提高。 ### 2.1 工程文件描述 ``` |--金山待办 ​ |-- databaseutil.cpp //系统的数据库操作工具类,所有的sql执行都在这里完成 ​ |-- kcustombuttom.cpp //界面左侧的自定义按钮类,继承于QPushButton ​ |-- kcustomcalendar.cpp //界面右侧每个待办设置到期时间的日历弹出框,继承于QDialog ​ |-- kcustominputdialog.cpp //添加分组信息时弹出的输入框,继承于QDialog ​ |-- kcustomitemwidget.cpp //自定义的每个待办对象,用于每个待办的属性封装与存储,继承于QWidget ​ |-- kcustomlineedit.cpp //界面中间的自定义输入栏,继承于QLineEdit。可以实现提示框与输入框的转变 ​ |-- kcustomtodobutton.cpp //界面中间的按钮(未安排、七天内等),通过重写enterEvent与leaveEvent函数实现QIcon的变化 ​ |-- kmainwindow.cpp //系统主类,其他类的数据都流向kmainwindow.cpp并实现相应的数据库操作 ``` #### 2.1.1 databaseutil.cpp (数据库工具类) ​ 该类继承于QObject,其成员方法包括数据库连接、数据库关闭、插入数据,更新数据、查询等相关的数据操作,并以拼接sql的形式进行sql语句组装。调用者只需将相关的参数传递给该成员函数集合,而不用在逻辑代码中嵌套sql本身。以更新数据的成员函数示例: ``` //参数1:要操作的数据表名称; //参数2:要更新的数据,以key和value的形式存储,key为字段名,value为数据,可以修改多个字段 //参数3:拼接的查询条件,以key和value的形式存储,key为字段名,value为数据,可以拼接多个查询条件 bool updateData(const QString& table,const QVariantMap& data,const QVariantMap& conditions); ``` ​ 数据表:1.待办表;2.组别表 ``` //待办表 CREATE TABLE todoitem ( id INTEGER PRIMARY KEY AUTOINCREMENT //主键自增id UNIQUE NOT NULL, name TEXT NOT NULL, //待办名称 create_time TEXT NOT NULL, //待办创建时间 expire_time TEXT DEFAULT defaultTime, //待办到期时间 remind_time TEXT DEFAULT defaultTime, //待办提醒时间 description TEXT, //待办描述 address TEXT, //待办地址 remark TEXT, //待办备注 repeat INTEGER DEFAULT (0), //提醒是否重复 status INTEGER DEFAULT (1), //待办状态(1为未完成;2为已完成) item_group TEXT, //待办所属分组 current_added INTEGER DEFAULT (1), //是否为新建待办(新建为1,其他为0) level TEXT DEFAULT (3) //待办优先级(0,1,2,3)默认为3,即无优先级 ); //分组表 CREATE TABLE custom_group ( id INTEGER PRIMARY KEY AUTOINCREMENT //主键自增id UNIQUE NOT NULL, item_group TEXT NOT NULL, //分组名称 create_time TEXT //分组创建时间 ); ``` #### 2.1.2 kcustombuttom.cpp (左侧自定义按钮) ​ 设置这个自定义按钮的目的主要是为了设置按钮样式。如默认情况下,hover和pressed时候的边框,字体颜色,背景颜色样式。 ​ 自定义的左侧按钮,在系统初始化时创建三个按钮(我的首页、我的待办、分配给我的)。当新建分组成功之后,会初始化当前的按钮对象并加入到当前的分组按钮布局中。当每个按钮被点击时,其按钮的QIcon会发生变化。 #### 2.1.3 kcustomcalendar.cpp (设置到期时间) ​ 该类的实例化对象唯一且在系统启动的时候被创建,并作为主类kmainwindow.cpp的成员函数。 ​ 该类添加了年份选择QComboBox,月份选择QComboBox,以及日历QCalendarWidget。该对话框被打开时,系统默认选择当前的年-月-日日期,当年份选择QComboBox与月份选择QComboBox变化时,会触发相应的槽函数来改变当前日历界面 ​ 槽函数如下: ``` //年份变化的槽函数 void KCustomCalendar::updateCalendar(int index) { //获取选中的年份 QString yearText = m_yearComboBox->itemText(index); int year = yearText.toInt(); //更新QCalendarWidget的显示日期 QDate date = m_calendarWidget->selectedDate(); date.setDate(year, date.month(), date.day()); m_calendarWidget->setSelectedDate(date); } //月份变化的槽函数 void KCustomCalendar::handleMonthIndexChanged(const int index) { //获取选择的月份 int selectedMonth = m_monthComboBox->itemData(index).toInt(); //获取当前日期 QDate currentDate = QDate::currentDate(); //设置当前页为选择的月份 m_calendarWidget->setCurrentPage(currentDate.year(), selectedMonth); } ``` ​ 为了防止用户误操作,必须点击当前界面的日历才能点击确定(通过&QCalendarWidget::clicked点击信号来实现按钮的setEnabled())。当按钮的setEnabled()为true时,该按钮的点击信号会将当前选择时间以自定义信号的形式发送到KMainwindow中,完成数据存储与渲染。 #### 2.1.4 kcustominputdialog.cpp (添加分组) ​ 在点击添加分组后,会弹出添加分组对话框。通过在对话框中输入分组名并点击确定,创建分组。 ​ 为防止用户误操作,通过&QLineEdit::textChanged信号的槽函数来实现按钮是否可以点击。 ``` connect(m_lineEdit, &QLineEdit::textChanged, this, &KCustomInputDialog::updateButtonStatus); void KCustomInputDialog::updateButtonStatus(const QString& text) { //如果输入框文本为空,则不能点击 m_submitButton->setEnabled(!text.isEmpty()); } ``` ​ 确认按钮若点击成功,则通过自定义信号将数据发送到KMainwindow中完成数据存储与界面渲染。 #### 2.1.5 kcustomitemwidget.cpp (每个待办对象) ​ 该类继承QWidget,添加上下两个水平布局。 ​ 布局中的组件包括如下6个,在加载数据库中的待办时,将待办数据装配到每个实例中: ​ 1.完成待办的勾选QCheckBox; ​ 2.待办名称QPushButton,QPushButton的QIcon用来显示每个待办的优先级; ​ 3.到期事件QLabel; ​ 4.是否重复QPushbutton; ​ 5.提醒时间QPushButton; ​ 6.组别时间QPushbutton; ​ 通过重写相关事件实现每个待办背景颜色的切换。这里需要设置每个组件的QSS样式为透明状态 ``` //绘制背景颜色 void paintEvent(QPaintEvent* event) override; void enterEvent(QEvent* event) override; void leaveEvent(QEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; ``` ​ 定时功能:在每个实例对象封装数据到期时间数据的时候,会更新定时器;在更新时,会判断时间数据是否有效来启动。 ``` //设置提醒时间 void KCustomItemWidget::setRemindTime(const QString& remindTime) { m_remindTime = remindTime; m_remindTimeButton->setText(m_remindTime); m_remindTimeButton->setVisible(true); //更新定时器 updateTimer(); } //更新定时器 void KCustomItemWidget::updateTimer() { // 停止之前的定时器 m_timer.stop(); // 计算当前时间和目标时间之间的时间差 QDateTime time = QDateTime::fromString(m_remindTime, "yyyy-MM-dd hh:mm:ss"); if (time.isValid()) { int msec = QDateTime::currentDateTime().msecsTo(time); if (msec > 0) m_timer.start(msec); } } ``` #### 2.1.6 kcustomlineedit.cpp (新建待办输入栏) ​ 在该编辑框的构造中添加水平布局,并放置logo图标、QLabel文本标签。之后重写相关事件,实现输入栏点击后相关组件的隐藏与显示。相关重写事件如下: ``` //获取鼠标点击时间 void mousePressEvent(QMouseEvent* event) override; //捕获回车键时间 void keyPressEvent(QKeyEvent* event) override; //判断event->type() == QEvent::Leave,然后进行组件的隐藏与显示 bool event(QEvent* event) override; //布局内部的组件隐藏 void focusInEvent(QFocusEvent* event) override; ``` #### 2.1.7 kmainwindow.cpp (系统主类) ​ 其他自定义类都是在kmainwindow.cpp中创建的,并加载自定义类的信号和主类槽函数的连接,实现数据的数据传递,并在主类中进行数据库更新。其他类的数据都是单向流动到kmainwindow.cpp的实例对象中。数据流动方式有如下两点: ​ 1.在kmainwindow.cpp的实例对象中,查询数据库数据并通过自定义类组件的set方法获取,然后将自定义类组件加载到ui布局中,例如kcustomitemwidget.cpp这个待办对象 ​ 2.自定义信号与槽函数实现不同类之间的传递,例如自定义输入栏的数据、设置到期时间等其他属性向kmainwindow.cpp实例化对象的传递。 ### 三、额外功能设计 1. 待办列表通过滑动布局来展示,界面更加美观 2. 在新建分组功能时,若分组的编辑框为空时,不能提交确认,防止数据污染;我认为分组名应该不能重复,所以在设计时,新建分组就会先查询数据库,若组名存在,则不能提交。 3. 设置到期时间时,弹出的日历对话框中的日历如果不点击的情况下,是不能确认的,防止用户误操作;弹出的日历对话框默认为当前日期 4. 设计待办的描述、地址、备注采用水平布局。 - 当布局中的按钮点击后,按钮隐藏,后面的输入栏可见并聚焦; - 输入栏失去焦点时,去除输入栏的首尾空字符(防止空字符造成输入栏显示的bug),并完成数据更新; - 如果这些属性不为空,下次点击待办详情时,直接显示属性对应的输入栏(默认无边框,聚焦有边框),并可以点击更新 ​ 5.已完成的待办取消勾选QCheckBox时,待办数据会标记为未完成,并完成数据修改与界面重新渲染。