QCustomplot 布局简介(五)

2024-05-10 06:38
文章标签 布局 简介 qcustomplot

本文主要是介绍QCustomplot 布局简介(五),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、历史对比

  关于QCPLayoutElement这个元素的讲解之前,我想先对1.3.2release版本和2.0.0beta版本的该元素做以简单的对比介绍,首先,1.3.2release版本时,鼠标单击时,如果按下的位置是一个布局元素,那么QCustomPlot首先会把这个事件回调给该被点击的元素,并且mouse系列的方法都是这样传递给QCPLayoutElement对象,该布局元素的声明会像这样QPointer<QCPLayoutElement> mMouseEventElement;但是到了2.0.0beta版本时,QCustomPlot源码做出了很大的调整,不仅仅是QCPLayoutElement布局元素可以接收鼠标事件,凡事继承自QCPLayerable类的元素都可以支持鼠标事件,因为mouse一系列的方法被移到了QCPLayerable类中。下面我分别贴出这两个版本时的mousePressEvent处理方法

    1.3.2release版本鼠标按下处理方式

 1 void QCustomPlot::mousePressEvent(QMouseEvent *event)2 {3     emit mousePress(event);4     mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)5 6     // call event of affected layout element:7     mMouseEventElement = layoutElementAt(event->pos());//后去当前选中布局元素 并调用其相关接口8     if (mMouseEventElement)9         mMouseEventElement->mousePressEvent(event);
10 
11     QWidget::mousePressEvent(event);
12 }

    2.0.0beta版本鼠标按下处理方式

 1 void QCustomPlot::mousePressEvent(QMouseEvent *event)2 {3     emit mousePress(event);4     // save some state to tell in releaseEvent whether it was a click:5     mMouseHasMoved = false;6     mMousePressPos = event->pos();7 8     if (mSelectionRect && mSelectionRectMode != QCP::srmNone)//优先处理鼠标绘制矩形事件9     {
10         if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect
11             mSelectionRect->startSelection(event);
12     }
13     else
14     {
15         // no selection rect interaction, so forward event to layerable under the cursor:
16         QList<QVariant> details;
17         QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);//根据鼠标位置获取当前点选的元素
18         for (int i = 0; i < candidates.size(); ++i)
19         {
20             event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
21             candidates.at(i)->mousePressEvent(event, details.at(i));
22             if (event->isAccepted())//如果有候选者处理了鼠标事件,那么事件循环结束
23             {
24                 mMouseEventLayerable = candidates.at(i);
25                 mMouseEventLayerableDetails = details.at(i);
26                 break;
27             }
28         }
29     }
30 
31     event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
32 }

    对比上述鼠标按下时两个版本的代码,beta版本(指2.0.0版本)比发布版本(1.3.2)明显多了一些代码,具体在两个方面体现:1、多了一个矩形选择区域;2、鼠标按下时获取当前候选元素,返回值类型为QCPLayerable。

    如图2是我设置了矩形选择区域画笔颜色为红色之后,进行的测试,可以画出一个红色的矩形框,测试代码:selectionRect()->setPen(QPen(Qt::red));

图1 绘制矩形

二、布局元素成员

    从第一小节看过来我们已经知道了mouse系列的方法已经从QCPLayoutElement移到了QCPLayerable类中,加之上述对鼠标按下事件的理解,这里我就不在说明鼠标事件的处理逻辑了,其实还是比较简单的,总之鼠标事件是从QCustomPlot类中触发,然后通过指针回调到各个QCPLayerable对象中,直到有一个对象处理了这个事件。

    如图2所示,是QCPLayoutElement类的继承关系图,从QCPLayoutElement派生出来的直接子类一共有5个,下面分别做以介绍

  • QCPAbstractLegendItem:图例项,包含于QCPLegend,具体可以参看QCustomplot使用分享(二) 源码解读图1
  • QCPAxisRect:坐标轴矩形,一般默认包含4个坐标轴,前边的文章也有简单的介绍到
  • QCPColorScale:颜色表,配合QCPColorMap使用
  • QCPLayout:布局抽象类,直接子类包括:QCPLayoutGrid和QCPLaoutInset
  • QCPTextElement:文本,可以支持鼠标事件

    以上类我是按照QCustomPlot提供的类图类进行分析的,但是本节我将不对坐标轴、文本元素、图例项和颜色表进行分析,仅仅只对QCPLayout类加以说明

图2 QCPLayoutElement类图

三、QCPLayout

    从图2可以看出QCPLayout有2个直接子类和1个间接子类,QCPLegend就是图例类了,他可以包含多个QCPAbstractLegend项,构成一个真正的的图例。接下来我们将进入到其直接子类的介绍,也是我们本片文章的重点需要讲解到的。

