>>>

QCustomPlot绘制正玄和余玄图 |
|---|
>>>
#-------------------------------------------------
#
# Project created by QtCreator 2012-03-04T23:24:55
#
#-------------------------------------------------
# 添加QT模块
QT += core gui
# 如果QT主版本号大于4,则添加widgets和printsupport模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# 如果QT主版本号大于4,则添加c++11配置
greaterThan(QT_MAJOR_VERSION, 4): CONFIG += c++11
# 如果QT主版本号小于5,则添加c++11编译选项
lessThan(QT_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -std=c++11
# 设置目标名称
TARGET = interaction-example
# 设置模板类型为应用程序
TEMPLATE = app
# 添加源文件
SOURCES += main.cpp\
mainwindow.cpp \
qcustomplot.cpp
# 添加头文件
HEADERS += mainwindow.h \
qcustomplot.h
# 添加表单文件
FORMS += mainwindow.ui
>>>
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QInputDialog>
#include "qcustomplot.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
// 构造函数
explicit MainWindow(QWidget *parent = 0);
// 析构函数
~MainWindow();
private slots:
// 双击标题栏
void titleDoubleClick(QMouseEvent *event);
// 双击坐标轴标签
void axisLabelDoubleClick(QCPAxis* axis, QCPAxis::SelectablePart part);
// 双击图例
void legendDoubleClick(QCPLegend* legend, QCPAbstractLegendItem* item);
// 选择改变
void selectionChanged();
// 鼠标按下
void mousePress();
// 鼠标滚轮
void mouseWheel();
// 添加随机图形
void addRandomGraph();
// 移除选中图形
void removeSelectedGraph();
// 移除所有图形
void removeAllGraphs();
// 上下文菜单请求
void contextMenuRequest(QPoint pos);
// 移动图例
void moveLegend();
// 图形点击
void graphClicked(QCPAbstractPlottable *plottable, int dataIndex);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H>>>
#include "mainwindow.h"
#include "ui_mainwindow.h"
// MainWindow类的构造函数,初始化窗口和UI
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// 设置随机数种子 获取当前时间,并将其转换为自1970年1月1日以来的毫秒数,将毫秒数除以1000,得到秒数
std::srand(QDateTime::currentDateTime().toMSecsSinceEpoch()/1000.0);
// 设置UI
ui->setupUi(this);
// 设置交互方式
// 设置自定义绘图控件的交互方式,包括范围拖动、范围缩放、选择坐标轴、选择图例和选择绘图元素
ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes |
QCP::iSelectLegend | QCP::iSelectPlottables);
// 设置坐标轴范围
ui->customPlot->xAxis->setRange(-8, 8);
ui->customPlot->yAxis->setRange(-5, 5);
// 设置坐标轴 axisRect获取绘图区域 setupFullAxesBox自动为绘图区域添加顶部、底部、左侧和右侧的轴??
ui->customPlot->axisRect()->setupFullAxesBox();
// 设置标题
ui->customPlot->plotLayout()->insertRow(0);
QCPTextElement *title = new QCPTextElement(ui->customPlot, "Interaction Example", QFont("sans", 17, QFont::Bold));
ui->customPlot->plotLayout()->addElement(0, 0, title);
// 设置坐标轴标签
ui->customPlot->xAxis->setLabel("x Axis");
ui->customPlot->yAxis->setLabel("y Axis");
// 设置图例
ui->customPlot->legend->setVisible(true);
QFont legendFont = font();
legendFont.setPointSize(10);
ui->customPlot->legend->setFont(legendFont);
ui->customPlot->legend->setSelectedFont(legendFont);
// 设置图例的可选择部分为图例项
ui->customPlot->legend->setSelectableParts(QCPLegend::spItems); // legend box shall not be selectable, only legend items
// 添加随机图形
addRandomGraph();
addRandomGraph();
addRandomGraph();
addRandomGraph();
// 重绘图形
// 调整坐标轴范围,使其适应数据
ui->customPlot->rescaleAxes();
// 连接槽函数,将一些轴的选择绑定在一起(特别是相反的轴):
connect(ui->customPlot, SIGNAL(selectionChangedByUser()), this, SLOT(selectionChanged()));
// 连接槽函数,处理当轴被选择时,只有该方向可以被拖动和缩放:
connect(ui->customPlot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePress()));
connect(ui->customPlot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheel()));
// 使底部和左侧轴的范围同步到顶部和右侧轴:
connect(ui->customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->xAxis2, SLOT(setRange(QCPRange)));
connect(ui->customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->yAxis2, SLOT(setRange(QCPRange)));
// 连接一些交互槽函数:
connect(ui->customPlot, SIGNAL(axisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)), this, SLOT(axisLabelDoubleClick(QCPAxis*,QCPAxis::SelectablePart)));
connect(ui->customPlot, SIGNAL(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*)), this, SLOT(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*)));
connect(title, SIGNAL(doubleClicked(QMouseEvent*)), this, SLOT(titleDoubleClick(QMouseEvent*)));
// 连接槽函数,当图形被点击时,在状态栏显示消息:
connect(ui->customPlot, SIGNAL(plottableClick(QCPAbstractPlottable*,int,QMouseEvent*)), this, SLOT(graphClicked(QCPAbstractPlottable*,int)));
// 设置策略并连接上下文菜单弹出槽函数:
ui->customPlot->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->customPlot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));
}
// MainWindow类的析构函数,删除UI
MainWindow::~MainWindow()
{
delete ui;
}
// 标题双击槽函数,双击标题时设置新的标题
void MainWindow::titleDoubleClick(QMouseEvent* event)
{
Q_UNUSED(event) // 忽略event参数
if (QCPTextElement *title = qobject_cast<QCPTextElement*>(sender())) // 如果sender()是一个QCPTextElement对象
{
// Set the plot title by double clicking on it
bool ok;
QString newTitle = QInputDialog::getText(this, "QCustomPlot example", "New plot title:", QLineEdit::Normal, title->text(), &ok); // 弹出一个对话框,让用户输入新的标题
if (ok)
{
title->setText(newTitle); // 设置新的标题
ui->customPlot->replot(); // 重新绘制图表
}
}
}
// 坐标轴标签双击槽函数,双击坐标轴标签时设置新的标签
void MainWindow::axisLabelDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part)
{
// 设置轴标签通过双击轴标签
if (part == QCPAxis::spAxisLabel) // 仅当实际轴标签被点击时做出反应,而不是刻度标签或轴主干
{
bool ok;
// 弹出一个对话框,让用户输入新的轴标签
QString newLabel = QInputDialog::getText(this, "QCustomPlot example", "New axis label:", QLineEdit::Normal, axis->label(), &ok);
if (ok)
{
// 设置新的轴标签
axis->setLabel(newLabel);
// 重新绘制图表
ui->customPlot->replot();
}
}
}
// 图例双击槽函数,双击图例项时重命名图形
void MainWindow::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item)
{
// Rename a graph by double clicking on its legend item
Q_UNUSED(legend)
if (item) // 仅在点击了项目时做出反应(用户可能点击了图例边框的填充区域,此时item为0)
{
QCPPlottableLegendItem *plItem = qobject_cast<QCPPlottableLegendItem*>(item);
bool ok;
// 弹出一个对话框,让用户输入新的图名
QString newName = QInputDialog::getText(this, "QCustomPlot example", "New graph name:", QLineEdit::Normal, plItem->plottable()->name(), &ok);
if (ok)
{
// 将新的图名赋值给plottable
plItem->plottable()->setName(newName);
// 重新绘制图表
ui->customPlot->replot();
}
}
}
// 选择改变槽函数,同步轴的选择状态
void MainWindow::selectionChanged()
{
/*
通常情况下,轴基线、轴刻度标签和轴标签是可以分别选择的,但我们希望用户只能整体选择轴,
因此我们将刻度标签和轴基线的选择状态绑定在一起。然而,轴标签可以单独选择。
左轴和右轴的选择状态需要同步,底轴和顶轴的选择状态也需要同步。
此外,我们希望将图形的选择状态与相应的图例项的选择状态同步。这样,用户可以通过点击图形本身或其图例项来选择图形。
*/
// 使顶部和底部轴同步选择,并将轴和刻度标签作为一个可选择的对象:
if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
ui->customPlot->xAxis2->selectedParts().testFlag(QCPAxis::spAxis) || ui->customPlot->xAxis2->selectedParts().testFlag(QCPAxis::spTickLabels))
{
// 如果顶部或底部轴被选中,则将顶部和底部轴都设置为选中状态
ui->customPlot->xAxis2->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
ui->customPlot->xAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
}
// 使左侧和右侧轴同步选择,并将轴和刻度标签作为一个可选择的对象:
if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis) || ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spTickLabels) ||
ui->customPlot->yAxis2->selectedParts().testFlag(QCPAxis::spAxis) || ui->customPlot->yAxis2->selectedParts().testFlag(QCPAxis::spTickLabels))
{
// 如果左侧或右侧轴被选中,则将左侧和右侧轴都设置为选中状态
ui->customPlot->yAxis2->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
ui->customPlot->yAxis->setSelectedParts(QCPAxis::spAxis|QCPAxis::spTickLabels);
}
// 同步图形的选择状态与相应的图例项的选择状态:
for (int i=0; i<ui->customPlot->graphCount(); ++i)
{
// 遍历所有图形
QCPGraph *graph = ui->customPlot->graph(i);
// 获取当前图形的图例项
QCPPlottableLegendItem *item = ui->customPlot->legend->itemWithPlottable(graph);
// 如果图形或图例项被选中,则将图例项和图形都设置为选中状态
if (item->selected() || graph->selected())
{
item->setSelected(true);
graph->setSelection(QCPDataSelection(graph->data()->dataRange()));
}
}
}
// 鼠标按下槽函数,如果选择了轴,则只允许拖动该轴的方向
void MainWindow::mousePress()
{
// if an axis is selected, only allow the direction of that axis to be dragged
// if no axis is selected, both directions may be dragged
// 如果x轴被选中
if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
// 设置x轴可拖动
ui->customPlot->axisRect()->setRangeDrag(ui->customPlot->xAxis->orientation());
// 如果y轴被选中
else if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
// 设置y轴可拖动
ui->customPlot->axisRect()->setRangeDrag(ui->customPlot->yAxis->orientation());
// 否则
else
// 设置x轴和y轴都可拖动
ui->customPlot->axisRect()->setRangeDrag(Qt::Horizontal|Qt::Vertical);
}
// 鼠标滚轮槽函数,如果选择了轴,则只允许缩放该轴的方向
void MainWindow::mouseWheel()
{
// if an axis is selected, only allow the direction of that axis to be zoomed
// if no axis is selected, both directions may be zoomed
// 如果x轴被选中
if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
// 设置x轴的缩放范围
ui->customPlot->axisRect()->setRangeZoom(ui->customPlot->xAxis->orientation());
// 如果y轴被选中
else if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
// 设置y轴的缩放范围
ui->customPlot->axisRect()->setRangeZoom(ui->customPlot->yAxis->orientation());
// 否则
else
// 设置水平和垂直方向的缩放范围
ui->customPlot->axisRect()->setRangeZoom(Qt::Horizontal|Qt::Vertical);
}
// 添加随机图形槽函数,添加随机图形
void MainWindow::addRandomGraph()
{
int n = 50; // number of points in graph
// RAND_MAX 0x7fff
// std::rand()用于生成一个介于0到RAND_MAX之间的随机整数。
// (std::rand()/(double)RAND_MAX)将生成的随机整数转换为0到1之间的随机浮点数。
// 对于xScale和yScale,通过加上0.5并乘以2,转换得到的随机浮点数的范围变为1到2之间,作为x轴和y轴的缩放因子。
double xScale = (std::rand()/(double)RAND_MAX + 0.5)*2; // x轴缩放因子
double yScale = (std::rand()/(double)RAND_MAX + 0.5)*2; // y轴缩放因子
// 通过减去0.5并乘以4或10,转换得到的随机浮点数的范围变为-2到2或-5到5之间,作为x轴和y轴的偏移量。
double xOffset = (std::rand()/(double)RAND_MAX - 0.5)*4; // x轴偏移量
double yOffset = (std::rand()/(double)RAND_MAX - 0.5)*10; // y轴偏移量
double r1 = (std::rand()/(double)RAND_MAX - 0.5)*2; // 参数r1
double r2 = (std::rand()/(double)RAND_MAX - 0.5)*2; // 参数r2
double r3 = (std::rand()/(double)RAND_MAX - 0.5)*2; // 参数r3
double r4 = (std::rand()/(double)RAND_MAX - 0.5)*2; // 参数r4
QVector<double> x(n), y(n);
for (int i=0; i<n; i++)
{
/*
(i/(double)n - 0.5) * 10.0 确保了 x 坐标的值在 -5 和 5 之间均匀分布。
xScale 用于缩放这些值,使其范围在 [-5.0*xScale, 5.0*xScale) 之间。
xOffset 用于对这些缩放后的值进行水平偏移,使其最终范围在 [-5.0*xScale + xOffset, 5.0*xScale + xOffset) 之间。
*/
x[i] = (i/(double)n-0.5)*10.0*xScale + xOffset; // 计算x坐标
/*
(x[i]*r1*5):这部分计算是将x[i]乘以随机生成的r1因子,并乘以5。r1是一个范围在-1到1之间的随机数。
qSin(x[i]*r1*5):对上述结果取正弦值,得到一个新的值。
(x[i]*r2*3):将x[i]乘以随机生成的r2因子,并乘以3。r2是一个范围在-1到1之间的随机数。
qCos(x[i]*r2):对上述结果取余弦值。
(x[i]*r2*3)*r4*3:进一步将qCos(x[i]*r2)的结果乘以r2、3、r4和3。r4也是一个范围在-1到1之间的随机数。
qSin(qCos(x[i]*r2)*r4*3):对上述结果取正弦值。
qSin(x[i]*r1*5) * qSin(qCos(x[i]*r2)*r4*3):将上述两个正弦值相乘。
qSin(x[i]*r1*5) * qSin(qCos(x[i]*r2)*r4*3) + r3*qCos(qSin(x[i])*r4*2):再加上r3*qCos(qSin(x[i])*r4*2),其中r3是一个范围在-1到1之间的随机数。
* yScale:将上述结果乘以yScale,使得y值在不同的范围内。
+ yOffset:最后加上yOffset,其中yOffset是一个范围在-5到5之间的随机数。
*/
y[i] = (qSin(x[i]*r1*5)*qSin(qCos(x[i]*r2)*r4*3)+r3*qCos(qSin(x[i])*r4*2))*yScale + yOffset; // 计算y坐标
}
ui->customPlot->addGraph(); // 添加新的图形
ui->customPlot->graph()->setName(QString("New graph %1").arg(ui->customPlot->graphCount()-1)); // 设置图形名称
ui->customPlot->graph()->setData(x, y); // 设置图形数据
ui->customPlot->graph()->setLineStyle((QCPGraph::LineStyle)(std::rand()%5+1)); // 设置图形线型
if (std::rand()%100 > 50)
// std::rand()%14+1 生成一个范围在 1 到 14 之间的随机整数。这个整数用来选择 QCPScatterStyle::ScatterShape 枚举类型中的一个值。
ui->customPlot->graph()->setScatterStyle(QCPScatterStyle((QCPScatterStyle::ScatterShape)(std::rand()%14+1))); // 设置图形散点样式
QPen graphPen;
graphPen.setColor(QColor(std::rand()%245+10, std::rand()%245+10, std::rand()%245+10)); // 设置图形颜色
graphPen.setWidthF(std::rand()/(double)RAND_MAX*2+1); // 设置图形线宽
ui->customPlot->graph()->setPen(graphPen); // 设置图形画笔
ui->customPlot->replot(); // 重绘图形
}
// 移除选中的图形
void MainWindow::removeSelectedGraph()
{
// 如果有选中的图形
if (ui->customPlot->selectedGraphs().size() > 0)
{
// 移除选中的图形
ui->customPlot->removeGraph(ui->customPlot->selectedGraphs().first());
// 重新绘制图形
ui->customPlot->replot();
}
}
// 移除所有图形
void MainWindow::removeAllGraphs()
{
// 清除所有图形
ui->customPlot->clearGraphs();
// 重新绘制图形
ui->customPlot->replot();
}
// 上下文菜单请求
void MainWindow::contextMenuRequest(QPoint pos)
{
// 创建上下文菜单
QMenu *menu = new QMenu(this);
// 设置关闭时删除
menu->setAttribute(Qt::WA_DeleteOnClose);
// 如果在图例上请求上下文菜单
if (ui->customPlot->legend->selectTest(pos, false) >= 0)
{
// 添加移动图例的选项
menu->addAction("Move to top left", this, SLOT(moveLegend()))->setData((int)(Qt::AlignTop|Qt::AlignLeft));
menu->addAction("Move to top center", this, SLOT(moveLegend()))->setData((int)(Qt::AlignTop|Qt::AlignHCenter));
menu->addAction("Move to top right", this, SLOT(moveLegend()))->setData((int)(Qt::AlignTop|Qt::AlignRight));
menu->addAction("Move to bottom right", this, SLOT(moveLegend()))->setData((int)(Qt::AlignBottom|Qt::AlignRight));
menu->addAction("Move to bottom left", this, SLOT(moveLegend()))->setData((int)(Qt::AlignBottom|Qt::AlignLeft));
}
else // 在图形上请求上下文菜单
{
// 添加添加随机图形的选项
menu->addAction("Add random graph", this, SLOT(addRandomGraph()));
// 如果有选中的图形,添加移除选中的图形的选项
if (ui->customPlot->selectedGraphs().size() > 0)
menu->addAction("Remove selected graph", this, SLOT(removeSelectedGraph()));
// 如果有图形,添加移除所有图形的选项
if (ui->customPlot->graphCount() > 0)
menu->addAction("Remove all graphs", this, SLOT(removeAllGraphs()));
}
// 在指定位置弹出上下文菜单
menu->popup(ui->customPlot->mapToGlobal(pos));
}
// 移动图例
void MainWindow::moveLegend()
{
// 确保这个槽函数是由上下文菜单动作调用的,并携带我们需要的数据
if (QAction* contextAction = qobject_cast<QAction*>(sender()))
{
bool ok;
// 获取数据
int dataInt = contextAction->data().toInt(&ok);
if (ok)
{
// 设置图例位置
ui->customPlot->axisRect()->insetLayout()->setInsetAlignment(0, (Qt::Alignment)dataInt);
// 重新绘制图形
ui->customPlot->replot();
}
}
}
// 图形点击事件
void MainWindow::graphClicked(QCPAbstractPlottable *plottable, int dataIndex)
{
// 由于我们知道图中只有QCPGraphs,我们可以立即访问interface1D()
// 通常最好先检查interface1D()是否返回非零,然后再使用它。
double dataValue = plottable->interface1D()->dataMainValue(dataIndex);
// 构造消息
QString message = QString("Clicked on graph '%1' at data point #%2 with value %3.").arg(plottable->name()).arg(dataIndex).arg(dataValue);
// 显示消息
ui->statusBar->showMessage(message, 2500);
}
>>>

