Windows高DPI系列控件(二) – 柱状图

目录

原文链接:Windows高DPI系列控件(二) – 柱状图

一、QCP

QCP全称QCustomPlot,是一个基于Qt的图表库,同时支持Qt4和Qt5,使用起来照样很利便的,不管是编译成dll照样直接嵌入到我们自己的程序都是极其容易,究竟只有两个文件。之前写过几篇简朴的关于QCP的文章,从使用的角度剖析了QCP的一些简朴用法,包罗:QCustomplot使用分享(一) 能做什么事QCustomplot使用分享(二) 源码解读QCustomplot使用分享(三) 图QCustomplot使用分享(四) QCPAbstractItemQCustomplot使用分享(五) 结构QCustomplot使用分享(六) 坐标轴和网格线QCustomplot使用分享(七) 层(完结),感兴趣的同砚可以抽时间阅读一遍。高DPI系列控件大多数也都是基于QCP库举行的定制,首先是对QCP的包装,其次其次也对QCP源码举行了高DPI适配,主要照样针对我自己的DPI框架举行适配。

此高DPI适配框架,在DPI为96整数倍下是异常完善的,无失真情形,其他非整数比缩放情形下会有一些小瑕疵,好比非整数缩放比下图片举行过拉伸,某些拉伸会导致失真的图片会发生模糊的情形;其次当缩放比不是0.5的整数倍时,字号放大有可能不是按比例的,会导致界面字体不协调,然则字体不会发虚。对于大多数用户来说,现在的高DPI框架都是可以知足的,好比我小我私家的显示就是一个1080P显示器和一个4K显示器,两个显示器的缩放比分别是100%和200%,没有任何失真的情形。

上一篇文章Windos高DPI系列控件(一) – 饼图中讲过怎么在高DPI显示器下绘制饼图,而且坐到没有任何的失真,究竟都是自己绘制的。饼图控件是很早以前就完成的功效,这一篇文章主要是对他举行了高DPI的适配,让饼图控件在我的4K显示器上也可以很友好的展示,而且可以在两个显示器直接无缝切换。

本篇文章是继Windos高DPI系列控件(一) – 饼图文章后的第二篇关于高DPI控件的实例,包罗后续会定制的一些列高DPI控件,大多数基于QCP来实现。文章前面也说过,QCP是一个异常壮大的绘图库,对照遗憾的就是没有饼图这个控件,上一篇Windos高DPI系列控件(一) – 饼图控件是我参考QCP的代码自己实现的绘制,效率也是杠杠滴,除此之外QCP也是异常容易扩展的,好比可以自己添加新的层,去绘制一些自己需要的器械,多说一句QCP2.0版本区别于1.0版本又一个很大的优势就是多层绘制的,这样效率对照高,而且您也可以指定某个层举行单独刷新,本篇文章讲到的柱状图上的tooltips提醒实现方式就是新增了一个tooltips绘制层,这样实现的利益就是跟现有框架的其他层代码是解耦的,好比后期您不要这个层了,可以直接删掉,或者隐藏都是可以的,这样对整个代码运行效率险些没有任何损失。

二、效果展示

如下图所示,柱状图和折线图适配高DPI后展示效果。

左右两侧的显示物理尺寸一致,也就是视觉上巨细一样大,差别的是左侧是1080P显示器,右侧是4K显示器

由于是视频录制缘故原由,可能会有视觉误差,现实看的话,左右两个窗体给人的视觉感受巨细是一样的。

Windows高DPI系列控件(二) - 柱状图

《往返切换显示器》


Windows高DPI系列控件(二) - 柱状图

《柱状图》

三、高DPI适配

1、自定义柱状图

QCP库中的柱状图支持多种模式,上一小节中的效果图是通俗的柱状图展示效果,除此之外QCP中的柱状图可以做到多组同时展示,而且两组柱状图之间可以堆叠,由于适配需要花更多的现实,因此更多庞大的展示效果会在后续的文章中会陆续提供。

