Windows high DPI series controls (2) - histogram


Original Link: Windows High DPI Series Controls (2) - Histogram

1. QCP

The full name of QCP is QCustomPlot. It is a chart library based on Qt. It supports both Qt4 and Qt5. It is very convenient to use. It is extremely easy to compile it into a dll or directly embed it into our own program. After all, there are only two files. I have written a few simple articles about QCP before, and analyzed some simple usages of QCP from the perspective of use, including: QCustomplot use sharing (1) what can be done , QCustomplot use sharing (2) source code interpretation , QCustomplot use sharing ( 3) Figure , QCustomplot use sharing (4) QCPAbstractItem , QCustomplot use sharing (5) layout , QCustomplot use sharing (6) coordinate axes and grid lines and QCustomplot use sharing (7) layer (end) , interested students can draw Time to read it again. Most of the high DPI series controls are also customized based on the QCP library. Firstly, it is the packaging of QCP, and secondly, it also adapts the QCP source code to high DPI, mainly for my own DPI framework.

This high DPI adaptation framework is perfect when the DPI is an integer multiple of 96. There is no distortion. In other non-integer ratio scaling cases, there will be some small flaws. Stretching will cause the distorted picture to be blurred; secondly, when the zoom ratio is not an integer multiple of 0.5, the font size may not be enlarged proportionally, which will cause uncoordinated fonts on the interface, but the fonts will not be blurred. For most users, the current high DPI framework is sufficient. For example, my personal display is a 1080P display and a 4K display. The scaling ratios of the two displays are 100% and 200% respectively, without any distortion. Condition.

The previous article, Windos High DPI Series Controls (1) - Pie Chart, talked about how to draw a pie chart under a high DPI monitor, and sit without any distortion, after all, it is drawn by myself. The pie chart control is a function that was completed a long time ago. This article mainly adapts it to high DPI, so that the pie chart control can also be displayed very friendly on my 4K monitor, and can be displayed on two Displays are switched directly and seamlessly.

This article is the second example of high DPI controls after the Windos high DPI series controls (1) - pie chart article, including some columns of high DPI controls that will be customized later, most of which are implemented based on QCP. As mentioned earlier in the article, QCP is a very powerful drawing library. It is a pity that there is no pie chart control. In the previous Windos high DPI series controls (1) - the pie chart control is a drawing that I realized by referring to the QCP code. , The efficiency is also great. In addition, QCP is also very easy to expand. For example, you can add new layers by yourself to draw some things you need. Let me say that QCP2.0 version is different from 1.0 version. Another great advantage It is drawn in multiple layers, which is more efficient, and you can also specify a layer for separate refresh. The implementation of the tooltips prompt on the histogram mentioned in this article is to add a new tooltips drawing layer. The benefits of this implementation It is decoupled from other layer codes of the existing framework. For example, if you don’t want this layer later, you can delete it directly or hide it, so that there is almost no loss in the efficiency of the entire code.

2. Effect display

As shown in the figure below, the histogram and line chart are displayed after being adapted to high DPI.

The physical size of the display on the left and right sides is the same, that is, the visual size is the same, the difference is that the left side is a 1080P display, and the right side is a 4K display

Because of the video recording, there may be visual errors. In reality, the left and right windows give people the same visual perception.

"Switching Displays Back and forth"


"Histogram"

3. High DPI adaptation

1. Custom histogram

The histogram in the QCP library supports multiple modes. The rendering in the previous section is a common histogram display effect. In addition, the histogram in QCP can be displayed in multiple groups at the same time, and between two sets of histograms It can be stacked. Since adaptation requires more practice, more complex display effects will be provided in subsequent articles.

For example, the "Histogram" shows the effect in the picture. We have customized a new histogram class for the histogram. Based on this class, we support the display of the tooltips effect. The following mainly introduces the more important implementation points

Tips Realization

As mentioned at the beginning of the article, QCP2.0 is drawn in multiple layers, which can improve drawing efficiency and enhance stronger expansion capabilities, as analyzed in this article using QCustomplot to share (seven) layers (end) . The prompt box in this article is to inherit the drawable object QCPLayerable. Almost all drawing elements on the interface, including layout elements, are inherited from this class. This class has a draw function. Implementing this interface can realize what we want to draw Things, when the interface is drawn, the drawing area depends on the layout area where it is located.

