IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 分析一个跨平台QT项目(获取系统CPU使用率和内存占用率) -> 正文阅读

[C++知识库]分析一个跨平台QT项目(获取系统CPU使用率和内存占用率)

如果是仅仅做界面的话,Qt框架本身就是跨平台的,不需要额外的操作。但是如果涉及到一些和平台自身特性相关的操作的时候,这部分代码就无法实现跨平台了。针对平台特性相关的代码,我们采用宏定义和qmake,在不同的平台下执行不同的代码,从而实现跨平台。这里介绍一下,如何通过宏定义和qmake,实现项目的跨平台。

下面以一个获取系统CPU使用率和内存使用率应用为例,说明一下设计思路。例子中主要采用了两种设计模式:策略模式和单例模式。

通过采用策略模式,我们抽离出应用和系统相关的接口,然后通过子类化,添加系统接口在不同平台下的实现。类图如下所示:

SysInfo中抽离出了系统访问的接口,供Qt框架调用,在Windows系统中执行SysInfoWindowsImpl中的函数,在Linux系统中执行SysInfoLinuxImpl中的函数,在Mac系统中执行SysInfoMacImpl中的函数。我们将三个子类分别放置于三个不同的文件中,确保在不同的系统中只会编译对应平台的内容。

系统信息访问类的公共接口如下:

//sysinfo.h
#ifndef SYSINFO_H
#define SYSINFO_H

class SysInfo
{
public:
    //构造和系统函数
    static SysInfo& instance();
    virtual ~SysInfo();

    //初始化函数
    virtual void init() = 0;

    //cpu平均使用率
    virtual double cpuLoadAverage() = 0;

    //内存使用率
    virtual double memoryUsed() = 0;

protected:
    explicit SysInfo();
private:
    SysInfo(const SysInfo& rhs);
    SysInfo& operator=(const SysInfo& rhs);
};
#endif // SYSINFO_H

在各个平台下接口的实现子类如下:

windows平台接口实现

//syteminfowindowsimpl.h
//针对windows平台的接口
#ifndef SYTEMINFOWINDOWSIMPL_H
#define SYTEMINFOWINDOWSIMPL_H
#include <QtGlobal>
#include <QVector>
#include "SysInfo.h"
typedef struct _FILETIME FILETIME;
class SysInfoWindowsImpl : public SysInfo
{
public:
    SysInfoWindowsImpl();
    void init() override;
    //windows下的实现
    double cpuLoadAverage() override;

    //windows下的实现
    double memoryUsed() override;
private:
    //获取并记录CPU的原始数据
    QVector<qulonglong> cpuRawData();
    //转换时间格式
    qulonglong convertFileTime(const FILETIME& filetime) const;
private:
    QVector<qulonglong> mCpuLoadLastValues;
};

#endif // SYTEMINFOWINDOWSIMPL_H

//syteminfowindowsimpl.cpp
#include "syteminfowindowsimpl.h"
#include <windows.h>

SysInfoWindowsImpl::SysInfoWindowsImpl() : SysInfo(),mCpuLoadLastValues()
{
}
double SysInfoWindowsImpl::memoryUsed()
{
    //获取内存的使用率
    MEMORYSTATUSEX memoryStatus;
    memoryStatus.dwLength = sizeof(MEMORYSTATUSEX);
    GlobalMemoryStatusEx(&memoryStatus);
    qulonglong memoryPhysicalUsed = memoryStatus.ullTotalPhys - memoryStatus.ullAvailPhys;
    return (double)memoryPhysicalUsed / (double)memoryStatus.ullTotalPhys * 100.0;
}
void SysInfoWindowsImpl::init()
{
    mCpuLoadLastValues = cpuRawData();
}
QVector<qulonglong> SysInfoWindowsImpl::cpuRawData()
{
    //获取CPU的占用率
    //闲置时间
    FILETIME idleTime;
    //内核使用时间
    FILETIME kernelTime;
    //用户使用时间
    FILETIME userTime;
    GetSystemTimes(&idleTime, &kernelTime, &userTime);
    QVector<qulonglong> rawData;
    rawData.append(convertFileTime(idleTime));
    rawData.append(convertFileTime(kernelTime));
    rawData.append(convertFileTime(userTime));
    return rawData;
}
qulonglong SysInfoWindowsImpl::convertFileTime(const FILETIME& filetime) const
{
    ULARGE_INTEGER largeInteger;
    largeInteger.LowPart = filetime.dwLowDateTime;
    largeInteger.HighPart = filetime.dwHighDateTime;
    return largeInteger.QuadPart;
}