如《柱状图》展示图中的效果,我们定制了柱状图一个新的柱状图类,主要是基于该类我们支持了展示tooltips效果,下面主要先容下对照主要的几个实现点

tips实现

一文读懂链表反转(迭代法和递归法)

文章开头也提到过,QCP2.0是多层绘制的,这样可以提高绘制效率,而且提升更强的扩展能力,如QCustomplot使用分享(七) 层(完结)这篇文章所剖析的那样。本篇文章中的提醒框就是继续可绘制工具QCPLayerable,界面上险些所有的绘制元素包罗结构元素都是继续自这个类,该类有一个draw函数,实现该接口就可以实现我们自己想要绘制的器械,界面绘制时,绘制区域取决于自己所在的结构区域。

#ifndef CHARTTIP_H
#define CHARTTIP_H

#include "qcp/QCustomplot.h"

class CBaseToolTip : public  QCPLayerable
{
	Q_OBJECT

public:
	CBaseToolTip(QCustomPlot * plot);
	~CBaseToolTip();

public:
	QString LayerName() const;
	void SetVisible(bool visible);//ÉèÖòãÊÇ·ñ»æÖÆ

	void SetTipLabel(const QVector<QString> & labels);
	void SetTopLeft(const QPoint & pos);

protected:
	virtual void applyDefaultAntialiasingHint(QCPPainter * painter) const override{};
	virtual void draw(QCPPainter * painter) override;

	virtual void DrawTip(QCPPainter * painter) = 0;

protected:
	QPoint m_AnchorPos;
	QVector<QString> m_Labels;

private:

};

class CSideToolTip : public  CBaseToolTip
{
public:
	CSideToolTip(QCustomPlot * plot);
	~CSideToolTip(){}

protected:
	virtual void DrawTip(QCPPainter * painter) override;
};

class CTopToolTip : public  CBaseToolTip
{
public:
	CTopToolTip(QCustomPlot * plot);
	~CTopToolTip(){}

protected:
	virtual void DrawTip(QCPPainter * painter) override;

private:
	int radius = 3;
	int height = 46;
	int width = 125;
};

#endif // CHARTTIP_H

上述头文件中,包罗三个类,其中CBaseToolTip是tip提醒的基类,他卖力完成了tip工具的一些公有属性,好比组织该绘制工具时自动建立tip绘制层,并把层加到QCP窗口工具上;除此之外还可以指定绘制的位置等。

本篇文章中的tip是CSideToolTip类来实现的,下面是该类详细的绘制逻辑,仔细一看下面的diamante,发现这里又泛起了我们熟悉的XXX_SCALE_NUMBER宏,不错这个宏就是用来实现高DPI逻辑的,好比下面绘制矩形框的逻辑,矩形的长和宽都被我们用QCP_PAINTER_SCLAE_NUMBER宏给包裹起来,这样绘制时绘制的矩形巨细就是我们需要的巨细。

#define QCP_PAINTER_SCLAE_NUMBER(a) (a) * painter->dpi_scale 

这里简朴说下这个宏的作用,QCPPainter源码自己是没有dpi_scale这个变量的,为了更好的适配高DPI显示器,这个变量是我自己加上的,示意当前绘制painter需要缩放的比例,好比我们绘制tip矩形区域时,就需要对长和宽举行需要的缩放。

除过QCPPainter我添加了dpi_scale缩放系数成员变量以外,QCPLayerable基类我也添加了该成员变量,而且在合适的现实都市举行变量更新,后续我会专门写一篇关于QCP适配高DPI的文章,专门解说针对高DPI适配我都做了哪些事情。