>>>


>>>
QCPAbstractPlottable 是 QCustomPlot 库中的一个抽象基类,用于表示图表中的可绘制对象。QCustomPlot 是一个功能强大的 C++ 库,用于在 Qt 应用程序中创建交互式图表。
QCPAbstractPlottable 提供了一些基本的接口和功能,这些功能可以被具体的绘图对象(如曲线、散点图、条形图等)继承和扩展。以下是一些关键点:
QCPAbstractPlottable 提供了 draw 方法,用于在图表上绘制对象。这个方法通常由 QCustomPlot 的内部机制调用。QCPAbstractPlottable 负责管理绘图所需的数据。例如,曲线图需要管理一系列的点,而散点图需要管理一系列的坐标。QCPAbstractPlottable 提供了一些接口,用于处理用户交互,如鼠标点击、悬停等。QCPAbstractPlottable 提供了一些接口,用于设置和获取绘图对象的样式,如颜色、线型、标记等。QCPAbstractPlottable 可以处理一些事件,如鼠标事件、键盘事件等。QCPGraph 是 QCustomPlot 库中的一个类,用于表示图表中的曲线图。它继承自 QCPAbstractPlottable,并提供了绘制和操作曲线所需的功能。
以下是一些 QCPGraph 的关键点:
QCPGraph 可以管理一系列的点,这些点定义了曲线的形状。数据可以通过 addData 方法添加,也可以通过 setData 方法一次性设置。QCPGraph 提供了一些接口,用于设置和获取曲线的样式,如颜色、线型、标记等。QCPGraph 可以处理鼠标事件,如点击、悬停等,并可以显示数据点的信息。QCPDataSelection 是 QCustomPlot 库中的一个类,用于表示图表中数据点的选择。它通常用于处理用户交互,如选择数据点、拖动选择区域等。
以下是一些 QCPDataSelection 的关键点:
QCPDataSelection 可以表示一个或多个数据点的选择。每个数据点由其在数据集中的索引表示。QCPDataSelection 可以表示一个选择范围,由起始和结束索引定义。QCPDataSelection 可以表示不同的选择类型,如单个数据点、连续数据点、非连续数据点等。QCPDataSelection 提供了一些方法,用于添加、删除、清除选择,以及检查是否包含特定的数据点。❤❤❤
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。