double SysInfoWindowsImpl::cpuLoadAverage()
{
    QVector<qulonglong> firstSample = mCpuLoadLastValues;
    QVector<qulonglong> secondSample = cpuRawData();
    mCpuLoadLastValues = secondSample;
    
    //获取两个时间点之间的CPU时间
    qulonglong currentIdle = secondSample[0] - firstSample[0];
    qulonglong currentKernel = secondSample[1] - firstSample[1];
    qulonglong currentUser = secondSample[2] - firstSample[2];
    qulonglong currentSystem = currentKernel + currentUser;
    
    //(总的时间 - 空闲时间)/ 总的时间 = 占用cpu的时间,也就是占用率
    double percent = (currentSystem - currentIdle) * 100.0 /
    currentSystem ;
    return qBound(0.0, percent, 100.0);
}

Linux平台接口实现

//sysinfolinuximpl.h
#ifndef SYSINFOLINUXIMPL_H
#define SYSINFOLINUXIMPL_H
#include <QtGlobal>
#include <QVector>
#include "sysinfo.h"

class SysInfoLinuxImpl : public SysInfo
{
public:
    SysInfoLinuxImpl();
    void init() override;
    double cpuLoadAverage() override;
    double memoryUsed() override;
private:
    QVector<qulonglong> cpuRawData();
private:
    QVector<qulonglong> mCpuLoadLastValues;
};

#endif // SYSINFOLINUXIMPL_H

//sysinfolinuximpl.cpp
#include "sysinfolinuximpl.h"
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <QFile>

SysInfoLinuxImpl::SysInfoLinuxImpl() :
    SysInfo(),
    mCpuLoadLastValues()
{
}

void SysInfoLinuxImpl::init()
{
    mCpuLoadLastValues = cpuRawData();
}

double SysInfoLinuxImpl::memoryUsed()
{
    struct sysinfo memInfo;
    sysinfo(&memInfo);
    qulonglong totalMemory = memInfo.totalram;
    totalMemory += memInfo.totalswap;
    totalMemory *= memInfo.mem_unit;
    qulonglong totalMemoryUsed = memInfo.totalram - memInfo.freeram;
    totalMemoryUsed += memInfo.totalswap - memInfo.freeswap;
    totalMemoryUsed *= memInfo.mem_unit;
    double percent = (double)totalMemoryUsed /
            (double)totalMemory * 100.0;
    return qBound(0.0, percent, 100.0);
}

QVector<qulonglong> SysInfoLinuxImpl::cpuRawData()
{
    QFile file("/proc/stat");
    file.open(QIODevice::ReadOnly);
    QByteArray line = file.readLine();
    file.close();
    qulonglong totalUser = 0, totalUserNice = 0,
    totalSystem = 0, totalIdle = 0;
    std::sscanf(line.data(), "cpu %llu %llu %llu %llu",
    &totalUser, &totalUserNice, &totalSystem,
    &totalIdle);
    QVector<qulonglong> rawData;
    rawData.append(totalUser);
    rawData.append(totalUserNice);
    rawData.append(totalSystem);
    rawData.append(totalIdle);
    return rawData;
}

double SysInfoLinuxImpl::cpuLoadAverage()
{
    QVector<qulonglong> firstSample = mCpuLoadLastValues;
    QVector<qulonglong> secondSample = cpuRawData();
    mCpuLoadLastValues = secondSample;
    double overall = (secondSample[0] - firstSample[0])
    + (secondSample[1] - firstSample[1])
    + (secondSample[2] - firstSample[2]);
    
    //(用户时间 + 系统时间)/ 总的时间 = 占用cpu的时间,也就是占用率
    double total = overall + (secondSample[3] - firstSample[3]);
    double percent = (overall / total) * 100.0;
    return qBound(0.0, percent, 100.0);
}