void CSideToolTip::DrawTip(QCPPainter * painter)
{
	//绘制圆圈
	painter->setPen(Qt::transparent);
	// 	painter->setBrush(QColor(255, 204, 51, 80));
	// 	painter->drawEllipse(m_AnchorPos, 5, 5);
	painter->setBrush(QColor(255, 181, 26));
	painter->drawEllipse(m_AnchorPos, int(QCP_PAINTER_SCLAE_NUMBER(3)), int(QCP_PAINTER_SCLAE_NUMBER(3)));

	//绘制矩形 135 * 65
	QRect rect(0, 0, QCP_PAINTER_SCLAE_NUMBER(100), QCP_PAINTER_SCLAE_NUMBER(30));
	rect.moveBottomRight(m_AnchorPos - QPoint(QCP_PAINTER_SCLAE_NUMBER(8), 0));

	if (rect.left() < mParentPlot->axisRect()->outerRect().left())
	{
		rect.moveBottomLeft(m_AnchorPos + QPoint(QCP_PAINTER_SCLAE_NUMBER(8), 0));
	}

	painter->setPen(QColor(255, 204, 51));
	painter->setBrush(QColor(0, 0, 0, 255 * 0.7));
	painter->drawRect(rect);

	//绘制矩形框文字
	QFont font(QStringLiteral("微软雅黑"));
	font.setPixelSize(QCP_PAINTER_SCLAE_NUMBER(10));
	painter->setFont(font);

	painter->drawText(QPoint(QCP_PAINTER_SCLAE_NUMBER(8), QCP_PAINTER_SCLAE_NUMBER(13)) 
		+ rect.topLeft(), QStringLiteral("两融余额:%1").arg(m_Labels[0]));
	painter->drawText(QPoint(QCP_PAINTER_SCLAE_NUMBER(8), QCP_PAINTER_SCLAE_NUMBER(25)) 
		+ rect.topLeft(), QStringLiteral("上证指数:%1").arg(m_Labels[1]));
}

2、新的柱状图

新的柱状图被我命名为CTooltipBars,该类没有什么对照牛逼的实现,代码量也不大,其中有一个对照主要的接口OnCheckHover和一个要害信号HoverIndex;

#ifndef TIPBAR_H
#define TIPBAR_H

#include "../rluilib/dpi_macro.h"

#include "qcp/QCustomplot.h"

class QMouseEvent;
class CTooltipBars : public QCPBars
{
	Q_OBJECT

signals :
	void HoverIndex(double index);

public:
	CTooltipBars(float scale, QCPAxis * keyAxis, QCPAxis * valueAxis);
	~CTooltipBars();

public:
	void SetValueVisible(bool visible);

public slots:
	void OnCheckHover(QMouseEvent * pos);

protected:
	virtual void draw(QCPPainter * painter) override;

private:
	bool m_bValueVisible = false;
	int m_iLabelHeight = 0;
};

#endif // TIPBAR_H

OnCheckHover:hover行为检测接口,当我们移动鼠标时,该接口会重复被触发去检测是否hover到了某个柱子上,若是hover乐成那么就会触发HoverIndex信号,参数示意hover的柱子序号。

检测鼠标hover事宜的代码如下图所示,整个代码的焦点头脑就是获取所有可见的柱子数据,然后循环去判断鼠标当前的坐标是否在哪个柱子的区域内,并触发HoverIndex信号,参数为-1时示意没有hover到任何柱子,那么此时可能需要隐藏已经展示的tip提醒。

void CTooltipBars::OnCheckHover(QMouseEvent * event)
{
	QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
	getVisibleDataBounds(visibleBegin, visibleEnd);

	QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
	getDataSegments(selectedSegments, unselectedSegments);
	allSegments << unselectedSegments << selectedSegments;

	double success = -1;

	for (int i = 0; i < allSegments.size(); ++i)
	{
		bool isSelectedSegment = i >= unselectedSegments.size();
		QCPBarsDataContainer::const_iterator begin = visibleBegin;
		QCPBarsDataContainer::const_iterator end = visibleEnd;
		mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
		if (begin == end)
		{
			continue;
		}

		for (QCPBarsDataContainer::const_iterator it = begin; it != end; ++it)
		{
			QRectF rect = getBarRect(it->key, it->value);
			rect = rect.adjusted(0, -m_iLabelHeight, 0, 0);
			bool contain = rect.contains(event->pos());
			if (contain)
			{
				success = it->key;
				break;
			}
		}
		if (success != -1)
		{
			break;
		}
	}

	if (mParentPlot->axisRect()->rect().contains(event->pos()) == false)
	{
		success = -1;
	}

	emit HoverIndex(success);
}

