我想用C ++开发自己的信号范围。所以我用qt就是为了这个目的。我在ui中添加了一个graphicsView和一个按钮。在连接方法按下按钮我更新了graphicsView(最后我将这个方法传递给一个线程)。每次按下按钮,尽管删除指针,内存的使用量也会增加。我该怎么控制呢?
我检查了vs15诊断工具中的内存使用情况。
c ++头文件:
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication.h"
#include <QGraphicsScene>
#include <QGraphicsPathItem>
class QtGuiApplication : public QMainWindow
{
Q_OBJECT
public:
QtGuiApplication(QWidget *parent = Q_NULLPTR);
private:
Ui::QtGuiApplicationClass ui;
QGraphicsScene* scene = new QGraphicsScene();
QPolygon pol;
QGraphicsPathItem* pathItem = new QGraphicsPathItem();
int index_ = 0; // graph offset
QPen* myPen = new QPen(Qt::red, 2);
private slots:
void btn_Display_clicked();
};
c ++源文件:
#include "QtGuiApplication.h"
#include <math.h> /* sin */
#define pi 3.14159265
QtGuiApplication::QtGuiApplication(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.graphicsView->setScene(scene);
ui.graphicsView->show();
connect(ui.btn_Display, SIGNAL(clicked()), this, SLOT(btn_Display_clicked()));
}
void QtGuiApplication::btn_Display_clicked()
{
scene->removeItem(pathItem);
QPoint pos;
double x_amp_, y_amp_;
int x_pix_, y_pix_;
double phi_ = 0;
double freq_ = 5;
for (int i = 0; i<800; i++)
{
y_amp_ = 100 * sin(2 * pi*freq_*i / 811 + phi_) + 20 * index_;
x_amp_ = i;
x_pix_ = x_amp_;
y_pix_ = y_amp_;
pos.setX(x_pix_);
pos.setY(y_pix_);
pol.append(pos);
}
QPainterPath* myPath = new QPainterPath();
(*myPath).addPolygon(pol);
pathItem = scene->addPath(*myPath, *myPen);
delete myPath;
pol.clear();
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
我有几条评论。
new
)。按值保存对象:它们被设计为以这种方式工作,并让编译器担心其余部分。btn_Display_clicked
的范围;你在循环之外声明循环变量(x_amp_
和y_amp_
)。connect
声明是多余的:setupUi
为您做连接。<QtModule/QClass>
形式推迟检测项目错误配置,直到链接时间并且是不必要的。你的意思是直接包括<QClass>
。如果出现编译错误,那么您的项目配置错误 - 也许您需要重新运行qmake(典型解决方案)。<cmath>
是惯用语,而不是<math.h>
。保留后者以与C兼容。M_PI
。它是C ++中pi广泛使用的名称。QPolygonF
。因此,界面变为:
#pragma once
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsPathItem>
#include "ui_QtGuiApplication.h"
class GuiApplication : public QMainWindow
{
Q_OBJECT
public:
GuiApplication(QWidget *parent = {});
private:
Ui::GuiApplicationClass ui;
QGraphicsScene scene;
QGraphicsPathItem pathItem; // the order matters: must be declared after the scene
int index_ = 0;
Q_SLOT void btn_Display_clicked();
};
并实施:
#include "GuiApplication.h"
#define _USE_MATH_DEFINES
#include <cmath>
#if !defined(M_PI)
#define M_PI (3.14159265358979323846)
#endif
GuiApplication::GuiApplication(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
ui.graphicsView->setScene(&scene);
ui.graphicsView->show();
pathItem.setPen({Qt::red, 2});
scene.addItem(&pathItem);
}
void GuiApplication::btn_Display_clicked()
{
const double phi_ = 0;
const double freq_ = 5;
const int N = 800;
QPolygonF pol{N};
for (int i = 0; i < pol.size(); ++i) {
qreal x = i;
qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
pol[i] = {x, y};
}
QPainterPath path;
path.addPolygon(pol);
pathItem.setPath(path);
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
在C ++ 14中,如果您希望在其他地方重用它,您可以轻松地将生成器分解出来;那么循环将被替换为:
auto generator = [=,i=-1]() mutable {
++i;
return QPointF{qreal(i), 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_};
};
std::generate(pol.begin(), pol.end(), generator);
唉,就目前而言,btn_Display_clicked
实现抛弃了分配用于存储多边形的堆,并且它不必要地将数据从多边形复制到路径内的内部元素存储。我们可以通过直接修改画家项目的路径来避免这两种情况:
void GuiApplication::btn_Display_clicked()
{
const double phi_ = 0;
const double freq_ = 5;
const int N = 800;
if (pathItem.path().isEmpty()) { // preallocate the path
QPainterPath path;
path.addPolygon(QPolygon{N});
pathItem.setPath(path);
}
auto path = pathItem.path();
pathItem.setPath({}); // we own the path now - item's path is detached
Q_ASSERT(path.elementCount() == N);
for (int i = 0; i < path.elementCount(); ++i) {
qreal x = i;
qreal y = 100 * sin(2 * M_PI * freq_ * i / 811 + phi_) + 20 * index_;
path.setElementPositionAt(i, x, y);
}
pathItem.setPath(path);
index_ = (index_ + 1) % 20; // just for sense of change in graph
}
因为QPainterPath
是隐式共享值类,所以path = item.path(); item.setPath({});
序列相当于将路径移出路径项。随后的setPath(path)
将其移回,因为我们不对本地副本进行进一步更改。
在
pathItem = scene->addPath(*myPath, *myPen);
创建了一个新的QGraphicsPathItem
并将指针返回到pathItem
。迟早会再次单击该按钮
scene->removeItem(pathItem);
从场景中删除QGraphicsPathItem
。不幸的是according to the documentation
项目的所有权传递给调用者(即,QGraphicsScene在销毁时将不再删除项目)。
删除pathItem
取决于程序员。 pathItem
没有删除,并在随后的电话中泄露
pathItem = scene->addPath(*myPath, *myPen);
解决方案:在pathItem
泄露之前删除它。
scene->removeItem(pathItem);
delete pathItem;
应该做的工作。