Mac平台接口实现

//sysinfomacimpl.h
#ifndef SYSINFOMACIMPL_H
#define SYSINFOMACIMPL_H
#include "sysinfo.h"
#include <QtGlobal>
#include <QVector>

class SysInfoMacImpl : public SysInfo
{
public:
    SysInfoMacImpl();

    void init() override;
    double cpuLoadAverage() override;
    double memoryUsed() override;

private:
    QVector<qulonglong> cpuRawData();
private:
    QVector<qulonglong> mCpuLoadLastValues;
};

#endif // SYSINFOMACIMPL_H
//sysinfomacimpl.cpp
#include "sysinfomacimpl.h"
#include <mach/vm_statistics.h>
#include <mach/mach_types.h>
#include <mach/mach_init.h>
#include <mach/mach_host.h>
#include <mach/vm_map.h>

SysInfoMacImpl::SysInfoMacImpl() :
SysInfo()
{
}
double SysInfoMacImpl::memoryUsed()
{
    vm_size_t pageSize;
    vm_statistics64_data_t vmStats;
    mach_port_t machPort = mach_host_self();
    mach_msg_type_number_t count = sizeof(vmStats) / sizeof(natural_t);
    host_page_size(machPort, &pageSize);
    host_statistics64(machPort, HOST_VM_INFO, (host_info64_t)&vmStats, &count);
    qulonglong freeMemory = (int64_t)vmStats.free_count * (int64_t)pageSize;
    qulonglong totalMemoryUsed = ((int64_t)vmStats.active_count + (int64_t)vmStats.inactive_count +
    (int64_t)vmStats.wire_count)* (int64_t)pageSize;
    qulonglong totalMemory = freeMemory + totalMemoryUsed;
    double percent = (double)totalMemoryUsed / (double)totalMemory * 100.0;
    return qBound(0.0, percent, 100.0);
}

void SysInfoMacImpl::init()
{
    mCpuLoadLastValues = cpuRawData();
}
QVector<qulonglong> SysInfoMacImpl::cpuRawData()
{
    host_cpu_load_info_data_t cpuInfo;
    mach_msg_type_number_t cpuCount = HOST_CPU_LOAD_INFO_COUNT;
    QVector<qulonglong> rawData;
    qulonglong totalUser = 0, totalUserNice = 0, totalSystem = 0, totalIdle = 0;
    host_statistics(mach_host_self(),HOST_CPU_LOAD_INFO,(host_info_t)&cpuInfo, &cpuCount);
    for(unsigned int i = 0; i < cpuCount; i++)
    {
        unsigned int maxTicks = CPU_STATE_MAX * i;
        totalUser += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_USER];
        totalUserNice += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_SYSTEM];
        totalSystem += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_NICE];
        totalIdle += cpuInfo.cpu_ticks[maxTicks + CPU_STATE_IDLE];
    }
    rawData.append(totalUser);
    rawData.append(totalUserNice);
    rawData.append(totalSystem);
    rawData.append(totalIdle);
    return rawData;
}

通过在pro文件中添加平台对应的宏操作,确保在执行qmake的时候只有对应平台的接口实现参与编译,pro文件中的宏添加方法如下:

windows {
    SOURCES += syteminfowindowsimpl.cpp
    HEADERS += syteminfowindowsimpl.h
}
linux {
    SOURCES += sysinfolinuximpl.cpp
    HEADERS += sysinfolinuximpl.h
}

macx {
    SOURCES += sysinfomacimpl.cpp
    HEADERS += sysinfomacimpl.h
}

在文件编译层我们可以通过qmake的宏实现在不同的平台上编译不同的文件,在代码层我们可以通过Qt系统的宏来保障不同的平台下执行不同的代码。

下面我们引入平台宏和单例模式确保在不同的平台下调用不同的类,实现逻辑如下:

