使用两种设计模式:
策略模式:这是一个描述功能(例如,检索 CPU 使用率)的接口,特定行为(在 Windows/Mac OS/Linux 上检索 CPU 使用率)将执行到实现此接口的子类中。
单例模式:此模式保证给定类的单个实例。 使用唯一的访问点可以轻松访问此实例。
SysInfo.h
#ifndef SYSINFO_H
#define SYSINFO_H
//单例
class SysInfo
{
public:
//返回对象的实例
static SysInfo& instance();
//虚析构
//我此析构函数必须是虚拟的,以确保从基类指针删除派生类的实例时
//将调用派生类析构函数,而不仅仅是基类析构函数
virtual ~SysInfo();
//虚函数接口
//init():该函数允许派生类根据操作系统平台执行任何初始化过程
virtual void init() = 0;
//该函数调用一些特定于操作系统的代码来检索平均 CPU 负载并将其作为百分比值返回
virtual double cpuLoadAverage() = 0;
//该函数调用一些特定于操作系统的代码来检索使用的内存并将其作为百分比值返回
virtual double memoryUsed() = 0;
//virtual 关键字表示该函数可以在派生类中被覆盖。
//= 0 语法意味着这个函数是纯虚拟的,并且必须在任何具体的派生类中被覆盖
//此外,这使得 SysInfo 成为无法实例化的抽象类
protected:
explicit SysInfo();
private:
//禁止拷贝
SysInfo(const SysInfo& rhs);
SysInfo& operator=(const SysInfo& rhs);
};
#endif // SYSINFO_H
SysInfo.cpp
#include "SysInfo.h"
#include <QtGlobal>
#ifdef Q_OS_WIN
#include "SysInfoWindowsImpl.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()
{
}
SysInfoLinuxImpl.h
#ifndef SYSINFOLINUXIMPL_H
#define SYSINFOLINUXIMPL_H
#include <QtGlobal>
#include <QVector>
#include "SysInfo.h"
//重载上面接口函数
//Linux 实现
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::cpuLoadAverage()
{
//CPU 负载功能比内存功能稍微复杂一些
//我们将从 Linux 中检索 CPU 执行不同类型工作所花费的总时间
QVector<qulonglong> firstSample = mCpuLoadLastValues;
QVector<qulonglong> secondSample = cpuRawData();
mCpuLoadLastValues = secondSample;
//我们必须返回瞬时 CPU 负载。 一种常见的获取方法是在短时间内检索两个样本值,并利用差来获取瞬时CPU负载
double overall = (secondSample[0] - firstSample[0])
+ (secondSample[1] - firstSample[1])
+ (secondSample[2] - firstSample[2]);
double total = overall + (secondSample[3] - firstSample[3]);
double percent = (overall / total) * 100.0;
return qBound(0.0, percent, 100.0);
}
double SysInfoLinuxImpl::memoryUsed()
{
//此函数使用 Linux 特定的 API
struct sysinfo memInfo;
//添加所需的包含后,您可以使用 Linux sysinfo() 函数返回有关整体系统统计信息的信息
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);
}
//执行 Linux API 调用以检索系统计时信息并返回 qulonglong 类型的 QVector 类中的值
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;
}
//下面界面的实现
两个窗口将从 SysInfoWidget 类继承并执行它们的特定任务。
SysInfoWidget.h
#ifndef SYSINFOWIDGET_H
#define SYSINFOWIDGET_H
#include <QWidget>
#include <QTimer>
#include <QtCharts/QChartView>
//该类将处理布局并显示QChartView
class SysInfoWidget : public QWidget
{
Q_OBJECT
public:
//参数startDelayMs和updateSeriesDelayMs具有默认值,调用者可以根据需要对其进行自定义。
explicit SysInfoWidget(QWidget *parent = 0,
int startDelayMs = 500,
int updateSeriesDelayMs = 500);
protected:
//QChartView是可以显示多种类型图表
QtCharts::QChartView& chartView();
protected slots:
//这是一个纯虚槽函数
//槽updateSeries()将被子类覆盖以检索系统值并定义应如何绘制图表
virtual void updateSeries() = 0;
private:
//QTimer会定期调用槽函数updateSeries()
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
mRefreshTimer.setInterval(updateSeriesDelayMs);
//定义定时器间隔和每当触发超时信号时要调用的槽
connect(&mRefreshTimer, &QTimer::timeout,
this, &SysInfoWidget::updateSeries);
//静态函数QTimer::singleShot()将在startDelayMs定义的延迟后启动实时计时器
QTimer::singleShot(startDelayMs, [this] { mRefreshTimer.start(); });
//启用抗锯齿以平滑图表绘制
mChartView.setRenderHint(QPainter::Antialiasing);
mChartView.chart()->legend()->setVisible(false);
//处理布局以便SysInfoWidget类中显示QChartView
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(&mChartView);
setLayout(layout);
}
QChartView& SysInfoWidget::chartView()
{
return mChartView;
}
CpuWidget.h
#ifndef CPUWIDGET_H
#define CPUWIDGET_H
//我们包含QtCharts模块中的QPieSeries
//以便我们可以创建名为mSeries的成员QPieSeries*
//QPieSeries是QAbstractSeries的子类
#include <QtCharts/QPieSeries>
#include "SysInfoWidget.h"
//由于重写SysInfoWidget::updateSeries()槽,我们必须包含Q_OBJECT宏以允许CPUWidget响应 SysInfoWidgetmRefreshTimer::timeout()信号
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))
{
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");
}
//重写SysInfoWidget类中的updateSeries()函数并调用QtCharts API:
void CpuWidget::updateSeries()
{
//我们获取SysInfo的实例对象
//然后我们在cpuLoadAverage中计算当前平均CPU负载
double cpuLoadAverage = SysInfo::instance().cpuLoadAverage();
//我们只需要当前CPU平均负载
//mSeries->clear()清除mSeries数据
mSeries->clear();
//我们必须将这些数据提供给我们的mSeries
mSeries->append("Load", cpuLoadAverage);
mSeries->append("Free", 100.0 - cpuLoadAverage);
}
在CpuWidget类中,我们不必关心update,所有的工作都是在 SysInfoWidget 子类中完成的.我们只关心应该显示什么数据以及使用什么样的图形来显示它。
MemoryWidget.h
这个界面将显示数据的历史记录,以便我们可以看到内存消耗如何随着时间的推移而演变。为了显示这些数据,我们将使用 Qt Chart 模块中的 QLineSeries 类
#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;
//mPointPositionX是一个unsigned long long(使用Qt符号qint64变量
//确保mPointPositionX永远不会溢出
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()函数根据areaSeries类型创建X轴和Y轴。
//如果我们使用3D Series,createDefaultAxes()函数会添加Z轴。
chart->createDefaultAxes();
chart->axisX()->setRange(0, CHART_X_RANGE_MAX);
//使用chart->axisX()->setVisible(false)隐藏X轴刻度值
chart->axisX()->setVisible(false);
chart->axisY()->setRange(0, 100);
}
void MemoryWidget::updateSeries()
{
//我们首先获取内存百分比
double memoryUsed = SysInfo::instance().memoryUsed();
mSeries->append(mPointPositionX++, memoryUsed);
//我们添加超过 CHART_X_RANGE_COUNT 个点时会发生什么
//图表上的可见“窗口”是静态的,点将添加到外部。这意味着我们只会看到之前CHART_X_RANGE_MAX 点的内存使用情况
if (mSeries->count() > CHART_X_RANGE_COUNT) {
QChart* chart = chartView().chart();
//QChart 提供了在视图内滚动以移动可见窗口的功能
chart->scroll(chart->plotArea().width() / CHART_X_RANGE_MAX, 0);
//然后我们使用mSeries->remove(0)删除索引0处的点,以确保小部件不会存储无限数据集
mSeries->remove(0);
}
}