Qt学习之旅 I

embedded/2024/9/20 11:23:09/ 标签: qt, 学习, 开发语言, C++, windows, linux

构建一个跨平台的应用(Create A Cross-Platform Application)

目录

构建一个跨平台的应用(Create A Cross-Platform Application)

设计模式

开始构建


Qt是跨平台的C++框架,这里,我们将会构建一个简单的C++跨平台项目来熟悉QT是如何实现简单的跨平台的。

我们将要构建的是一个动态查看,不同操作系统下操作系统运行时的一些状态,这就是一个跨平台的需求。

设计模式

要从 OS(操作系统)检索 CPU 和内存使用情况,我们将使用一些特定于平台的代码。 为了成功完成此任务,我们将使用两种设计模式:

  1. 策略模式:这是一个描述功能的接口(例如,检索 CPU 使用情况),特定行为(在 Windows/macOS/Linux 上检索 CPU 使用情况)将在实现此接口的子类中执行。

  2. pIMPL法:pointer to implements方法

  3. 单例模式:此模式保证给定类只有一个实例。此实例将通过唯一访问点轻松访问。

  4. 工厂模式:返回对应子类的实例

我们将会使用一个单例:System_Info,这是可以理解的因为我们的应用软件只会跑在一个操作系统上,因此没有必要创建多个实例,这种方式广泛的应用在那些维护全局状态的对象当中,也是一个常见的设计模式了。

开始构建

我们刚刚大致的想法就是如此,现在我们开始构建代码。我们仔细思考,作为用户,他只会关心接口的功能而不在乎里面的实现。为此,我们需要做的是使用pIMPL法,把设计到具体的项目平台的代码给他封装起来,这样可以减轻我们的管理负担。

因此,我们先带有尝试性质的——设计这些必要的接口:

#ifndef SYSTEM_INFOIMPL_H
#define SYSTEM_INFOIMPL_H
​
#include <QtClassHelperMacros>
​
/*The Actual Action classAll Bases For Every Possible OperatingSystem
*/
class System_InfoImpl {
public:System_InfoImpl() = default;Q_DISABLE_COPY(System_InfoImpl);virtual ~System_InfoImpl() = default;// Impl Interfacesvirtual void   _initWork()       = 0;virtual double _cpuLoadAverage() = 0;virtual double _memoryUsed()     = 0;
};
​
#endif  // SYSTEM_INFOIMPL_H

可以看到,我们的IMPL接口自身也被抽象为接口类,这是合理的——每一个操作系统获取内存和CPU的状态都不一样,需要我们更加具体的类实现。

这样,我们的System_Info这个前端接口,就只是单纯的转发请求到System_InfoImpl类中,这个类在不同的平台被初始化为不同的类中去了。

IMPL方法具备这些优点,对于那些强调不必关心内部实现的方法中:

  • 降低耦合

  • 信息隐藏

  • 降低编译依赖,提高编译速度

  • 接口与实现分离

下面,我们就来看看Windows平台下的如何实现