1、QCPLayoutGrid

    不管是beta版本还是发布版本,对于这个类的事件回调一直没有变化,都是类似如下代码

1 mPlotLayout->update(QCPLayoutElement::upPreparation);//margin计算和布局之前的准备
2 mPlotLayout->update(QCPLayoutElement::upMargins);//计算margin
3 mPlotLayout->update(QCPLayoutElement::upLayout);//更新布局

    这3次调用update方法,只是参数不同而已,看起来有些可怕,其实不然,好了,现在就跟我们一步一步分析这个调用过程吧

a、回调到抽象类QCPLayout的update方法中,因为phase第一次传递的是QCPLayoutElement::upPreparation,因此第3行的代码(原因看b小节)和第6行的if都不能执行

 1 void QCPLayout::update(UpdatePhase phase)2 {3     QCPLayoutElement::update(phase);4 5     // set child element rects according to layout:6     if (phase == upLayout)7         updateLayout();8 9     // propagate update call to child elements:
10     const int elCount = elementCount();//返回当前布局元素下总共有多少个子类 一般情况下至少有一个QCPAxisRect
11     for (int i = 0; i < elCount; ++i)
12     {
13         if (QCPLayoutElement *el = elementAt(i))
14             el->update(phase);
15     }
16 }

b、当update方法中传递QCPLayoutElement::upMargins参数时,第3行的QCPLayoutElement::update方法就可以被执行了,先看看如下代码,是不是有点儿明白了,在QCustomPlot类中虽然3次调用了update方法,但其实他们真正的计算是被分开了的,QCPLayoutElement::update处理margin计算,QCPLayout::update中的if负责布局更新,并把事件传递给所有的子类布局元素

 1 void QCPLayoutElement::update(UpdatePhase phase)2 {3     if (phase == upMargins)//只有margin计算才能进来4     {5         if (mAutoMargins != QCP::msNone)6         {7             // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:8             QMargins newMargins = mMargins;9             QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
10             foreach(QCP::MarginSide side, allMarginSides)
11             {
12                 if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
13                 {
14                     if (mMarginGroups.contains(side))
15                         QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
16                     else
17                         QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
18                     // apply minimum margin restrictions:
19                     if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
20                         QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
21                 }
22             }
23             setMargins(newMargins);
24         }
25     }
26 }

c、QCPLayoutGrid是一个递归包含的类,正是因为这样我们才可以实现在一个QCustomPlot窗口包含多个图表,不仅仅是多个图表,而且图表可能包含在不同的坐标轴上,如图3所示,在一个QCustomPlot窗口中默认有一个QCPLayoutGrid,而QCPLayoutGrid可以添加了多个QCPLayoutElement元素。仔细揣摩一下图2的类图,你就会发现这样的设计真是不简单,可以实现一个比较复杂的搭配,本节就只简单的描述下其类关系,如果需要实现复杂的示例,还是同学自己去努力啦。不过在本系列博客完结之后我将会把自己二次开发的一个demo分享给大家,现在还请大家耐心等待。

图3 多坐标轴示例

2、QCPLayoutInset

    相对于QCPLayoutGrid来说,该类是一个一维的数组,也是一个递归的包含,源码中用该类是在QCPAxisRect中用到了,负责保存outrect的大小,仔细看QCPAxisRect的源码就会发现该类有两个get接口是像如下代码这样写的。

1 QRect rect() const { return mRect; }
2 QRect outerRect() const { return mOuterRect; }

    关于这个类的更新信息我就不多说了,看的时候没有过多的关注,呵呵。。。有这部分需求的小伙伴就可能需要自己去看源码了,个人觉得东西应该也不多,主要是我们当时的业务没有涉及到这块,所以不需要进行修改

四、布局示例

    效果如图4所示