#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

The above header file includes three classes, among which CBaseToolTip is the base class of the tip, which is responsible for completing some public properties of the tip object, such as automatically creating the tip drawing layer when constructing the drawing object, and adding the layer to the QCP window object On; in addition, you can also specify the location of the drawing, etc.

The tip in this article is implemented by the CSideToolTip class. The following is the specific drawing logic of this class. Take a closer look at the diamante below and find that the familiar XXX_SCALE_NUMBER macro appears here. Yes, this macro is used to implement high DPI logic. Yes, for example, the logic of drawing a rectangular frame below, the length and width of the rectangle are wrapped by the QCP_PAINTER_SCLAE_NUMBER macro, so that the size of the rectangle drawn when drawing is the size we need.

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

Here is a brief introduction to the function of this macro. The QCPPainter source code itself does not have the variable dpi_scale. In order to better adapt to high-DPI displays, this variable is added by myself to indicate the scaling ratio of the current drawing painter. For example, we draw When tipping a rectangular area, the length and width need to be scaled as necessary.

In addition to QCPPainter, I added the dpi_scale zoom factor member variable, and I also added this member variable to the QCPLayerable base class, and the variable will be updated when appropriate. I will write an article about QCP adapting to high DPI later. Specifically explain what work I have done for high DPI adaptation.

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. New histogram

The new histogram is named CTooltipBars by me. This class has no awesome implementation, and the amount of code is not large. There is a relatively important interface OnCheckHover and a key signal 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 behavior detection interface, when we move the mouse, this interface will be triggered repeatedly to detect whether the hover is on a column, if the hover is successful, the HoverIndex signal will be triggered, and the parameter indicates the column number of the hover.

The code for detecting the mouse hover event is shown in the figure below. The core idea of ​​the whole code is to obtain all visible column data, and then loop to determine whether the current coordinates of the mouse are in the area of ​​which column, and trigger the HoverIndex signal. When the parameter is -1 Indicates that there is no hover to any column, then it may be necessary to hide the displayed tip at this time.

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 : The signal is triggered when the histogram hovers, indicating that the mouse is in the column specified by the parameter

3. Test code

The following code is the test code in the effect diagram. We constructed a CBarChart object, and then added some simple test data. Isn’t it very cool to use?

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("柱状图"));
}

The CBarChart class is an external histogram use class. It mainly assembles the histogram supporting tip, tip class and legend class. I will not explain it in detail here. The member variables of this class are shown in the following code, and there are some Auxiliary members, such as the line chart QCPGraph class, QCP's only window class QCustomPlot, this class is also an indispensable object when we draw charts, so the drawing call logic starts from the paintEvent function of the window, and the The class has done various drawing optimization operations and supports 3 drawing methods. Interested students can search for the QCPAbstractPaintBuffer class. This is the base class of drawing buffer, and other drawing implementations are based on this class.

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;
};

4. Related Articles

  1. Qt's high DPI display (1) - solution arrangement
  2. Qt's High DPI Display (2) - Adaptive Solution Analysis
  3. Qt's self-drawing pie chart
  4. Layout system of QCustomPlot
  5. QCustomplot use sharing (1) what can be done
  6. QCustomplot use sharing (2) source code interpretation
  7. QCustomplot use sharing (3) Figure
  8. QCustomplot use sharing (4) QCPAbstractItem
  9. QCustomplot uses sharing (5) layout
  10. QCustomplot uses shared (six) coordinate axes and grid lines
  11. QCustomplot uses sharing (seven) layers (end)
  12. Windos high DPI series controls (1) - pie chart

Excellent articles worth a look:

  1. Financial Associated Press-Products
  2. Glodon-Products
  3. Qt custom control list
  4. Awesome Qt library

If you think the article is good, you may wish to give a reward, writing is not easy, thank you for your support. Your support is my greatest motivation, thank you! ! !




It's Important – Reprint Statement

  1. The articles on this site have no special instructions, all are original and copyrighted, please use the link when reprinting, and give the source of the original text. At the same time, write the original author: morning ten night eight or Twowords

  2. If you want to reprint, please reprint the original text. If you modify this article when reprinting, please inform in advance. It is forbidden to modify this article to benefit the reprinter when reprinting.


Guess you like

Origin blog.csdn.net/qq_30392343/article/details/107142498