HoverIndex:柱状图hover时触发信号,示意鼠标在参数指定的柱子内

3、测试代码

如下代码是效果图中的测试代码,我们组织了一个CBarChart工具,然后添加了一些简朴的额测试数据,用起来是不是很爽呢!

QWidget * CreateBar(float scale)
{
	CBarChart * barChart = new CBarChart(scale);
	BarDataList datas;
	datas.push_back({ 1585816691, 200, });
	datas.push_back({ 1588408691, 150, });
	datas.push_back({ 1591087091, 220, });
	datas.push_back({ 1593679092, 100, });
	barChart->SetDatas(datas);
	barChart->SetYRange(QCPRange(0, 250), 5);
	return barChart;

    QStringLiteral("柱状图"));
}

CBarChart类是一个对外的柱状图使用类,主要是把支持tip的柱状图、tip类和legend类举行了组装,这里就不在详细解说了,该类的成员变量如下代码所示,其中另有一些辅助性的成员,好比说折线图QCPGraph类,QCP唯一窗口类QCustomPlot,该类也是我们绘制图表时不可或缺的工具,以是的绘制挪用逻辑都是从该窗口的paintEvent函数最先出发的,而且该类做了种种绘制优化操作,而且支持3种绘制方式,感兴趣的同砚可以搜索下QCPAbstractPaintBuffer这个类,这个是绘制buffer基类,其他绘制的实现都是基于该类实现。

struct BarChartPrivate
{
	QVector<double> m_TickKey;
	QVector<QString> m_TickNames;
	QLabel * m_pBarLabel = nullptr;
	QLabel * m_pGraphLabel = nullptr;
	CLegendWidget * m_pLegend = nullptr;
	CBaseToolTip * m_pToolTip = nullptr;
	QCPGraph * m_pGraph = nullptr;
	QList<CTooltipBars *> m_pBars;
	QSharedPointer<QCPAxisTickerText> m_pXAxisTicker;
	QCustomPlot * m_pWidget = nullptr;
	QCPMarginGroup * m_pMarginGroup = nullptr;
};

四、相关文章

  1. Qt之高DPI显示器(一) – 解决方案整理
  2. Qt之高DPI显示器(二) – 自适配解决方案剖析
  3. Qt之自绘制饼图
  4. QCustomPlot之结构系统
  5. QCustomplot使用分享(一) 能做什么事
  6. QCustomplot使用分享(二) 源码解读
  7. QCustomplot使用分享(三) 图
  8. QCustomplot使用分享(四) QCPAbstractItem
  9. QCustomplot使用分享(五) 结构
  10. QCustomplot使用分享(六) 坐标轴和网格线
  11. QCustomplot使用分享(七) 层(完结)
  12. Windos高DPI系列控件(一) – 饼图

值得一看的优异文章:

  1. 财联社-产物展示
  2. 广联达-产物展示
  3. Qt定制控件列表
  4. 牛逼哄哄的Qt库

若是您以为文章不错,不妨给个
打赏,写作不易,谢谢列位的支持。您的支持是我最大的动力,谢谢!!!

Windows高DPI系列控件(二) - 柱状图 Windows高DPI系列控件(二) - 柱状图

很主要–转载声明

  1. 本站文章无稀奇说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords

  2. 如要转载,请原文转载,如在转载时修改本文,请事先见告,谢绝在转载时通过修改本文到达有利于转载者的目的。

原创文章,作者:28x29新闻网,如若转载,请注明出处:https://www.28x29.com/archives/20333.html