全屏定时提醒工具
本文档详细介绍了全屏定时提醒工具的代码实现过程,从项目架构到具体功能的逐步实现。
📋 目录
1. 项目架构设计
1.1 整体架构
ReminderTool/
├── main.cpp # 程序入口点
├── mainwindow.h/.cpp/.ui # 主窗口 - 提醒管理界面
├── reminderdialog.h/.cpp/.ui # 提醒对话框 - 全屏提醒显示
├── ReminderTool.pro # Qt项目配置
└── res.qrc # 资源文件
1.2 设计模式
- MVC模式:UI界面与业务逻辑分离
- 单例模式:密码管理使用静态成员
- 观察者模式:Qt信号槽机制
1.3 核心组件
- MainWindow:主控制器,负责提醒管理
- ReminderDialog:全屏提醒显示窗口
- Reminder结构体:提醒数据模型
- QTimer:定时检查和倒计时功能
2. 环境配置与项目创建
2.1 创建Qt项目
cpp
// ReminderTool.pro 配置文件
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp \
reminderdialog.cpp
HEADERS += \
mainwindow.h \
reminderdialog.h
FORMS += \
mainwindow.ui \
reminderdialog.ui
RESOURCES += res.qrc
2.2 程序入口点设计
cpp
// main.cpp - 关键配置
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 🔑 关键设置:程序不会在最后窗口关闭时退出
// 这样可以在系统托盘中继续运行
a.setQuitOnLastWindowClosed(false);
// 设置应用程序图标
a.setWindowIcon(QIcon(":/avatar.jpg"));
MainWindow w;
w.show();
return a.exec();
}
实现要点:
setQuitOnLastWindowClosed(false)是实现托盘功能的关键- 应用程序图标设置提升用户体验
3. 核心数据结构设计
3.1 提醒数据结构
cpp
// mainwindow.h - 提醒数据模型
struct Reminder {
QDateTime time; // 提醒时间
QString message; // 提醒内容
bool triggered = false; // 是否已触发
bool isRecurring = false; // 是否为周期性任务
int recurringType = 0; // 周期类型:0=一次性,1=每天重复
};
设计考虑:
- 使用
QDateTime处理时间,支持日期和时间 triggered标志避免重复触发isRecurring和recurringType支持周期性提醒
3.2 主窗口类设计
cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
Ui::MainWindow *ui;
QTimer *m_timer; // 定时检查器
QList<Reminder> m_reminders; // 提醒列表
// 系统托盘相关
QSystemTrayIcon *m_trayIcon;
QMenu *m_trayMenu;
// 免打扰功能
QDateTime m_doNotDisturbEndTime;
// 核心功能函数
void setupTimer(); // 设置定时器
void setupTrayIcon(); // 设置系统托盘
void checkReminders(); // 检查提醒
void saveReminders(); // 保存数据
void loadReminders(); // 加载数据
};
4. 主窗口实现
4.1 构造函数 - 初始化流程
cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_timer(new QTimer(this))
, m_trayIcon(nullptr)
, m_trayMenu(nullptr)
{
// 步骤1:基础UI设置
ui->setupUi(this);
setWindowTitle("全屏定时提醒工具");
setFixedSize(900, 700);
// 步骤2:字体和样式设置
setupFontAndStyle();
// 步骤3:初始化控件默认值
initializeControls();
// 步骤4:连接信号槽
connectSignals();
// 步骤5:设置核心功能
setupTimer(); // 定时检查
setupTrayIcon(); // 系统托盘
// 步骤6:加载数据
loadReminders(); // 加载保存的提醒
loadSettings(); // 加载设置
}
4.2 字体和样式设置
cpp
void MainWindow::setupFontAndStyle()
{
// 设置全局字体抗锯齿
QFont font = this->font();
font.setFamily("Microsoft YaHei UI, Segoe UI, Arial, sans-serif");
font.setHintingPreference(QFont::PreferFullHinting);
font.setStyleStrategy(QFont::PreferAntialias);
this->setFont(font);
// 应用全局样式
this->setStyleSheet(this->styleSheet() +
"* { font-family: 'Microsoft YaHei UI', 'Segoe UI', Arial, sans-serif; }");
}
4.3 控件初始化
cpp
void MainWindow::initializeControls()
{
// 日期控件设置
ui->dateEdit->setDate(QDate::currentDate());
ui->dateEdit->setMinimumDate(QDate::currentDate());
ui->dateEdit->setMaximumDate(QDate::currentDate().addYears(1));
// 时间控件设置为当前时间
QTime currentTime = QTime::currentTime();
ui->hourSpinBox->setValue(currentTime.hour());
ui->minuteSpinBox->setValue(currentTime.minute());
// 列表控件设置
ui->reminderListWidget->setFocusPolicy(Qt::StrongFocus);
ui->reminderListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
}
4.4 信号槽连接
cpp
void MainWindow::connectSignals()
{
// 基础功能连接
connect(ui->setPasswordButton, &QPushButton::clicked,
this, &MainWindow::setPassword);
// 周期性选择
connect(ui->recurringComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::onRecurringChanged);
// 列表双击删除
connect(ui->reminderListWidget, &QListWidget::itemDoubleClicked,
this, &MainWindow::onReminderItemDoubleClicked);
// 开机自启
connect(ui->autoStartCheckBox, &QCheckBox::toggled,
this, &MainWindow::onAutoStartToggled);
// 快捷时间按钮
connectQuickTimeButtons();
}
4.5 定时器设置
cpp
void MainWindow::setupTimer()
{
// 连接定时器信号
connect(m_timer, &QTimer::timeout, this, &MainWindow::checkReminders);
// 每秒检查一次 - 这是核心功能
m_timer->start(1000);
}
关键点:
- 1秒间隔确保及时触发提醒
- 使用
QTimer::timeout信号进行周期性检查
5. 提醒检查与触发机制
5.1 核心检查逻辑
cpp
void MainWindow::checkReminders()
{
// 检查是否在免打扰时间
if (isDoNotDisturbActive()) {
return;
}
QDateTime currentTime = QDateTime::currentDateTime();
bool hasTriggered = false;
for (int i = 0; i < m_reminders.size(); ++i) {
Reminder &reminder = m_reminders[i];
// 检查是否到达提醒时间且未触发
if (!reminder.triggered && currentTime >= reminder.time) {
// 触发提醒
triggerReminder(reminder);
hasTriggered = true;
// 处理周期性任务
if (reminder.isRecurring && reminder.recurringType == 1) {
// 每天重复:设置为明天同一时间
reminder.time = reminder.time.addDays(1);
reminder.triggered = false;
} else {
// 一次性任务:标记为已触发
reminder.triggered = true;
}
}
}
if (hasTriggered) {
// 清理已完成的一次性任务
cleanupTriggeredReminders();
// 更新显示和保存数据
updateReminderListDisplay();
saveReminders();
}
}
5.2 提醒触发
cpp
void MainWindow::triggerReminder(const Reminder &reminder)
{
// 获取倒计时设置
int countdownSeconds = ui->countdownSpinBox->value();
// 创建并显示全屏提醒对话框
ReminderDialog *dialog = new ReminderDialog(
reminder.message,
countdownSeconds,
this
);
// 模态显示,阻塞其他操作
dialog->exec();
// 对话框关闭后自动删除
dialog->deleteLater();
}
6. 提醒对话框实现
6.1 全屏对话框设计
cpp
ReminderDialog::ReminderDialog(const QString &message, int countdownSeconds, QWidget *parent) :
QDialog(parent),
ui(new Ui::ReminderDialog),
m_countdownTimer(new QTimer(this)),
m_remainingSeconds(countdownSeconds)
{
ui->setupUi(this);
// 🔑 关键:设置窗口为无边框全屏置顶
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool);
setWindowState(Qt::WindowFullScreen);
// 设置背景和样式
setStyleSheet("background-color: #2E8B57; color: white;");
// 强制获取焦点
setAttribute(Qt::WA_ShowWithoutActivating, false);
setModal(true);
// 设置UI和启动倒计时
setupUI(message);
startCountdown(countdownSeconds);
// 确保窗口置顶(Windows特定)
ensureTopmost();
}
6.2 UI布局设计
cpp
void ReminderDialog::setupUI(const QString &message)
{
QVBoxLayout *layout = new QVBoxLayout(this);
// 主要提醒消息
m_messageLabel = new QLabel(message, this);
m_messageLabel->setAlignment(Qt::AlignCenter);
m_messageLabel->setWordWrap(true);
m_messageLabel->setStyleSheet(
"font-size: 48px; "
"font-weight: bold; "
"margin: 50px; "
"font-family: 'Microsoft YaHei UI', 'Segoe UI', Arial, sans-serif;"
);
// 倒计时标签
m_countdownLabel = new QLabel(this);
m_countdownLabel->setAlignment(Qt::AlignCenter);
m_countdownLabel->setStyleSheet(
"font-size: 36px; "
"font-weight: bold; "
"margin: 20px; "
"color: #FFD700; "
"background-color: rgba(255, 255, 255, 0.1); "
"border-radius: 10px; "
"padding: 15px;"
);
// 密码输入区域
setupPasswordInput();
// 添加到布局
layout->addWidget(m_messageLabel);
layout->addWidget(m_countdownLabel);
layout->addWidget(m_passwordLabel);
layout->addLayout(createPasswordLayout());
layout->addWidget(m_closeButton, 0, Qt::AlignCenter);
setLayout(layout);
// 添加背景闪烁效果
setupBlinkEffect();
}
6.3 倒计时功能
cpp
void ReminderDialog::startCountdown(int seconds)
{
m_remainingSeconds = seconds;
updateCountdown();
// 连接倒计时定时器
connect(m_countdownTimer, &QTimer::timeout, this, &ReminderDialog::updateCountdown);
m_countdownTimer->start(1000);
}
void ReminderDialog::updateCountdown()
{
if (m_remainingSeconds > 0) {
// 显示剩余时间
m_countdownLabel->setText(QString("倒计时: %1 秒").arg(m_remainingSeconds));
m_remainingSeconds--;
} else {
// 倒计时结束,启用密码输入
m_countdownTimer->stop();
enablePasswordInput();
}
}
6.4 屏幕闪烁效果
cpp
void ReminderDialog::setupBlinkEffect()
{
// 创建闪烁定时器
QTimer *blinkTimer = new QTimer(this);
connect(blinkTimer, &QTimer::timeout, this, [this]() {
static bool isOriginalColor = true;
if (isOriginalColor) {
// 切换到闪烁颜色
setStyleSheet("background-color: #32CD32; color: white;");
} else {
// 恢复原始颜色
setStyleSheet("background-color: #2E8B57; color: white;");
}
isOriginalColor = !isOriginalColor;
});
blinkTimer->start(500); // 每500毫秒闪烁一次
// 🔑 关键改进:闪烁时长与倒计时同步
QTimer::singleShot(m_remainingSeconds * 1000, this, [this, blinkTimer]() {
blinkTimer->stop();
setStyleSheet("background-color: #2E8B57; color: white;");
});
}
7. 系统托盘功能
7.1 托盘图标设置
cpp
void MainWindow::setupTrayIcon()
{
// 检查系统支持
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
QMessageBox::critical(this, "系统托盘", "系统不支持托盘功能");
return;
}
// 创建托盘图标
m_trayIcon = new QSystemTrayIcon(this);
m_trayIcon->setIcon(QIcon(":/avatar.jpg"));
m_trayIcon->setToolTip("定时提醒工具");
// 创建托盘菜单
createTrayMenu();
// 连接信号
connect(m_trayIcon, &QSystemTrayIcon::activated,
this, &MainWindow::trayIconActivated);
// 显示托盘图标
m_trayIcon->show();
}
7.2 托盘菜单创建
cpp
void MainWindow::createTrayMenu()
{
m_trayMenu = new QMenu(this);
// 显示主窗口
m_showAction = new QAction("显示主窗口", this);
connect(m_showAction, &QAction::triggered, this, &MainWindow::showWindow);
// 开机自启
m_autoStartAction = new QAction("开机自启", this);
m_autoStartAction->setCheckable(true);
m_autoStartAction->setChecked(isAutoStartEnabled());
connect(m_autoStartAction, &QAction::triggered, this, &MainWindow::toggleAutoStart);
// 免打扰选项
createDoNotDisturbMenu();
// 退出程序
m_quitAction = new QAction("退出程序", this);
connect(m_quitAction, &QAction::triggered, this, &MainWindow::quitApplication);
// 添加到菜单
m_trayMenu->addAction(m_showAction);
m_trayMenu->addSeparator();
m_trayMenu->addAction(m_autoStartAction);
m_trayMenu->addSeparator();
addDoNotDisturbActions();
m_trayMenu->addSeparator();
m_trayMenu->addAction(m_quitAction);
m_trayIcon->setContextMenu(m_trayMenu);
}
7.3 窗口关闭处理
cpp
void MainWindow::closeEvent(QCloseEvent *event)
{
if (m_trayIcon && m_trayIcon->isVisible()) {
// 最小化到托盘而不是退出
hide();
event->ignore();
// 首次提示用户
static bool firstTime = true;
if (firstTime) {
m_trayIcon->showMessage(
"定时提醒工具",
"程序已最小化到系统托盘",
QSystemTrayIcon::Information,
2000
);
firstTime = false;
}
} else {
event->accept();
}
}
8. 数据持久化
8.1 保存提醒数据
cpp
void MainWindow::saveReminders()
{
QSettings settings("ReminderTool", "Reminders");
// 使用数组方式保存
settings.beginWriteArray("reminders");
for (int i = 0; i < m_reminders.size(); ++i) {
settings.setArrayIndex(i);
settings.setValue("time", m_reminders[i].time);
settings.setValue("message", m_reminders[i].message);
settings.setValue("triggered", m_reminders[i].triggered);
settings.setValue("isRecurring", m_reminders[i].isRecurring);
settings.setValue("recurringType", m_reminders[i].recurringType);
}
settings.endArray();
// 保存其他设置
settings.setValue("countdownSeconds", ui->countdownSpinBox->value());
}
8.2 加载提醒数据
cpp
void MainWindow::loadReminders()
{
QSettings settings("ReminderTool", "Reminders");
int size = settings.beginReadArray("reminders");
m_reminders.clear();
for (int i = 0; i < size; ++i) {
settings.setArrayIndex(i);
Reminder reminder;
reminder.time = settings.value("time").toDateTime();
reminder.message = settings.value("message").toString();
reminder.triggered = settings.value("triggered").toBool();
reminder.isRecurring = settings.value("isRecurring", false).toBool();
reminder.recurringType = settings.value("recurringType", 0).toInt();
// 处理周期性任务的时间调整
if (reminder.isRecurring && reminder.triggered) {
adjustRecurringTime(reminder);
}
m_reminders.append(reminder);
}
settings.endArray();
// 加载其他设置
int countdownSeconds = settings.value("countdownSeconds", 10).toInt();
ui->countdownSpinBox->setValue(countdownSeconds);
// 更新显示
updateReminderListDisplay();
}
9. 高级功能实现
9.1 开机自启功能
cpp
bool MainWindow::isAutoStartEnabled()
{
// 检查Windows注册表
QSettings settings(
"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
QSettings::NativeFormat
);
return settings.contains("ReminderTool");
}
void MainWindow::setAutoStart(bool enabled)
{
QSettings settings(
"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
QSettings::NativeFormat
);
if (enabled) {
// 添加到启动项
QString appPath = QCoreApplication::applicationFilePath();
appPath = QDir::toNativeSeparators(appPath);
settings.setValue("ReminderTool", appPath);
} else {
// 从启动项移除
settings.remove("ReminderTool");
}
}
9.2 免打扰模式
cpp
bool MainWindow::isDoNotDisturbActive()
{
if (!m_doNotDisturbEndTime.isValid()) {
return false;
}
QDateTime currentTime = QDateTime::currentDateTime();
if (currentTime >= m_doNotDisturbEndTime) {
// 免打扰时间已过,自动关闭
m_doNotDisturbEndTime = QDateTime();
updateDoNotDisturbMenu();
return false;
}
return true;
}
void MainWindow::toggleDoNotDisturb2Hours()
{
m_doNotDisturbEndTime = QDateTime::currentDateTime().addSecs(2 * 3600);
updateDoNotDisturbMenu();
if (m_trayIcon) {
m_trayIcon->showMessage(
"免打扰",
"已启用2小时免打扰模式",
QSystemTrayIcon::Information,
2000
);
}
}
9.3 快捷时间设置
cpp
void MainWindow::setQuickTime5Min()
{
QDateTime quickTime = QDateTime::currentDateTime().addSecs(5 * 60);
ui->dateEdit->setDate(quickTime.date());
ui->hourSpinBox->setValue(quickTime.time().hour());
ui->minuteSpinBox->setValue(quickTime.time().minute());
}
void MainWindow::setQuickTimeTomorrow()
{
QDateTime tomorrow = QDateTime::currentDateTime().addDays(1);
ui->dateEdit->setDate(tomorrow.date());
// 时间保持当前设置不变
}
10. UI美化与用户体验
10.1 倒计时结束界面美化
cpp
void ReminderDialog::enablePasswordInput()
{
// 美化倒计时结束提示
m_countdownLabel->setText("🎉 倒计时结束!现在可以输入密码了 🎉");
// 渐变背景样式
m_countdownLabel->setStyleSheet(
"font-size: 32px; "
"font-weight: bold; "
"margin: 20px; "
"color: #FFFFFF; "
"background: qlineargradient(x1:0, y1:0, x2:1, y2:1, "
"stop:0 #00FF7F, stop:0.5 #32CD32, stop:1 #228B22); "
"border: 3px solid #FFD700; "
"border-radius: 15px; "
"padding: 20px; "
"text-shadow: 2px 2px 4px rgba(0,0,0,0.5);"
);
// 美化密码输入框
beautifyPasswordInput();
// 美化按钮
beautifyCloseButton();
// 添加动画效果
addInputAnimation();
// 启用输入控件
m_passwordEdit->setEnabled(true);
m_closeButton->setEnabled(true);
m_passwordEdit->setFocus();
}
10.2 动画效果
cpp
void ReminderDialog::addInputAnimation()
{
// 为密码输入框添加"呼吸"动画
QPropertyAnimation *animation = new QPropertyAnimation(m_passwordEdit, "geometry");
animation->setDuration(1000);
animation->setLoopCount(3);
QRect originalGeometry = m_passwordEdit->geometry();
QRect expandedGeometry = originalGeometry.adjusted(-5, -2, 5, 2);
animation->setKeyValueAt(0, originalGeometry);
animation->setKeyValueAt(0.5, expandedGeometry);
animation->setKeyValueAt(1, originalGeometry);
animation->start(QAbstractAnimation::DeleteWhenStopped);
}
10.3 按钮悬停效果
cpp
void ReminderDialog::beautifyCloseButton()
{
m_closeButton->setStyleSheet(
"QPushButton {"
"font-size: 24px; "
"padding: 18px 30px; "
"background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
"stop:0 #98FB98, stop:0.5 #90EE90, stop:1 #7CFC00); "
"color: #2E8B57; "
"border: 2px solid #32CD32; "
"border-radius: 12px; "
"font-weight: bold;"
"} "
"QPushButton:hover {"
"background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
"stop:0 #ADFF2F, stop:0.5 #9AFF9A, stop:1 #90EE90); "
"border: 2px solid #228B22; "
"transform: scale(1.05);"
"} "
"QPushButton:pressed {"
"background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
"stop:0 #7CFC00, stop:0.5 #90EE90, stop:1 #98FB98);"
"}"
);
}
11. 调试与优化
11.1 常见问题解决
问题1:窗口无法置顶
cpp
// 解决方案:Windows特定API
#ifdef Q_OS_WIN
void ReminderDialog::ensureTopmost()
{
QTimer::singleShot(100, this, [this]() {
SetWindowPos((HWND)winId(), HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
});
}
#endif
问题2:变量作用域错误
cpp
// 错误:使用局部变量
QTimer::singleShot(countdownSeconds * 1000, ...);
// 正确:使用成员变量
QTimer::singleShot(m_remainingSeconds * 1000, ...);
问题3:内存泄漏
cpp
// 确保对话框自动删除
ReminderDialog *dialog = new ReminderDialog(...);
dialog->exec();
dialog->deleteLater(); // 重要:自动删除
这个实现指南展示了从零开始构建一个功能完整的桌面应用程序的完整过程,涵盖了Qt开发的核心技术和最佳实践。