图4 多坐标轴矩形

    图4中有两个坐标轴矩形,上边的是默认存在的,底下的是新增坐标轴矩形,新增代码如下(下述代码的参数决定是新增还是隐藏已有)

 1 QCPBars * TimeSharingTrendPlot::ShowVOL(bool visible)2 {3     Q_D(TimeSharingTrendPlot);4     if (visible)5     {6         if (d->m_eQuota.testFlag(QCP::QT_VOL) == false)7         {8             d->m_eQuota |= QCP::QT_VOL;9 
10             QCPAxisRect *VolAxisRect = new QCPAxisRect(d_ptr->m_pPlot);
11             connect(d_ptr->m_pPlot->xAxis, static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::rangeChanged)
12                 , VolAxisRect->axis(QCPAxis::atBottom), static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::setRange));
13             connect(VolAxisRect->axis(QCPAxis::atBottom), static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::rangeChanged)
14                 , d_ptr->m_pPlot->xAxis, static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::setRange));
15             VolAxisRect->setMaximumSize(QSize(QWIDGETSIZE_MAX, 100));
16             VolAxisRect->axis(QCPAxis::atBottom)->setLayer("axes");
17             VolAxisRect->axis(QCPAxis::atBottom)->grid()->setLayer("grid");
18             VolAxisRect->setAutoMargins(QCP::msLeft | QCP::msRight | QCP::msBottom);
19             VolAxisRect->setMargins(QMargins(0, 0, 0, 0));
20             d_ptr->m_pPlot->plotLayout()->addElement(1, 0, VolAxisRect);
21 
22             VolAxisRect->setMarginGroup(QCP::msLeft | QCP::msRight, d->m_pMarginGroup);
23 
24             EnableFixedTicker(QCPAxis::atLeft | QCPAxis::atRight);
25 
26             //拉取分时图下VOL指标数据
27             Q_D(TimeSharingTrendPlot);
28             QCPBars * bars = AddBars(VolAxisRect->axis(QCPAxis::atBottom), VolAxisRect->axis(QCPAxis::atLeft));
29 
30             return bars;
31         }
32         else
33         {
34         }
35     }
36     else
37     {
38         d->m_eQuota &= ~QCP::QT_VOL;
39         d->m_pPlot->plotLayout()->remove(d_ptr->m_pPlot->plotLayout()->element(1, 0));
40     }
41 
42     d->m_pPlot->plotLayout()->simplify();
43 
44     return nullptr;
45 }

这篇关于QCustomplot 布局简介(五)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/975711

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

业务协同平台--简介

一、使用场景         1.多个系统统一在业务协同平台定义协同策略,由业务协同平台代替人工完成一系列的单据录入         2.同时业务协同平台将执行任务推送给pda、pad等执行终端,通知各人员、设备进行作业执行         3.作业过程中,可设置完成时间预警、作业节点通知,时刻了解作业进程         4.做完再给你做过程分析,给出优化建议         就问你这一套下

容器编排平台Kubernetes简介

目录 什么是K8s 为什么需要K8s 什么是容器(Contianer) K8s能做什么? K8s的架构原理  控制平面(Control plane)         kube-apiserver         etcd         kube-scheduler         kube-controller-manager         cloud-controlle

【Tools】AutoML简介

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 AutoML(自动机器学习)是一种使用机器学习技术来自动化机器学习任务的方法。在大模型中的AutoML是指在大型数据集上使用自动化机器学习技术进行模型训练和优化。

lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示

在使用 LVGL 8.3.6 创建一个垂直布局,其中 label 控件位于 image 控件下方,你可以使用 lv_obj_set_flex_flow 来设置布局为垂直,并确保 label 控件在 image 控件后添加。这里是如何步骤性地实现它的一个基本示例: 创建父容器:首先创建一个容器对象,该对象将作为布局的基础。设置容器为垂直布局:使用 lv_obj_set_flex_flow 设置容器

SaaS、PaaS、IaaS简介

云计算、云服务、云平台……现在“云”已成了一个家喻户晓的概念,但PaaS, IaaS 和SaaS的区别估计还没有那么多的人分得清,下面就分别向大家普及一下它们的基本概念: SaaS 软件即服务 SaaS是Software-as-a-Service的简称,意思是软件即服务。随着互联网技术的发展和应用软件的成熟, 在21世纪开始兴起的一种完全创新的软件应用模式。 它是一种通过Internet提供

Apache Tiles 布局管理器

陈科肇 =========== 1.简介 一个免费的开源模板框架现代Java应用程序。  基于该复合图案它是建立以简化的用户界面的开发。 对于复杂的网站,它仍然最简单,最优雅的方式来一起工作的任何MVC技术。 Tiles允许作者定义页面片段可被组装成在运行一个完整的网页。  这些片段,或Tiles,可以用于为了降低公共页面元素的重复,简单地包括或嵌入在其它瓦片,制定了一系列可重复使用

【CSS in Depth 2 精译_023】第四章概述 + 4.1 Flexbox 布局的基本原理

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结) 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位(已完结) 2.1 相对单位的威力2.2 em 与 rem2.3 告别像素思维2.4 视口的相对单位2.5 无单位的数值与行高2.6 自定义属性2.7 本章小结 第三章 文档流与盒模型(已

LIBSVM简介

LIBSVM简介 支持向量机所涉及到的数学知识对一般的化学研究者来说是比较难的,自己编程实现该算法难度就更大了。但是现在的网络资源非常发达,而且国际上的科学研究者把他们的研究成果已经放在网络上,免费提供给用于研究目的,这样方便大多数的研究者,不必要花费大量的时间理解SVM算法的深奥数学原理和计算机程序设计。目前有关SVM计算的相关软件有很多,如LIBSVM、mySVM、SVMLight等,这些