#include "sysinfo.h"
#include <QtGlobal>

#ifdef Q_OS_WIN
#include "syteminfowindowsimpl.h"
#elif defined(Q_OS_MAC)
#include "sysinfomacimpl.h"
#elif defined(Q_OS_LINUX)
#include "sysinfolinuximpl.h"
#endif

SysInfo& SysInfo::instance()
{
#ifdef Q_OS_WIN
    static SysInfoWindowsImpl singleton;
#elif defined(Q_OS_MAC)
    static SysInfoMacImpl singleton;
#elif defined(Q_OS_LINUX)
    static SysInfoLinuxImpl singleton;
#endif
    return singleton;
}
SysInfo::SysInfo(){}
SysInfo::~SysInfo(){}

通过引入qmake宏和Qt框架的宏,我们就可以获取CPU使用率和内存占用率的跨平台接口了。接下来通过Qt的QChartView控件动态显示CPU的占用率和内存的使用率。

首先抽离出控件的公共类如下:

//sysinfowidget.h
#ifndef SYSINFOWIDGET_H
#define SYSINFOWIDGET_H

#include <QWidget>
#include <QTimer>
#include <QtCharts/QChartView>

class SysInfoWidget : public QWidget
{
    Q_OBJECT
public:
    //@1父控件 @2控件延时 @3控件的刷新事件
    explicit SysInfoWidget(QWidget *parent = 0,
                           int startDelayMs = 500,
                           int updateSeriesDelayMs = 500);
protected:
    //获取公共的QtCharView控件
    QtCharts::QChartView& chartView();
protected slots:
    //刷新控件
    virtual void updateSeries() = 0;
private:
    QTimer mRefreshTimer;
    QtCharts::QChartView mChartView;
};

#endif // SYSINFOWIDGET_H
//sysinfowidget.cpp
#include "sysinfowidget.h"
#include <QVBoxLayout>
using namespace QtCharts;
SysInfoWidget::SysInfoWidget(QWidget *parent,
                             int startDelayMs,
                             int updateSeriesDelayMs) :
    QWidget(parent),
    mChartView(this)
{
    //初始化定时器和控件基本样式
    mRefreshTimer.setInterval(updateSeriesDelayMs);
    connect(&mRefreshTimer, &QTimer::timeout,
            this, &SysInfoWidget::updateSeries);
    QTimer::singleShot(startDelayMs, [this] { mRefreshTimer.start(); });

    mChartView.setRenderHint(QPainter::Antialiasing);
    mChartView.chart()->legend()->setVisible(false);

    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(&mChartView);
    setLayout(layout);
}
QChartView& SysInfoWidget::chartView()
{
    return mChartView;
}

CPU使用率控件实现

//cpuwidget.h
#ifndef CPUWIDGET_H
#define CPUWIDGET_H
#include <QtCharts/QPieSeries>
#include "sysinfowidget.h"
class CpuWidget : public SysInfoWidget
{
    Q_OBJECT
public:
    explicit CpuWidget(QWidget* parent = 0);
protected slots:
    void updateSeries() override;
private:
    QtCharts::QPieSeries* mSeries;
};

#endif // CPUWIDGET_H
cpuwidget.cpp
#include "cpuwidget.h"
#include "sysinfo.h"
using namespace QtCharts;
CpuWidget::CpuWidget(QWidget* parent) :
    SysInfoWidget(parent),
    mSeries(new QPieSeries(this))
{
    //以饼状图显示CPU的使用率
    mSeries->setHoleSize(0.35);
    mSeries->append("CPU Load", 30.0);
    mSeries->append("CPU Free", 70.0);
    
    QChart* chart = chartView().chart();
    chart->addSeries(mSeries);
    chart->setTitle("CPU average load");
}
void CpuWidget::updateSeries()
{
    //动态刷新CPU的使用率
    double cpuLoadAverage = SysInfo::instance().cpuLoadAverage();
    mSeries->clear();
    mSeries->append("Load", cpuLoadAverage);
    mSeries->append("Free", 100.0 - cpuLoadAverage);
}

内存占用率控件实现

