Qt学习之旅 I

news2024/9/25 3:18:24

构建一个跨平台的应用(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 class
    All Bases For Every Possible Operating
    System
*/
class System_InfoImpl {
public:
    System_InfoImpl() = default;
    Q_DISABLE_COPY(System_InfoImpl);
    virtual ~System_InfoImpl() = default;
    // Impl Interfaces
    virtual 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;
​
    // Impls
    void   _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_CALC
    qulonglong 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();
    // Functions
    double 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;
    // Impls
    void   _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.coloradmin.cn/o/2138272.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTB-MarkUp(XXE漏洞、SSH id_rsa密钥)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解MarkUp靶机 渗透过程 信息搜集 服务器开放了22SSH端口、80HTTP端口 和 443HTTPS端口 弱口令登录后台 抓取http 数据包&#xff0c;进行加载 账号密码字典 账号: admin密码: password 利用XXE漏洞 捕…

超分辨率技术之插值算法

&#x1f31e;欢迎莅临我的个人主页&#x1f448;&#x1f3fb;这里是我专注于深度学习领域、用心分享知识精粹与智慧火花的独特角落&#xff01;&#x1f349; &#x1f308;如果大家喜欢文章&#xff0c;欢迎&#xff1a;关注&#x1f377;点赞&#x1f44d;&#x1f3fb;评论…

天融信把桌面explorer.exe删了,导致开机之后无windows桌面,只能看到鼠标解决方法

win10开机进入桌面&#xff0c;发现桌面无了&#xff0c;但是可以ctrlaltdelete调出任务管理器 用管理员权限打开cmd&#xff0c;输入&#xff1a; sfc /scanfilec:\windowslexplorer.exe 在运行C:\windows\Explorer.exe&#xff1b;可以进入桌面&#xff0c;但是隔离几秒钟…

链式二叉树的基本操作(C语言版)

目录 1.二叉树的定义 2.创建二叉树 3.递归遍历二叉树 1&#xff09;前序遍历 2&#xff09;中序遍历 3&#xff09;后序遍历 4.层序遍历 5.计算节点个数 6.计算叶子节点个数 7.计算第K层节点个数 8.计算树的最大深度 9.查找值为x的节点 10.二叉树的销毁 从二叉树…

物体识别之微特征识别任务综述

“深度人工智能”是成都深度智谷科技旗下的人工智能教育机构订阅号&#xff0c;主要分享人工智能的基础知识、技术发展、学习经验等。此外&#xff0c;订阅号还为大家提供了人工智能的培训学习服务和人工智能证书的报考服务&#xff0c;欢迎大家前来咨询&#xff0c;实现自己的…

一模--解题--71-80

文章目录 9.资源管理71、 [单选] 一个项目连续错过交付日期&#xff0c;项目团队评估完该情况后&#xff0c;项目经理意识到团队绩效差的原因在于团队成员对于自己的职责缺乏清晰认识。项目经理首先应该关注哪一项&#xff1f; 13.干系人管理72、 [单选] 项目团队刚刚完成一个新…

element-plus的面包屑组件el-breadcrumb

面包屑组件主要用来显示当页面路径&#xff0c;以及快速返回之前的页面。 涉及2个组件 el-breadcrumb 和el-breadcrumb-item, el-breadcrumb的spearator指定item的分隔符 el-breadcrumb-item的to和replace属性和vue-router的一致&#xff0c;需要结合vue_router一起使用 用法…

WSL安装Redis

前言 本来一直是在虚拟机的Ubuntu开发 但是 搞着搞着内存不足 导致我某些数据损坏了 然后目前迁移到Wsl开发 运行WSL的相较于虚拟机你不需要很多的性能开销&#xff01; 我只是代码开发和git交互&#xff0c;如果是搞逆向还是虚拟机。 记录一下redis 安装卸载 免得以后又忘了…

【中等】机试-滑动窗口(双指针)-例:无重复字符的最长子串

※高频、重点 字节&#xff08;飞书&#xff09;、百度等大厂测开高频面试题&#xff1a;最长不重复子串 . - 力扣&#xff08;LeetCode&#xff09;字节飞书面经里的高频题&#xff0c;没做出来&#xff0c;需要好好复习。 重点考察-滑动窗口这个概念&#xff0c;自学记录一…

攻击者如何在日常网络资源中隐藏恶意软件

近二十年来&#xff0c;安全 Web 网关 (SWG) 一直在监控网络流量&#xff0c;以检测恶意软件、阻止恶意网站并保护企业免受基于 Web 的威胁。 然而&#xff0c;攻击者已经找到了许多绕过这些防御措施的方法&#xff0c;SquareX的安全研究人员对此进行了记录。 最危险的策略之…

【Linux】调试和Git及进度条实现

这里是阿川的博客&#xff0c;祝您变得更强 ✨ 个人主页&#xff1a;在线OJ的阿川 &#x1f496;文章专栏&#xff1a;Linux入门到进阶 &#x1f30f;代码仓库&#xff1a; 写在开头 现在您看到的是我的结论或想法&#xff0c;但在这背后凝结了大量的思考、经验和讨论 目录 1.…

LinkedHashMap 如何实现排序

目录 一、LinkedHashMap二、排序实现三、代码片段分析 一、LinkedHashMap LinkedHashMap 是 Java 中的一个集合类&#xff0c;它是 HashMap 的一个子类&#xff0c;继承了 HashMap 的所有特性&#xff0c;并且在此基础上增加了一个双向链表来维护元素的插入顺序或者访问顺序。L…

java的内存分配和回收机制

Java 与 C之间有一堵由内存动态分配和垃圾收集技术所围成的高墙&#xff0c;墙外面的人想进去&#xff0c;墙里面的人却想出来。 概述 垃圾收集&#xff08;GC&#xff09;需要完成的三件事情&#xff1a; 哪些内存需要回收&#xff1f;什么时候回收&#xff1f;如何回收&am…

CloudXR 套件扩展 XR 工作流

NVIDIA为开发者提供了一个先进的平台&#xff0c;开发者可以在该平台上使用全新NVIDIA CloudXR 套件来创建可扩展、品牌化的定制扩展现实&#xff08;XR&#xff09;产品。 NVIDIA CloudXR 套件基于全新架构而打造&#xff0c;是扩展XR生态的重要工具。它为开发者、专业人士和…

高级 API 性能:着色器

着色器通过使您能够控制渲染过程的各个方面&#xff0c;在图形编程中发挥着关键作用。它们在 GPU 上运行&#xff0c;负责操作顶点、像素和其他数据。 常规着色器计算着色器像素渲染顶点着色器几何体、域和外壳着色器 常规着色器 这些提示适用于所有类型的着色器。 推荐 避…

[产品管理-10]:NPDP新产品开发 - 8 - 波士顿矩阵(当下与未来)在产品市场战略方面的应用

目录 一、波士顿矩阵 理论基础 产品类型划分 分析步骤 重要性 注意事项 二、波士顿矩阵的应用实例 示例背景 数据收集与准备 绘制波士顿矩阵 产品线分类 制定战略对策 一、波士顿矩阵&#xff1a;现在 VS 未来 波士顿矩阵理论&#xff0c;又称市场增长率-相对市场份…

读构建可扩展分布式系统:方法与实践04应用服务

1. 应用服务 1.1. 任何系统的核心都在于实现应用需求的特定业务逻辑 1.2. 服务是可扩展软件系统的核心 1.2.1. 它们将契约定义为一个API&#xff0c;向客户端声明它们的能力 1.3. 应用服务器高度依赖于编程语言&#xff0c;但通常都会提供多线程编程模型&#xff0c;允许服…

Ubuntu系统使用Docker部署Jupyter Notebook并实现笔记云同步

文章目录 前言1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 前言 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpol…

Netty(零散记录)

Netty&#xff1a; 1、Netty三种IO 2、Netty和Reactor的 1、Netty对Reactor的支持 Netty的线程模型时基于Reactor模型实现的&#xff0c;Netty对Reactor三种模式都有非常好的支持&#xff0c;并做了一定的改善&#xff0c;一般情况下&#xff0c;在服务端会采用主从架构模型…

Leetcode面试经典150题-739.每日温度

应读者私信要求&#xff0c;本题协商题目的具体内容 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0…