#ifndef SYSTEM_INFOWINDOWSIMPL_H
#define SYSTEM_INFOWINDOWSIMPL_H
#include <QList>
#include <array>
#include "System_InfoImpl.h"
​
using FILETIME = struct _FILETIME;  // Windows平台下的FILETIME接口
​
class System_InfoWindowsImpl : public System_InfoImpl {
public:System_InfoWindowsImpl();Q_DISABLE_COPY(System_InfoWindowsImpl);~System_InfoWindowsImpl() = default;
​// Implsvoid   _initWork() override;double _cpuLoadAverage() override;double _memoryUsed() override;
​struct WindowsTools {// 辅助函数static qulonglong fromWindowsFileTime(const FILETIME& fileTime);};
​
private:struct Labels {enum class Label { IDLE = 0, KERN = 1, USER = 2, SIZE };static constexpr short to_index(Label l) {return static_cast<short>(l);}};void _refreshCPURawData();std::array<qulonglong, static_cast<short>(Labels::Label::SIZE)>currentCPULoad;
};
​
#endif  // SYSTEM_INFOWINDOWSIMPL_H
​

具体的实现,可以参考任何一本Windows编程类的书进行学习,这里只是给出实现供各位参考

#include "System_InfoWindowsImpl.h"
#include <Windows.h>
System_InfoWindowsImpl::System_InfoWindowsImpl() : System_InfoImpl() {
}
​
qulonglong System_InfoWindowsImpl::WindowsTools::fromWindowsFileTime(const FILETIME& fileTime) {ULARGE_INTEGER integer;integer.LowPart  = fileTime.dwLowDateTime;integer.HighPart = fileTime.dwHighDateTime;return integer.QuadPart;
}
​
void System_InfoWindowsImpl::_initWork() {_refreshCPURawData();
}
​
void System_InfoWindowsImpl::_refreshCPURawData() {FILETIME idle, kernel, user;::GetSystemTimes(&idle, &kernel, &user);currentCPULoad[Labels::to_index(Labels::Label::IDLE)] =WindowsTools::fromWindowsFileTime(idle);
​currentCPULoad[Labels::to_index(Labels::Label::KERN)] =WindowsTools::fromWindowsFileTime(kernel);
​currentCPULoad[Labels::to_index(Labels::Label::USER)] =WindowsTools::fromWindowsFileTime(user);
}
​
double System_InfoWindowsImpl::_cpuLoadAverage() {std::array<qulonglong, static_cast<short>(Labels::Label::SIZE)> previous =currentCPULoad;_refreshCPURawData();
#define FAST_CALC(var_name, LABEL_NAME)                               \qulonglong var_name =                                             \currentCPULoad[Labels::to_index(Labels::Label::LABEL_NAME)] - \previous[Labels::to_index(Labels::Label::LABEL_NAME)]FAST_CALC(cur_idle, IDLE);FAST_CALC(cur_kern, KERN);FAST_CALC(cur_user, USER);
#undef FAST_CALCqulonglong systems = cur_kern + cur_user;
​double percentage = (systems - cur_idle) * 100.0 / (double)systems;return qBound(0.0, percentage, 100.0);
}
​
double System_InfoWindowsImpl::_memoryUsed() {MEMORYSTATUSEX mem;mem.dwLength = sizeof(MEMORYSTATUSEX);GlobalMemoryStatusEx(&mem);return (mem.ullTotalPhys - mem.ullAvailPhys) * 100.0 / mem.ullTotalPhys;
}

但是这并并有结束,我们还差了IMPL与INTERFACE的部分的鸿沟,为此,我们需要做的是,将类的实现类移动到一个工厂类来负责实现,工厂来裁决生成如何的实现类去!

#ifndef SYSTEM_INFOIMPL_FACTORY_H
#define SYSTEM_INFOIMPL_FACTORY_H
#include "System_InfoImpl.h"
class System_InfoImpl_Factory {
public:static System_InfoImpl* createImplementInstance();
};
​
#endif  // SYSTEM_INFOIMPL_FACTORY_H
#include "System_InfoImpl_Factory.h"
#include <QtGlobal>
#ifdef Q_OS_WIN
#include "System_InfoWindowsImpl.h"
​
static System_InfoWindowsImpl* instances() {return new System_InfoWindowsImpl;
}
​
#elif defined(Q_OS_LINUX)
// Waiting Implements
#endif
​
System_InfoImpl* System_InfoImpl_Factory::createImplementInstance() {return instances();
}

现在,我们就可以使用工厂和抽象的实现类,完成我们对系统信息前端类的实现了:

#ifndef SYSTEM_INFO_H
#define SYSTEM_INFO_H
#include <QtClassHelperMacros>  // Q_DISABLE_COPY
​
class System_InfoImpl;
​
class System_Info {
public:System_Info();Q_DISABLE_COPY(System_Info);~System_Info();// Functionsdouble cpuLoadAverage();double memoryUsed();
​
private:void             createAccordingPlatform();System_InfoImpl* impl;
};
​
#endif  // SYSTEM_INFO_H
​
#include "System_Info.h"
#include "System_InfoImpl.h"
#include "System_InfoImpl_Factory.h"
​
System_Info::System_Info() {impl = System_InfoImpl_Factory::createImplementInstance();
}
​
double System_Info::cpuLoadAverage() {return impl->_cpuLoadAverage();
}
​
double System_Info::memoryUsed() {return impl->_memoryUsed();
}
​
System_Info::~System_Info() {delete impl;
}

看起来相当的简洁!

我们下面就可以使用这个接口了:

08:50:24: Starting D:\QT projects\SystemInfoChecker\build\Desktop_Qt_6_6_1_MSVC2019_64bit-Debug\debug\SystemInfoChecker.exe...
50   36.7122

当然Linux的实现如法炮制的,这里放一下源码:

  1. 首先在pro文件修改成如下的代码:

QT       += core gui
​
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
​
CONFIG += c++17
​
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
​
SOURCES += \System_Info.cpp \System_InfoImpl.cpp \System_InfoImpl_Factory.cpp \main.cpp \systeminfowindow.cpp
​
HEADERS += \System_Info.h \System_InfoImpl.h \System_InfoImpl_Factory.h \systeminfowindow.h
​
FORMS += \systeminfowindow.ui
​
# 这里体现了跨平台的地方
# 对于Windows,向工具链提供这些文件
windows {HEADERS += System_InfoWindowsImpl.h \
​SOURCES += System_InfoWindowsImpl.cpp \
}
​
# 对Linux是另一些
linux {HEADERS += system_infolinuximpl.h \
​SOURCES += system_infolinuximpl.cpp \
}
​
​
​
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

  1. 写一个实现文件对

h文件

#ifndef SYSTEM_INFOLINUXIMPL_H
#define SYSTEM_INFOLINUXIMPL_H
#include "System_InfoImpl.h"
#include <QByteArray>
​
class System_InfoLinuxImpl : public System_InfoImpl
{
public:System_InfoLinuxImpl();Q_DISABLE_COPY(System_InfoLinuxImpl)virtual ~System_InfoLinuxImpl() = default;// Implsvoid   _initWork() override;double _cpuLoadAverage() override;double _memoryUsed() override;
​
​
private:struct LinuxTools{static QByteArray fromLinuxStatFile();};
​
​struct Labels {enum class Label { IDLE = 0, KERN = 1, USER = 2, USER_NICE = 3, SIZE };static constexpr short to_index(Label l) {return static_cast<short>(l);}};void _refreshCPURawData();std::array<qulonglong, static_cast<short>(Labels::Label::SIZE)>currentCPULoad;
​
​
};
​
#endif // SYSTEM_INFOLINUXIMPL_H
​

CPP文件

#include "system_infolinuximpl.h"
#include <QFile>
#include <sys/types.h>
#include <sys/sysinfo.h>
​
QByteArray System_InfoLinuxImpl::LinuxTools::fromLinuxStatFile(){QFile file("/proc/stat");file.open(QIODevice::ReadOnly);QByteArray dataLine = file.readLine();file.close();return dataLine;
}
​
System_InfoLinuxImpl::System_InfoLinuxImpl() : System_InfoImpl(){
​
}
​
void System_InfoLinuxImpl::_initWork(){_refreshCPURawData();
}
​
void System_InfoLinuxImpl::_refreshCPURawData(){QByteArray fromFile = LinuxTools::fromLinuxStatFile();qulonglong tol_user = 0, userNice = 0, sys = 0, idle = 0;std::sscanf(fromFile.data(), "cpu %llu %llu %llu %llu", &tol_user, &userNice, &sys, &idle);
​
#define FAST_REG(LABEL_NAME, var) \currentCPULoad[Labels::to_index(Labels::Label::LABEL_NAME)] = var
​FAST_REG(IDLE, idle);FAST_REG(USER, tol_user);FAST_REG(USER_NICE, userNice);FAST_REG(KERN, sys);
​
#undef FAST_REG
}
​
double System_InfoLinuxImpl::_cpuLoadAverage()
{std::array<qulonglong, static_cast<short>(Labels::Label::SIZE)> previous =currentCPULoad;_refreshCPURawData();
​
#define GET_TIME(LABEL_NAME) \currentCPULoad[Labels::to_index(Labels::Label::LABEL_NAME)] - \previous[Labels::to_index(Labels::Label::LABEL_NAME)]
​double overall = GET_TIME(USER) + GET_TIME(KERN) + GET_TIME(USER_NICE);double tol = overall + GET_TIME(IDLE);
​double per = overall * 100.0 / tol;return qBound(0.0, per, 100.0);
}
​
double System_InfoLinuxImpl::_memoryUsed()
{struct sysinfo meminfo;sysinfo(&meminfo);
​qulonglong tolMem = meminfo.totalram;tolMem += meminfo.totalswap;tolMem *= meminfo.mem_unit;
​qulonglong used = meminfo.totalram - meminfo.freeram;used += meminfo.totalswap - meminfo.freeswap;used *= meminfo.mem_unit;
​double per = used * 100.0 / tolMem;return qBound(0.0, per, 100.0);
}
  1. 向工厂提供初始化一个IMPL类的实例方法:

#elif defined(Q_OS_LINUX)
#include "system_infolinuximpl.h"
static System_InfoLinuxImpl* instances() {return new System_InfoLinuxImpl;
}

现在我们的代码可以无缝的游离在两个平台之间而无需改动代码,就可以编译运行了。


http://www.ppmy.cn/embedded/114142.html

相关文章

上传富文本插入文件时报错:JSON parse error: Unexpected character解决办法

方式一&#xff08;加密解密&#xff09;&#xff1a; 1.前端 &#xff08;1&#xff09;安装 crypto-js npm install crypto-js&#xff08;2&#xff09;util下引入asc.js asc.js import CryptoJS from crypto-js// 需要和后端一致 const KEY CryptoJS.enc.Utf8.parse(…

前端开发规范

前端开发规范 编写背景&#xff1a;当前项目前端开发过程中&#xff0c;每个人有不同的编码风格&#xff0c;这就导致同一个模块不同人开发时可能产生阅读不方便的情况&#xff0c;这对于项目的长久开发是不利的&#xff0c;所以编写这套前端开发规范。 编写目的&#xff1a;避…

google map小叉号不显示

背景需求 需要在uniapp中接入google地图,研究了一番,都没有找到合适的,现在说一下教程。 效果图 前期工作 这两点缺一不可,否则你啥也看不到。 1、电脑安装L-O-U梯 用于访问G-OO-G-LE的API或者创建google map key。 2、手机安装L-O-U梯 用于显示google地图。我就是手…

C++ ——string的模拟实现

目录 前言 浅记 1. 构造函数 2. 拷贝构造函数 2.1 拷贝构造传统写法 2.2 拷贝构造现代写法 3. swap 4. 赋值重载 4.1 赋值重载的传统写法 4.2 赋值重载的现代写法1 4.3 赋值重载的现代写法2 5. 析构函数 6. reserve&#xff08;扩容&#xff09; 7. push_back&am…

Windows10安装cuda11.3.0+cudnn8.5.0,以及创建conda虚拟环境(pytorch)

1、检查电脑驱动版本为561.09&#xff0c;选择cuda版本&#xff0c;下图可知cuda版本<12.6。 nvidia-smi #查看驱动版本&#xff0c;以及最大可以安装的cuda版本 2、Anaconda3-2024.06-1-Windows-x86_64.exe下载&#xff1a; 官网&#xff1a;https://www.baidu.com/link?…

Unity教程(十五)敌人战斗状态的实现

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

如何利用 Visual Studio 和 AI 工具实现高效编程

哪个编程工具让你的工作效率翻倍? 在现代软件开发的世界中,编程效率的提升对开发者来说至关重要。高效编程不仅仅是编写更多的代码,还包括如何减少重复劳动、提高代码质量、加快调试和测试流程等。而 Visual Studio 作为一个功能强大的开发环境(IDE),配合各种 AI 工具,…

Java入门:09.Java中三大特性(封装、继承、多态)02

2 继承 需要两个类才能实现继承的效果。 比如&#xff1a;类A 继承 类B A类 称为 子类 &#xff0c; 衍生类&#xff0c;派生类 B类 称为 父类&#xff0c;基类&#xff0c;超类 继承的作用 子类自动的拥有父类的所有属性和方法 &#xff08;父类编写&#xff0c;子类不需要…

基于vue框架的宠物领养管理系统88v55(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,宠物分类,宠物信息,领养信息,宠物动态,捐赠物资,领养进度,友情链接 开题报告内容 基于Vue框架的宠物领养管理系统开题报告 一、项目背景与意义 随着社会的进步和人们生活水平的提高&#xff0c;宠物已成为许多家庭不可或缺的一部…

node+express部署多套vue3项目,总404页面由node控制,子404页面由子vue控制,node路由重定向

const express require(express) const history require(connect-history-api-fallback) const { createProxyMiddleware } require(http-proxy-middleware) const cors require(cors)let app express()app.use(cors()) app.use(history())// //匹配api开头的请求&#xf…

利用Leaflet.js和turf.js创建交互式地图:航道路线绘制

引言 在现代Web应用中&#xff0c;地图的交互性是提供丰富用户体验的关键。Leaflet.js是一个轻量级的开源JavaScript库&#xff0c;它提供了简单易用的API来构建交云的地图。与此同时&#xff0c;turf.js作为一个强大的地理空间分析库&#xff0c;能够处理复杂的地理数据操作。…

【前端基础篇】JavaScript之DOM介绍

文章目录 前言WebAPI背景知识什么是WebAPI什么是APIAPI参考文档 DOM基本概念什么是DOMDOM树查找HTML元素方法概览1. document.getElementById(id)2.document.getElementsByTagName(name)3. document.getElementsByClassName(name)4. document.querySelector(CSS选择器)5. docum…

C++ std::find函数 容器元素查找

简介 std::find函数是C标准库内非常实用的一个函数&#xff0c;主要用于在给定范围内查找某个元素&#xff0c;如果找到该元素&#xff0c;则返回指向该元素的迭代器&#xff1b;如果没有找到&#xff0c;则返回指向范围末尾的迭代器&#xff08;即 end() &#xff09;。 fin…

解锁定位服务:Flutter应用中的高德地图定位

前言 在现代移动应用开发中&#xff0c;定位服务已成为一项基本功能&#xff0c;它使得应用能够获取用户的地理位置信息&#xff0c;为用户提供更加个性化的服务。 Flutter 作为跨平台的移动应用开发框架&#xff0c;支持集成多种服务&#xff0c;包括定位服务。 本文将介绍如…

Golang | Leetcode Golang题解之第405题数字转换为十六进制数

题目&#xff1a; 题解&#xff1a; func toHex(num int) string {if num 0 {return "0"}sb : &strings.Builder{}for i : 7; i > 0; i-- {val : num >> (4 * i) & 0xfif val > 0 || sb.Len() > 0 {var digit byteif val < 10 {digit 0…

Bio-Linux-shell详解-1-从0开始

21世纪是数据的世纪&#xff0c;蓬勃发展的生物学积累了大量的数据&#xff0c;急需计算生物学、生物信息学及系统生物学等交叉学科大放异彩&#xff0c;而windows作为我们最熟悉的操作平台&#xff0c;并不能承担如此巨大的工作量&#xff0c;课题组的服务器因此承担了这个责任…

前端开发之原型模式

介绍 原型模式本质就是借用一个已有的实例做原型&#xff0c;在这原型基础上快速复制出一个和原型一样的一个对象。 class CloneDemo {name clone democlone(): CloneDemo {return new CloneDemo()} } 原型原型链 函数&#xff08;class&#xff09;都有显示原型 prototyp…

【算法篇】栈与队列类(笔记)

目录 一、用栈实现队列 二、用队列实现栈 三、有效的括号 四、删除字符串中的所有相邻重复项 五、逆波兰表达式求值 六、滑动窗口最大值 七、前 K 个高频元素 一、用栈实现队列 232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/proble…

vue3常用的组件间通信

一 props props 可以实现父子组件通信&#xff0c;props数据是只读。 1. 基本用法 在父组件中&#xff0c;你可以这样传递 props&#xff1a; <template><ChildComponent message"Hello, Vue 3!" /> </template><script setup> import C…

C++STL之stack和queue容器适配器: 相关习题解析

1. 最小栈 . - 力扣&#xff08;LeetCode&#xff09; 思路: 一、整体架构 使用两个栈 st 和 min_st 分别实现不同的功能。 st 用于存放插入的数据&#xff0c;即主要的数据存储栈&#xff0c;模拟常规的数据存储结构。min_st 用于存放最小的元素&#xff0c;通过特定的插入…