//memorywidget.h
#ifndef MEMORYWIDGET_H
#define MEMORYWIDGET_H
#include <QtCharts/QLineSeries>
#include "sysinfowidget.h"
class MemoryWidget : public SysInfoWidget
{
    Q_OBJECT
public:
    explicit MemoryWidget(QWidget *parent = 0);

protected slots:
    void updateSeries() override;
    //以曲线图动态显示内存的占用率
private:
    QtCharts::QLineSeries* mSeries;
    qint64 mPointPositionX;
};
#endif // MEMORYWIDGET_H
//memorywidget.cpp
#include "memorywidget.h"
#include <QtCharts/QAreaSeries>
#include <QPen>
#include <QLinearGradient>
#include "sysinfo.h"

using namespace QtCharts;

const int CHART_X_RANGE_COUNT = 50;
const int CHART_X_RANGE_MAX = CHART_X_RANGE_COUNT - 1;

//渐变色的起始色和终止色
const int COLOR_DARK_BLUE = 0x209fdf;
const int COLOR_LIGHT_BLUE = 0xbfdfef;
const int PEN_WIDTH = 3;

MemoryWidget::MemoryWidget(QWidget *parent) :
    SysInfoWidget(parent),
    mSeries(new QLineSeries(this)),
    mPointPositionX(0)
{
    QPen pen(COLOR_DARK_BLUE);
    pen.setWidth(PEN_WIDTH);

    QLinearGradient gradient(QPointF(0, 0), QPointF(0, 1));
    gradient.setColorAt(1.0, COLOR_DARK_BLUE);
    gradient.setColorAt(0.0, COLOR_LIGHT_BLUE);
    gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
   
     //动态刷线曲线图
    QAreaSeries* areaSeries = new QAreaSeries(mSeries);
    areaSeries->setPen(pen);
    areaSeries->setBrush(gradient);

    QChart* chart = chartView().chart();
    chart->addSeries(areaSeries);
    chart->setTitle("Memory used");
    chart->createDefaultAxes();
    chart->axisX()->setRange(0, CHART_X_RANGE_MAX);
    chart->axisX()->setVisible(false);
    chart->axisY()->setRange(0, 100);
}

void MemoryWidget::updateSeries()
{
    //刷线内存使用率
    double memoryUsed = SysInfo::instance().memoryUsed();
    mSeries->append(mPointPositionX++, memoryUsed);
    //超出显示范围之后往前滚动一个格子
    if (mSeries->count() > CHART_X_RANGE_COUNT) {
        QChart* chart = chartView().chart();
        chart->scroll(chart->plotArea().width() / CHART_X_RANGE_MAX, 0);
        mSeries->remove(0);
    }
}

设计完成两个控件之后,我们在主窗口中添加两个对应的控件进行显示。

//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "cpuwidget.h"
#include "memorywidget.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private:
    Ui::MainWindow *ui;
    CpuWidget mCpuWidget;
    MemoryWidget mMemoryWidget;
};

#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_MainWindow.h"
#include <QDebug>
#include "sysinfo.h"
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    mCpuWidget(this),
    mMemoryWidget(this)
{
    ui->setupUi(this);
    SysInfo::instance().init();
    ui->centralWidget->layout()->addWidget(&mCpuWidget);
    ui->centralWidget->layout()->addWidget(&mMemoryWidget);
}
MainWindow::~MainWindow()
{
    delete ui;
}

?显示效果

到此为止一个跨平台显示系统CPU使用率和内存占用率的程序就完成了,项目运行的时候效果如下:

总结一下吧

1.Qt项目中文件层的跨平台可以通过qmake的宏在pro工程文件中实现

2.代码层次的跨平台可以通过Qt框架中的系统来实现

3.通过策略模式+单例模式,我们可以实现对应接口在不同平台下执行不同的代码

4.动态显示曲线图饼状图一类的视图的时候可以采用Qt框架中的QChartView模块

参考资料: 《End-to-End-GUI-development-with-Qt5》提供的Demo

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:22:53  更:2022-04-18 17:24:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:16:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码