15 KiB
15 KiB
项目架构说明
概述
本项目是一个基于 Qt5 的 Linux 应用程序,通过 DBus 监听系统锁屏事件,并在锁屏时自动停止所有 Paint 事件,以节省系统资源。
系统架构图
┌─────────────────────────────────────────────────────────────┐
│ Linux System │
│ ┌──────────────────────┐ ┌─────────────────────────┐ │
│ │ GNOME ScreenSaver │ │ systemd-logind │ │
│ │ (DBus Service) │ │ (DBus Service) │ │
│ └──────────┬───────────┘ └───────────┬─────────────┘ │
│ │ ActiveChanged(bool) │ Lock/Unlock │
└─────────────┼──────────────────────────────┼────────────────┘
│ │
│ DBus Signals │
│ │
┌─────────────┴──────────────────────────────┴────────────────┐
│ Qt Application (ScreenLockDemo) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ MainWindow │ │
│ │ ┌──────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ Control Panel│ │ Status Display │ │ │
│ │ │ - Enable Btn │ │ - Lock Status: 🔒/🔓 │ │ │
│ │ │ - Disable Btn│ │ - Paint Status: ✓/✗ │ │ │
│ │ │ - Reset Btn │ │ - Frame Count │ │ │
│ │ └──────────────┘ └────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ ↕ signals/slots │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ ScreenLockDetector │ │
│ │ - Monitors DBus signals │ │
│ │ - Emits: screenLocked() / screenUnlocked() │ │
│ │ - Manages connection to multiple DBus interfaces │ │
│ └─────────────────────┬──────────────────────────────────┘ │
│ ↓ lockStateChanged(bool) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ CustomWidget │ │
│ │ - Renders animations (60 FPS) │ │
│ │ - Controls paint events based on lock state │ │
│ │ - Draws: rotating circles, waves, gradients │ │
│ │ - Tracks frame count and statistics │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
核心组件详解
1. ScreenLockDetector (锁屏检测器)
职责:
- 连接到 Linux 系统的 DBus 服务
- 监听屏幕锁定/解锁事件
- 提供锁屏状态查询接口
- 发出标准化的 Qt 信号
关键方法:
bool initialize() // 初始化 DBus 连接
bool isScreenLocked() const // 查询当前锁屏状态
bool connectToGnomeScreenSaver() // 连接 GNOME 接口
bool connectToLoginManager() // 连接 systemd-logind 接口
void queryCurrentLockState() // 查询初始状态
信号:
void screenLocked() // 屏幕已锁定
void screenUnlocked() // 屏幕已解锁
void lockStateChanged(bool locked) // 状态改变(通用)
DBus 接口支持:
| 桌面环境 | DBus Service | 路径 | 接口 | 信号 |
|---|---|---|---|---|
| GNOME | org.gnome.ScreenSaver | /org/gnome/ScreenSaver | org.gnome.ScreenSaver | ActiveChanged(bool) |
| All (systemd) | org.freedesktop.login1 | /org/freedesktop/login1/session/auto | org.freedesktop.login1.Session | Lock(), Unlock() |
2. CustomWidget (自定义绘制组件)
职责:
- 实现动态动画效果
- 根据锁屏状态控制绘制
- 统计绘制性能数据
- 提供可视化反馈
绘制层次:
Layer 1: Background (渐变背景)
↓
Layer 2: Rotating Circles (旋转圆圈)
↓
Layer 3: Wave Effects (波浪效果)
↓
Layer 4: Status Info (状态文字)
动画参数:
- 刷新率:60 FPS (16ms/frame)
- 旋转角度:每帧 +2°
- 波浪相位:每帧 +0.05 rad
关键方法:
void setPaintingEnabled(bool enabled) // 控制绘制开关
void paintEvent(QPaintEvent *event) // 重写绘制事件
void drawBackground(QPainter &painter) // 绘制背景
void drawRotatingCircles(...) // 绘制旋转元素
void drawWaveEffect(...) // 绘制波浪
void drawStatusInfo(...) // 绘制状态信息
状态机:
[ENABLED] ←→ [DISABLED]
↓ ↓
Timer ON Timer OFF
↓ ↓
Draw frames Draw static
↓ ↓
Count++ Count unchanged
3. MainWindow (主窗口)
职责:
- 整合所有组件
- 提供用户界面
- 处理用户交互
- 实时状态展示
UI 布局:
┌─────────────────────────────────────┐
│ MainWindow │
├─────────────────────────────────────┤
│ ┌───────────────────────────────┐ │
│ │ CustomWidget │ │
│ │ (Animation Display Area) │ │
│ │ 600x400px │ │
│ └───────────────────────────────┘ │
├─────────────────────────────────────┤
│ ┌─── Manual Control ────────────┐ │
│ │ [Enable] [Disable] [Reset] │ │
│ └───────────────────────────────┘ │
├─────────────────────────────────────┤
│ ┌─── Status Information ────────┐ │
│ │ Detector: Active │ │
│ │ Lock: 🔓 UNLOCKED │ │
│ │ Paint: ✓ ENABLED │ │
│ │ Frames: 1234 │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
信号流向:
ScreenLockDetector → MainWindow → CustomWidget
screenLocked() onScreenLocked() setPaintingEnabled(false)
screenUnlocked() onScreenUnlocked() setPaintingEnabled(true)
数据流图
锁屏事件流
[User locks screen]
↓
[Linux System]
↓
[DBus Signal: Lock/ActiveChanged]
↓
[ScreenLockDetector::onSessionLocked()]
↓
[emit screenLocked()]
↓
[MainWindow::onScreenLocked()]
↓
[CustomWidget::setPaintingEnabled(false)]
↓
[m_animationTimer->stop()]
↓
[paintEvent() returns early]
↓
[Animation stops, resources saved]
解锁事件流
[User unlocks screen]
↓
[Linux System]
↓
[DBus Signal: Unlock/ActiveChanged]
↓
[ScreenLockDetector::onSessionUnlocked()]
↓
[emit screenUnlocked()]
↓
[MainWindow::onScreenUnlocked()]
↓
[CustomWidget::setPaintingEnabled(true)]
↓
[m_animationTimer->start(16)]
↓
[paintEvent() resumes drawing]
↓
[Animation resumes]
技术细节
Qt 信号槽机制
// 连接示例
connect(m_lockDetector, &ScreenLockDetector::screenLocked,
this, &MainWindow::onScreenLocked);
// 优势:
// - 类型安全
// - 解耦设计
// - 自动生命周期管理
DBus 连接管理
// Session Bus (用户会话)
QDBusConnection::sessionBus().connect(
"org.gnome.ScreenSaver", // Service
"/org/gnome/ScreenSaver", // Path
"org.gnome.ScreenSaver", // Interface
"ActiveChanged", // Signal
this, // Receiver
SLOT(onScreenSaverActiveChanged(bool))
);
// System Bus (系统级)
QDBusConnection::systemBus().connect(
"org.freedesktop.login1", // Service
"/org/freedesktop/login1/session/auto",
"org.freedesktop.login1.Session",
"Lock",
this,
SLOT(onSessionLocked())
);
Paint 事件优化
void CustomWidget::paintEvent(QPaintEvent *event)
{
// 快速退出:锁屏时不进行复杂绘制
if (!m_paintingEnabled) {
// 仅绘制静态状态信息
drawSimpleStatus();
return; // 节省 CPU 资源
}
// 正常绘制流程
drawBackground();
drawRotatingCircles();
drawWaveEffect();
drawStatusInfo();
m_frameCount++; // 统计
}
构建系统
CMake 配置流程
CMakeLists.txt
↓
find_package(Qt5)
↓
set(CMAKE_AUTOMOC ON) # 自动 MOC 处理
set(CMAKE_AUTOUIC ON) # 自动 UI 文件
set(CMAKE_AUTORCC ON) # 自动资源文件
↓
add_executable(ScreenLockDemo)
↓
target_link_libraries(Qt5::Core Qt5::Gui Qt5::Widgets Qt5::DBus)
编译流程
mkdir build && cd build
↓
cmake .. -DCMAKE_BUILD_TYPE=Release
↓
make -j$(nproc)
↓
./bin/ScreenLockDemo
性能特性
资源消耗对比
| 状态 | CPU使用 | GPU使用 | 内存 | 说明 |
|---|---|---|---|---|
| 绘制启用 | ~2-5% | ~1-3% | ~50MB | 60 FPS 动画 |
| 绘制禁用 | <1% | 0% | ~50MB | 仅监听 DBus |
| 锁屏期间 | <0.5% | 0% | ~50MB | 完全停止绘制 |
响应时间
- DBus 信号延迟:< 100ms
- 动画停止延迟:< 20ms (1-2 frames)
- 动画恢复延迟:< 20ms
扩展性设计
添加新的 DBus 接口
// 在 screenlockdetector.cpp 中添加
bool ScreenLockDetector::connectToKDEScreenSaver()
{
// 1. 创建 DBus 接口
m_kdeInterface = new QDBusInterface(
"org.kde.screensaver",
"/ScreenSaver",
"org.freedesktop.ScreenSaver",
QDBusConnection::sessionBus()
);
// 2. 连接信号
bool connected = QDBusConnection::sessionBus().connect(
"org.kde.screensaver",
"/ScreenSaver",
"org.freedesktop.ScreenSaver",
"ActiveChanged",
this,
SLOT(onKDEScreenSaverChanged(bool))
);
return connected;
}
添加新的动画效果
// 在 customwidget.cpp 中添加
void CustomWidget::drawParticleSystem(QPainter &painter)
{
// 1. 更新粒子位置
for (auto &particle : m_particles) {
particle.update(m_deltaTime);
}
// 2. 绘制粒子
for (const auto &particle : m_particles) {
particle.draw(painter);
}
}
// 在 paintEvent 中调用
void CustomWidget::paintEvent(QPaintEvent *event)
{
// ...
drawParticleSystem(painter);
}
测试策略
单元测试建议
// 测试锁屏检测器
void TestScreenLockDetector::testLockStateChange()
{
ScreenLockDetector detector;
QSignalSpy spy(&detector, &ScreenLockDetector::lockStateChanged);
// 模拟 DBus 信号
detector.onSessionLocked();
QCOMPARE(spy.count(), 1);
QVERIFY(detector.isScreenLocked());
}
// 测试绘制控制
void TestCustomWidget::testPaintingControl()
{
CustomWidget widget;
widget.setPaintingEnabled(false);
QVERIFY(!widget.isPaintingEnabled());
int initialCount = widget.getPaintFrameCount();
QTest::qWait(100); // 等待
QCOMPARE(widget.getPaintFrameCount(), initialCount);
}
集成测试场景
- 启动应用 → 验证动画运行
- 锁定屏幕 → 验证动画停止
- 解锁屏幕 → 验证动画恢复
- 手动禁用 → 验证控制有效
- 重置计数 → 验证统计准确
故障处理
错误处理策略
bool ScreenLockDetector::initialize()
{
bool gnomeOk = connectToGnomeScreenSaver();
bool loginOk = connectToLoginManager();
// 容错:至少一个接口成功即可
if (!gnomeOk && !loginOk) {
qWarning() << "No lock detection service available";
return false;
}
// 降级:查询当前状态(可能失败)
queryCurrentLockState();
return true;
}
日志级别
qDebug() // 调试信息(开发)
qInfo() // 一般信息
qWarning() // 警告信息(功能降级)
qCritical()// 严重错误
最佳实践
内存管理
- 使用 Qt 父子对象机制自动管理内存
- 显式 delete 前先判空
- 使用智能指针(如需要)
信号槽
- 优先使用新式语法(函数指针)
- 避免跨线程信号(本项目单线程)
- 断开不再需要的连接
绘制优化
- 使用 update() 而非 repaint()
- 避免在 paintEvent 中进行重计算
- 使用抗锯齿适度(性能影响)
总结
本项目展示了:
- ✅ Qt5 应用程序开发
- ✅ DBus 系统集成
- ✅ 信号槽机制运用
- ✅ 自定义绘制实现
- ✅ 事件驱动架构
- ✅ 跨组件通信
- ✅ 资源优化策略
适合作为 Qt + Linux 系统编程的学习参考。