【QT入门到晋级】QT项目中加入qml界面(包含源码)
前言
本篇的分享的需求场景是”在系统托盘的右键菜单中,增加一个点击之后,显示CPU占有率的圆形动态图“,系统托盘及托盘的其他右键菜单已经用qt来实现了,因为qt实现”动态图“的效果是比较差的,所以本文分享的是”CPU占有率“右键菜单,弹出的是一个qml窗口,即qt+qml结合的项目。
效果图
功能讲解
系统托盘的功能引用之前分享的博文,本文就不再详细讲解qt的系统托盘部分功能,主要讲解在此系统托盘的基础上,如何加入qml代码,并实现右键菜单功能。
【QT常用技术讲解】QSystemTrayIcon系统托盘-CSDN博客文章浏览阅读456次,点赞4次,收藏2次。项目中需要提供窗口的服务时,就会用到系统托盘。系统托盘支持右键菜单,也支持(左下角)系统消息提醒等实用的功能。_qsystemtrayiconhttps://blog.csdn.net/liangyuna8787/article/details/149550230?spm=1011.2124.3001.6209
第一步 增加CPU监控实现
新建一个C++ class类CpuMonitor,如下图操作到创建成功
以下是实现的代码
//cpumonitor.h
#ifndef CPUMONITOR_H
#define CPUMONITOR_H#include <QObject>
#include <QTimer>
#include <QProcess>
#include <QDebug>
class CpuMonitor : public QObject
{Q_OBJECT//定义QML可以访问的属性//int cpuUsage READ cpuUsage 告诉 Qt 元对象系统,获取此属性值时应调用 cpuUsage()成员函数//NOTIFY cpuUsageChanged 指定通知信号,当属性值变化时,CpuMonitor主动发送cpuUsageChanged()信号,qml需要接收此信号Q_PROPERTY(int cpuUsage READ cpuUsage NOTIFY cpuUsageChanged)
public:explicit CpuMonitor(QObject *parent = nullptr);int cpuUsage() const;signals:void cpuUsageChanged();private slots:void updateCpuUsage();private:int m_cpuUsage;QTimer *timer;qint64 lastIdleTime;qint64 lastTotalTime;void getCpuTimes(qint64 &idleTime, qint64 &totalTime);};#endif // CPUMONITOR_H
需要注意的是,一定要加上这一行代码,用于向qml发送信号:
//定义QML可以访问的属性//int cpuUsage READ cpuUsage 告诉 Qt 元对象系统,获取此属性值时应调用 cpuUsage()成员函数//NOTIFY cpuUsageChanged 指定通知信号,当属性值变化时,CpuMonitor主动发送cpuUsageChanged()信号,qml需要接收此信号Q_PROPERTY(int cpuUsage READ cpuUsage NOTIFY cpuUsageChanged)
通过定时器每秒中获取一次CPU,并发出cpuUsageChanged信号
//cpumonitor.cpp
#include "cpumonitor.h"
#include <QFile>
#include <QTextStream>
#include <QProcess>CpuMonitor::CpuMonitor(QObject *parent) : QObject(parent)
{// 初始化定时器timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &CpuMonitor::updateCpuUsage);timer->start(1000); // 每秒更新一次// 获取初始CPU时间updateCpuUsage();
}int CpuMonitor::cpuUsage() const
{return m_cpuUsage;
}void CpuMonitor::updateCpuUsage()
{qint64 idleTime, totalTime;getCpuTimes(idleTime, totalTime);if(lastTotalTime != 0 && lastIdleTime != 0) {qint64 totalDiff = totalTime - lastTotalTime;qint64 idleDiff = idleTime - lastIdleTime;if(totalDiff > 0) {int usage = static_cast<int>((totalDiff - idleDiff) * 100.0 / totalDiff);if(usage != m_cpuUsage) {m_cpuUsage = usage;emit cpuUsageChanged();}}}lastIdleTime = idleTime;lastTotalTime = totalTime;
}void CpuMonitor::getCpuTimes(qint64 &idleTime, qint64 &totalTime)
{idleTime = 0;totalTime = 0;QProcess process;process.start("wmic", QStringList() << "cpu" << "get" << "LoadPercentage");process.waitForFinished();QString output = process.readAllStandardOutput();// 修复: 使用兼容的方式分割字符串QStringList lines = output.split('\n', QString::SkipEmptyParts);if(lines.size() >= 2) {QString usageStr = lines[1].trimmed();bool ok;int usage = usageStr.toInt(&ok);if(ok) {m_cpuUsage = usage;emit cpuUsageChanged();}}
}
第二步 增加系统托盘菜单
增加新的右键菜单,及对应的窗口显示、关闭相关的函数
//trayicon.h
#include <QQmlApplicationEngine>
#include "cpumonitor.h"class TrayIcon : public QSystemTrayIcon
{Q_OBJECT
public:void setCpuMonitor(CpuMonitor *monitor);void setQmlEngine(QQmlApplicationEngine *engine);private slots:void showCpuMonitor();void hideCpuMonitor();private:CpuMonitor *cpuMonitor;QQmlApplicationEngine *qmlEngine;QObject *qmlRootObject;
};
//trayicon.cpp
void TrayIcon::createTrayMenu()
{QAction *showAction = new QAction("显示CPU占用", this);QAction *hideAction = new QAction("隐藏监控窗口", this);menu->addAction(showAction);menu->addAction(hideAction);connect(showAction, &QAction::triggered, this, &TrayIcon::showCpuMonitor);connect(hideAction, &QAction::triggered, this, &TrayIcon::hideCpuMonitor);
}void TrayIcon::setCpuMonitor(CpuMonitor *monitor)
{cpuMonitor = monitor;
}void TrayIcon::setQmlEngine(QQmlApplicationEngine *engine)
{qmlEngine = engine;if(qmlEngine && !qmlEngine->rootObjects().isEmpty()) {qmlRootObject = qmlEngine->rootObjects().first();}
}void TrayIcon::showCpuMonitor()
{if(qmlRootObject) {QQuickWindow *window = qobject_cast<QQuickWindow*>(qmlRootObject);if(window) {window->show();window->raise();window->requestActivate();}}
}void TrayIcon::hideCpuMonitor()
{if(qmlRootObject) {QQuickWindow *window = qobject_cast<QQuickWindow*>(qmlRootObject);if(window) {window->hide();}}
}
交互流程:
第三步 加入qml文件
创建完成之后,在项目的资源目录下可以看到main.qml文件,以下给出main.qml的代码
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12/*** 功能:* - 实时显示CPU使用率* - 可拖动位置* - 默认显示在桌面右下角* - 透明背景,圆形设计*/
Window {id: root // 窗口根元素标识符,可以随便命名width: 120 // 窗口宽度height: 120 // 窗口高度color: "transparent" // 窗口背景完全透明flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint // 窗口标志:无边框(FramelessWindowHint) + 置顶(WindowStaysOnTopHint)title: "CPU占用率监控" // 窗口标题(标题栏),上面已经去掉边框了,此标题看不见// 设置窗口初始位置在右下角x: Screen.desktopAvailableWidth - width - 20y: Screen.desktopAvailableHeight - height - 20// 可拖动区域(覆盖整个窗口)MouseArea {id: dragArea // 拖动区域标识符anchors.fill: parent // 覆盖整个父窗口property point dragStart: "0,0" // 拖动开始时鼠标的绝对位置property point windowStart: "0,0" // 拖动开始时窗口的位置// 鼠标按下事件处理--实现拖拽功能onPressed: {// 记录初始位置dragStart = Qt.point(mouse.x, mouse.y) // 记录鼠标绝对位置windowStart = Qt.point(root.x, root.y) // 记录窗口当前位置cursorShape = Qt.SizeAllCursor; // 设置光标为移动光标}// 鼠标位置改变事件处理(拖动中)onPositionChanged: {if (pressed) { // 仅在鼠标按下时处理// 计算鼠标移动距离var deltaX = mouse.x - dragStart.xvar deltaY = mouse.y - dragStart.y// 设置新窗口位置root.x = windowStart.x + deltaXroot.y = windowStart.y + deltaY}}// 鼠标释放事件处理onReleased: {cursorShape = Qt.ArrowCursor;// 恢复光标为默认箭头}// 悬停时显示拖动光标hoverEnabled: true // 启用悬停检测onEntered: cursorShape = Qt.SizeAllCursor; // 鼠标进入区域时显示移动光标onExited: cursorShape = Qt.ArrowCursor; // 鼠标离开区域时恢复默认光标}// 背景(圆形)Rectangle {id: backgroundwidth: root.widthheight: root.heightradius: width / 2 // 半径设置为宽度的一半,形成圆形color: "#333333"anchors.centerIn: parent // 在父元素中居中border.color: "#444444" // 边框颜色border.width: 2 // 边框宽度}// 环形进度条轨道Rectangle {id: progressTrackwidth: 120height: 120radius: width / 2 // 圆形半径color: "transparent" // 透明填充border.color: "#444444" // 边框颜色border.width: 5 // 边框宽度anchors.centerIn: parent // 在父元素中居中}// 环形进度条Rectangle {id: progressBarwidth: 120height: 120radius: width / 2 // 圆形半径color: "transparent" // 透明填充border.color: "#4CAF50" // 边框颜色border.width: 5 // 边框宽度anchors.centerIn: parent // 在父元素中居中rotation: -90 // 初始旋转-90度(使进度从顶部开始)// 旋转属性动画效果Behavior on rotation {RotationAnimation { // 旋转动画duration: 500 // 动画持续时间500毫秒direction: RotationAnimation.Shortest // 使用最短路径旋转}}}// CPU使用率文本Text {id: cpuTexttext: cpuMonitor.cpuUsage + "%" //调用cpuMonitor类的cpuUsage函数获取返回值color: "white"font.pointSize: 24 // 字体大小font.bold: true // 粗体anchors.centerIn: parent // 在父元素中居中}// CPU标签Text {text: "CPU"color: "#AAAAAA"font.pointSize: 12 // 字体大小font.bold: true // 粗体anchors {top: cpuText.bottom // 锚定到百分比文本底部topMargin: 5 // 上边距5像素horizontalCenter: parent.horizontalCenter // 水平居中}}//组件加载完成后初始化进度条Component.onCompleted: updateProgress() // 组件完成加载时调用updateProgress//根据CPU使用率更新进度条角度function updateProgress() {var usage = cpuMonitor.cpuUsage; //调用cpuMonitor类的cpuUsage函数获取返回值var angle = (usage / 100) * 360; //计算对应角度(360度圆周)progressBar.rotation = angle - 90; //设置进度条旋转角度(调整-90度起始位置)}//当CPU使用率变化时更新进度条Connections {target: cpuMonitor // 连接目标:CPU监控对象//捕获cpuMonitor发送的cpuUsageChanged信号,注意在qml中onCpuUsageChanged()一定要与cpuUsageChanged信号对应,规则是On+大小字母开头function onCpuUsageChanged() {updateProgress();}}
}
以下也给出qml的一些知识点
主窗口结构
元素 | 用途 | 说明 |
---|---|---|
Window | 创建应用程序主窗口 | 无边框透明窗口,始终置顶显示 |
├── | 窗口标识 | 用于内部引用的唯一标识符 |
├── | 设置窗口宽度 | 固定宽度为 200 像素 |
├── | 设置窗口高度 | 固定高度为 200 像素 |
├── | 设置窗口背景 | 完全透明背景,只显示内容元素 |
├── `flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint` | 设置窗口属性 |
├── | 设置窗口标题 | 用于调试和系统识别的标题 |
└── | 设置窗口位置 | 初始位置在屏幕右下角 |
交互元素
元素 | 用途 | 说明 |
---|---|---|
MouseArea | 处理鼠标交互 | 整个窗口的拖动区域 |
├── | 拖动区域标识 | 用于引用拖动区域的标识符 |
├── | 填充父元素 | 覆盖整个窗口区域 |
├── | 存储初始鼠标位置 | 记录拖动开始时的鼠标绝对坐标 |
├── | 存储初始窗口位置 | 记录拖动开始时的窗口位置 |
├── | 鼠标按下事件 | 记录初始位置,设置移动光标 |
├── | 鼠标移动事件 | 计算并更新窗口位置 |
├── | 鼠标释放事件 | 恢复默认光标 |
├── | 启用悬停检测 | 允许鼠标悬停时改变光标 |
├── | 鼠标进入区域 | 显示移动光标(↔) |
└── | 鼠标离开区域 | 恢复默认箭头光标 |
视觉元素
元素 | 用途 | 说明 |
---|---|---|
Rectangle (background) | 创建背景圆形 | CPU 监控器的背景 |
├── id: background | 背景标识 | 用于引用背景的标识符 |
├── width: 120 | 设置宽度 | 圆形直径为 120 像素 |
├── height: 120 | 设置高度 | 圆形直径为 120 像素 |
├── radius: width / 2 | 设置圆角半径 | 创建完美圆形 |
├── color: "#333333" | 设置填充颜色 | 深灰色背景 |
├── border.color: "#444444" | 设置边框颜色 | 稍亮的灰色边框 |
├── border.width: 2 | 设置边框宽度 | 2 像素边框 |
└── anchors.centerIn: parent | 设置位置 | 在窗口中居中 |
Rectangle (progressTrack) | 创建进度条轨道 | 进度条的背景轨道 |
├── id: progressTrack | 轨道标识 | 用于引用轨道的标识符 |
├── width: 120 | 设置宽度 | 与背景相同 |
├── height: 120 | 设置高度 | 与背景相同 |
├── radius: width / 2 | 设置圆角半径 | 创建完美圆形 |
├── color: "transparent" | 设置填充颜色 | 透明填充 |
├── border.color: "#444444" | 设置边框颜色 | 灰色边框 |
├── border.width: 5 | 设置边框宽度 | 5 像素边框 |
└── anchors.centerIn: parent | 设置位置 | 在窗口中居中 |
Rectangle (progressBar) | 创建进度条 | 显示 CPU 使用率的进度条 |
├── id: progressBar | 进度条标识 | 用于引用进度条的标识符 |
├── width: 120 | 设置宽度 | 与轨道相同 |
├── height: 120 | 设置高度 | 与轨道相同 |
├── radius: width / 2 | 设置圆角半径 | 创建完美圆形 |
├── color: "transparent" | 设置填充颜色 | 透明填充 |
├── border.color: "#4CAF50" | 设置边框颜色 | 绿色进度条 |
├── border.width: 5 | 设置边框宽度 | 5 像素边框 |
├── rotation: -90 | 设置初始旋转 | 从顶部开始显示进度 |
└── anchors.centerIn: parent | 设置位置 | 在窗口中居中 |
Behavior | 创建属性动画 | 控制进度条旋转动画 |
├── on rotation | 目标属性 | 当旋转属性变化时触发动画 |
└── RotationAnimation | 旋转动画 | 具体的动画实现 |
├── duration: 500 | 动画持续时间 | 500 毫秒动画 |
└── direction: RotationAnimation.Shortest | 动画方向 | 使用最短路径旋转 |
Text (cpuText) | 显示 CPU 使用率 | 中央大号百分比数字 |
├── id: cpuText | 文本标识 | 用于引用文本的标识符 |
├── text: cpuMonitor.cpuUsage + "%" | 设置文本内容 | 显示 CPU 使用率百分比 |
├── color: "white" | 设置文本颜色 | 白色文字 |
├── font.pointSize: 24 | 设置字体大小 | 24 磅字号 |
├── font.bold: true | 设置字体样式 | 粗体显示 |
└── anchors.centerIn: parent | 设置位置 | 在窗口中居中 |
Text (CPU 标签) | 显示 CPU 标签 | "CPU" 文字标签 |
├── text: "CPU" | 设置文本内容 | "CPU" 文字 |
├── color: "#AAAAAA" | 设置文本颜色 | 浅灰色文字 |
├── font.pointSize: 12 | 设置字体大小 | 12 磅字号 |
├── font.bold: true | 设置字体样式 | 粗体显示 |
├── anchors.top: cpuText.bottom | 垂直位置 | 位于百分比文本下方 |
├── anchors.topMargin: 5 | 上边距 | 5 像素间距 |
└── anchors.horizontalCenter: parent.horizontalCenter | 水平位置 | 水平居中 |
功能逻辑
元素 | 用途 | 说明 |
---|---|---|
Component.onCompleted | 组件初始化回调 | 组件加载完成后执行 |
└── updateProgress() | 调用更新函数 | 初始化进度条位置 |
function updateProgress() | 更新进度条 | 计算并设置进度条角度 |
├── var usage = cpuMonitor.cpuUsage | 获取 CPU 使用率 | 从 C++ 对象获取值 |
├── var angle = (usage / 100) * 360 | 计算角度 | 将百分比转换为角度 |
└── progressBar.rotation = angle - 90 | 设置进度条旋转 | 从顶部开始显示进度 |
Connections | 信号连接 | 连接 C++ 对象信号 |
├── target: cpuMonitor | 目标对象 | 连接的 C++ CPU 监控对象 |
└── onCpuUsageChanged | 信号处理 | CPU 使用率变化时回调 |
└── updateProgress() | 更新进度条 | 调用更新函数 |
pro文件引入qml
QT += core gui quick qml #增加quick qml# 添加QML文件到资源
DISTFILES += \main.qml
main.cpp引入qml引擎
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
#include "trayicon.h"
#include "cpumonitor.h"
int main(int argc, char *argv[])
{QApplication app(argc, argv);app.setQuitOnLastWindowClosed(false);// 创建CPU监控对象CpuMonitor cpuMonitor;// 创建QML引擎QQmlApplicationEngine engine;engine.rootContext()->setContextProperty("cpuMonitor", &cpuMonitor);engine.load(QUrl("qrc:/main.qml"));TrayIcon tray;tray.setCpuMonitor(&cpuMonitor);//传入CpuMonitor tray.setQmlEngine(&engine);//初始化qml引擎tray.show();return app.exec();
}
篇尾
与qt相比,qml更加美观,但是比较吃性能,看需求场景来开发,下一篇我会分享完全是qml的项目,其特点是前后端分离,适合有前端人员的项目组,能加快开发进度。