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版本鼠标按下处理方式
void QCustomPlot::mousePressEvent(QMouseEvent *event)
{
emit mousePress(event);
mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release) // call event of affected layout element:
mMouseEventElement = layoutElementAt(event->pos());//后去当前选中布局元素 并调用其相关接口
if (mMouseEventElement)
mMouseEventElement->mousePressEvent(event); QWidget::mousePressEvent(event);
}
2.0.0beta版本鼠标按下处理方式
void QCustomPlot::mousePressEvent(QMouseEvent *event)
{
emit mousePress(event);
// save some state to tell in releaseEvent whether it was a click:
mMouseHasMoved = false;
mMousePressPos = event->pos(); if (mSelectionRect && mSelectionRectMode != QCP::srmNone)//优先处理鼠标绘制矩形事件
{
if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect
mSelectionRect->startSelection(event);
}
else
{
// no selection rect interaction, so forward event to layerable under the cursor:
QList<QVariant> details;
QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);//根据鼠标位置获取当前点选的元素
for (int i = ; i < candidates.size(); ++i)
{
event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
candidates.at(i)->mousePressEvent(event, details.at(i));
if (event->isAccepted())//如果有候选者处理了鼠标事件,那么事件循环结束
{
mMouseEventLayerable = candidates.at(i);
mMouseEventLayerableDetails = details.at(i);
break;
}
}
} event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
}
对比上述鼠标按下时两个版本的代码,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版本还是发布版本,对于这个类的事件回调一直没有变化,都是类似如下代码
mPlotLayout->update(QCPLayoutElement::upPreparation);//margin计算和布局之前的准备
mPlotLayout->update(QCPLayoutElement::upMargins);//计算margin
mPlotLayout->update(QCPLayoutElement::upLayout);//更新布局
这3次调用update方法,只是参数不同而已,看起来有些可怕,其实不然,好了,现在就跟我们一步一步分析这个调用过程吧
a、回调到抽象类QCPLayout的update方法中,因为phase第一次传递的是QCPLayoutElement::upPreparation,因此第3行的代码(原因看b小节)和第6行的if都不能执行
void QCPLayout::update(UpdatePhase phase)
{
QCPLayoutElement::update(phase); // set child element rects according to layout:
if (phase == upLayout)
updateLayout(); // propagate update call to child elements:
const int elCount = elementCount();//返回当前布局元素下总共有多少个子类 一般情况下至少有一个QCPAxisRect
for (int i = ; i < elCount; ++i)
{
if (QCPLayoutElement *el = elementAt(i))
el->update(phase);
}
}
b、当update方法中传递QCPLayoutElement::upMargins参数时,第3行的QCPLayoutElement::update方法就可以被执行了,先看看如下代码,是不是有点儿明白了,在QCustomPlot类中虽然3次调用了update方法,但其实他们真正的计算是被分开了的,QCPLayoutElement::update处理margin计算,QCPLayout::update中的if负责布局更新,并把事件传递给所有的子类布局元素
void QCPLayoutElement::update(UpdatePhase phase)
{
if (phase == upMargins)//只有margin计算才能进来
{
if (mAutoMargins != QCP::msNone)
{
// set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
QMargins newMargins = mMargins;
QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
foreach(QCP::MarginSide side, allMarginSides)
{
if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
{
if (mMarginGroups.contains(side))
QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
else
QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
// apply minimum margin restrictions:
if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
}
}
setMargins(newMargins);
}
}
}
c、QCPLayoutGrid是一个递归包含的类,正是因为这样我们才可以实现在一个QCustomPlot窗口包含多个图表,不仅仅是多个图表,而且图表可能包含在不同的坐标轴上,如图3所示,在一个QCustomPlot窗口中默认有一个QCPLayoutGrid,而QCPLayoutGrid可以添加了多个QCPLayoutElement元素。仔细揣摩一下图2的类图,你就会发现这样的设计真是不简单,可以实现一个比较复杂的搭配,本节就只简单的描述下其类关系,如果需要实现复杂的示例,还是同学自己去努力啦。不过在本系列博客完结之后我将会把自己二次开发的一个demo分享给大家,现在还请大家耐心等待。
图3 多坐标轴示例
2、QCPLayoutInset
相对于QCPLayoutGrid来说,该类是一个一维的数组,也是一个递归的包含,源码中用该类是在QCPAxisRect中用到了,负责保存outrect的大小,仔细看QCPAxisRect的源码就会发现该类有两个get接口是像如下代码这样写的。
QRect rect() const { return mRect; }
QRect outerRect() const { return mOuterRect; }
关于这个类的更新信息我就不多说了,看的时候没有过多的关注,呵呵。。。有这部分需求的小伙伴就可能需要自己去看源码了,个人觉得东西应该也不多,主要是我们当时的业务没有涉及到这块,所以不需要进行修改
四、布局示例
效果如图4所示
图4 多坐标轴矩形
图4中有两个坐标轴矩形,上边的是默认存在的,底下的是新增坐标轴矩形,新增代码如下(下述代码的参数决定是新增还是隐藏已有)
QCPBars * TimeSharingTrendPlot::ShowVOL(bool visible)
{
Q_D(TimeSharingTrendPlot);
if (visible)
{
if (d->m_eQuota.testFlag(QCP::QT_VOL) == false)
{
d->m_eQuota |= QCP::QT_VOL; QCPAxisRect *VolAxisRect = new QCPAxisRect(d_ptr->m_pPlot);
connect(d_ptr->m_pPlot->xAxis, static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::rangeChanged)
, VolAxisRect->axis(QCPAxis::atBottom), static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::setRange));
connect(VolAxisRect->axis(QCPAxis::atBottom), static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::rangeChanged)
, d_ptr->m_pPlot->xAxis, static_cast<void (QCPAxis:: *)(const QCPRange &)>(&QCPAxis::setRange));
VolAxisRect->setMaximumSize(QSize(QWIDGETSIZE_MAX, ));
VolAxisRect->axis(QCPAxis::atBottom)->setLayer("axes");
VolAxisRect->axis(QCPAxis::atBottom)->grid()->setLayer("grid");
VolAxisRect->setAutoMargins(QCP::msLeft | QCP::msRight | QCP::msBottom);
VolAxisRect->setMargins(QMargins(, , , ));
d_ptr->m_pPlot->plotLayout()->addElement(, , VolAxisRect); VolAxisRect->setMarginGroup(QCP::msLeft | QCP::msRight, d->m_pMarginGroup); EnableFixedTicker(QCPAxis::atLeft | QCPAxis::atRight); //拉取分时图下VOL指标数据
Q_D(TimeSharingTrendPlot);
QCPBars * bars = AddBars(VolAxisRect->axis(QCPAxis::atBottom), VolAxisRect->axis(QCPAxis::atLeft)); return bars;
}
else
{
}
}
else
{
d->m_eQuota &= ~QCP::QT_VOL;
d->m_pPlot->plotLayout()->remove(d_ptr->m_pPlot->plotLayout()->element(, ));
} d->m_pPlot->plotLayout()->simplify(); return nullptr;
}
五、相关文章
QCustomplot使用分享(四) QCPAbstractItem
QCustomplot使用分享(五) 布局的更多相关文章
- QCustomplot使用分享(七) 层(完结)
一.分层绘制 一直说要讲2.0.0版本,但总是想把1.3.2版本拿出来比较一下,这篇文章也不例外.QCustomPlot2.0.0beta版本比1.3.2release版本有一个很大的改进那就是分层绘 ...
- QCustomplot使用分享(六) 坐标轴和网格线
一.概述 前边已经写了5篇对QCustomPlot的讲解,看过上述的几篇文章后,基本就能做一些简单的使用了,但是如果想要做到高度的控制图表,那么坐标轴将是很重要的一部分,因为坐标轴就是图表的一个参考系 ...
- QCustomplot使用分享(二) 源码解读
一.头文件概述 从这篇文章开始,我们将正式的进入到QCustomPlot的实践学习中来,首先我们先来学习下QCustomPlot的类图,如果下载了QCustomPlot源码的同学可以自己去QCusto ...
- QCustomplot使用分享(八) 绘制图表-加载cvs文件
目录 一.概述 二.效果图 三.源码讲解 1.源码结构 2.头文件 3.移动游标 4.设置坐标轴矩形个数 5.添加图表数据 6.设置折线图类型 6.其他函数 四.测试方式 1.测试工程 2.测试文件 ...
- QCustomplot使用分享(九) 绘制图表-多功能游标
目录 一.概述 二.效果图 三.源码讲解 1.源码结构 2.头文件 3.添加游标 4.监测移动 5.移动游标 6.其他函数 四.测试方式 1.测试工程 2.测试文件 3.测试代码 五.相关文章 六.总 ...
- QCustomplot使用分享(三) 图
一.可以实现的图 相对于其他绘制图表的第三方库来说,QCustomPlot算是比较轻量的,不仅仅能实现功能,而且二次开发比较容易.下面我们来具体说下他可以实现那些图 QCPGraph:折线图,Line ...
- QCustomplot使用分享(四) QCPAbstractItem
一.是什么 说起图,大家一下就可能想到折线图.柱状图和饼图等,但是除了这些显眼的东西以外其实还有很多东西辅助的存在着,有了这些辅助的东西图才会看起来有意义,或者说更加的真实.有说服力.这些东西都包括那 ...
- 每周分享五个 PyCharm 使用技巧(二)
这是 「PyCharm 技巧分享」系列的第二篇分享.由于上一篇文章得到了大家的一些赞同,所以今天又来给大家推荐一些我平时自己有用的小技巧,大家择需所取即可. 先声明下,并不保证对所有的人都是有帮助的, ...
- 每周分享五个 PyCharm 使用技巧(一)
PyCharm 是大多数 Python 开发者的首选 IDE,每天我们都在上面敲着熟悉的代码,写出一个又一个奇妙的功能. 一个每天都在使用的工具,如果能掌握一些高效的使用技巧,肯定会给我们的开发效率带 ...
随机推荐
- windows下搭建学习objective-c 的运行环境【转载】
对于Iphone开发学习者而言,Object -c 是必修的语言.但是由于苹果的自我封闭的产业链发展模式(从芯片.机器.开发语言.终端产品.服务)的限制,要想开发针对苹果iPhone等产品的应用程序, ...
- 移除http响应中的多余的头(X-AspNet-Version,Server等)
网上搜索出很多方法了,这里记录一下: 如果是asp.net mvc的话还得在global.ascx中加入: 至于移除Server头,按网上的写法写httpmoudle后发现无效的,最后还是用了微软官方 ...
- jQuery Mobile 移动开发中的日期插件Mobiscroll使用说明
近期在移动方面的开发,使用jQuery Mobile ,移动方面的插件不如Web 方面的插件多,选择的更少,有一些需要自己去封装,但功力尚不足啊. 日期插件JQM也提供了内置的,但样式方面不好看,只好 ...
- wwdc2016-session707 Notifications(draft)
Introduction to Notificationshttps://developer.apple.com/wwdc2016/707 通知这哥们说话有点不清晰啊. 远程通知本地通知 可以被操作的 ...
- WebApp开发之--"rem"单位(转)
随着web app的兴起,rem这是个低调的css单位,近一两年开始崭露头角,有许多朋友对于它的评价不一,有的在尝试使用,有的在使用过程中遇到坑就弃用了.但是我认为rem是用来做web app它绝对是 ...
- C# MailMessage Attachment 中文名附件发邮件-Firefox中文显示正常,网页打开邮件附件中文名乱码
一.故事 首先通过CDO.Message来获取邮件EML相关数据:邮件标题.邮件内容.邮件附件.发件人.收件人.CC主要就这么几个,其次通过MailMessage来组织邮件通过Python来发送邮件! ...
- Jquery中使用setInterval和setTimeout会提示缺少对象的错误,解决方法如下:
直接在ready中调用其他方法,会提示缺少对象的错误,解决方法如下: 方法1. 应用jQuery的扩展可以解决这个问题. $(document).ready(function(){ $.extend( ...
- CSS基础(一):开篇
背景 HTML是一种超文本标记语言,用来定义文档的结构和内容,例如标题.段落和列表等等,而文档内容如何渲染.如何展示,这就需要样式来修饰了.CSS正是可以与HTML很好地结合.如果将HTML比作水,那 ...
- Flash Media Server 4.5 序列号 (fms4.5 激活码)
激活码一枚 ,网上找不到的..我今天放出来了哦... 1462-5864-7783-6034-8316-3718 (亲测 可用) 安装前找到系统盘下windows/system32/driv ...
- android判断EditText输入的数字、中文还是字母方法
String txt = edInput.getText().toString(); Pattern p = Pattern.compile("[0-9]*"); Mat ...

