Qt5繪制儀表盤dashboard


說明

  • 本文演示Qt版本: Qt5.14.
  • 本文將使用QPainter一步一步繪制儀表盤:刻度、指針、刻度值
  • 注意: 繪制順序,如果先繪制,則后來繪制的將會覆蓋住先前繪制的。
  • 如果需要繪制半透明, 請設置QColor的第四個參數 alpha , 范圍: 0~255, 0 - 全透明, 255-不透明

樣式

  • 比較粗糙, 后面在優化
  • 這是工作上遇到的需要繪制的圖形。
  • 藍色的是指針indicator

下面一步一步開始繪制

開始前之前

  • 重寫函數paintEvent( QPaintEvent *event )QtGuiApplication1繼承自QWidget.
  • 設置反走樣(抗鋸齒)
	QPainter painter( this );
	painter.setRenderHint( QPainter::Antialiasing, true);

繪制圓

代碼

void QtGuiApplication1::draw_ellipse_( QPainter& painter )
{
	painter.save();
	/// -----------------------------------------------------------------

	/// 設置畫筆顏色
	painter.setPen(QPen(QColor(100, 200, 100), 2));

	/// 直接繪制圓
	painter.drawEllipse( circle_config_.start_x_, circle_config_.start_y_,
						 circle_config_.radius_ * 2, circle_config_.radius_ * 2 );
	/// -----------------------------------------------------------------
	painter.restore();
}

效果

繪制刻度

每10度繪制一個刻度線,每30度繪制一個較長的刻度線

代碼

void QtGuiApplication1::draw_dial_( QPainter& painter )
{
	painter.save();

	painter.setPen(QPen(QColor(200, 100, 200), 2));

	/// 旋轉坐標
	/// 第一個參數: 坐標起點X
	/// 第二個參數: 坐標起點Y
	/// 第三個參數: 相對當前坐標系 向目標坐標系需要旋轉多少度
	/// 第四個參數: 是否繪制較長的刻度
	auto draw_dial = [&]( const double& xxx, const double& yyy, const double& angle, const bool& is_longer )
	{
		int dial_len = 8;

		if (true == is_longer)
			dial_len = 30;

		/// 重置坐標系
		painter.resetTransform();
		/// 將其移動到目標點
		painter.translate(xxx, yyy );
		/// 再旋轉指定角度
		painter.rotate( angle );
		/// 繪制刻度
		painter.drawLine( QLine(QPoint(0, 0), QPoint(-dial_len, 0)) );

	};

	
	/// 圓。 360度, 每10度繪制一個刻度線
	for (int angle = 0; angle <= 360; angle += 10)
	{
		double xxx = 0;
		double yyy = 0;
		/// 根據圓心得到園周邊的點坐標
		get_dial_config_(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, xxx, yyy, angle);

		/// 每30°繪制一個長一點的刻度
		bool is_longer = ( 0 == ( angle % 30 ) ? true : false );
		/// 繪制刻度
		draw_dial( xxx, yyy, angle, is_longer);
	}

	painter.restore();

}

效果

繪制指針

  • 這里是用扇形標識指針
  • 注意: X軸正方向是3點鍾反向

代碼

/// --------------------------------------------------------------------------------------------------------
///	@brief: 	   繪制指針
/// --------------------------------------------------------------------------------------------------------
void QtGuiApplication1::draw_indicator_( QPainter& painter )
{
	painter.save();
	/// -----------------------------------------------------------------

	painter.setPen(QPen(QColor(0, 0, 239)));			 
	QBrush brush( QBrush( QColor( 0, 0, 239, 100) ) );
	painter.setBrush(brush);

	/// 在矩形內切橢圓上畫,3點鍾為0度,逆時針為正,角度要乘以16(參考文檔)
	/// 起點, 
	int startAngle	= -3 * 16;
	/// 圓弧的角度大小
	int spanAngle	= 6 * 16;	
	painter.drawPie(circle_config_.start_x_, circle_config_.start_y_, circle_config_.radius_ * 2, circle_config_.radius_ * 2, 
					 startAngle, spanAngle);

	/// -----------------------------------------------------------------
	painter.restore();
}

效果

繪制刻度數字

  • 刻度數字其實也是繪制在圓周邊上的,比如,上圖的中的半徑為R,而刻度文字所在圓的半徑則是 0.8倍R,這樣就可以將文字繪制圈圈內了。

代碼

void QtGuiApplication1::draw_scale_number_( QPainter& painter )
{
	painter.save();
	/// -----------------------------------------------------------------


	double rrr		= circle_config_.radius_ * 0.8 ;
	painter.setFont( QFont( "Microsoft Yahei", 10 ) );

	QFontMetrics fm( painter.font() );


	/// for (int index = -150; index <= 150; index += 30)
	for (int index = 0; index < 360; index += 30)
	{
		double xxx = 0;
		double yyy = 0;
		/// 根據圓心得到園周邊的點坐標
		get_dial_config_( circle_config_.center_x_, circle_config_.center_y_, rrr, xxx, yyy, index );

		int dial_num = index - 270;

		/// 3點為X軸, 故這里要特殊顯示為90 ~ 150
		if (90 > index)
			dial_num = index + 90;

		/// 180不顯示
		if (-180 == dial_num)
			continue;

		/// 下面這一步是為了得到繪制文字的寬和高, 從而決定文字的起點坐標
		/// -----------------------------------------------------------------
		/// 字體的高度
		int fontw			= fm.width(QString::number( dial_num));
		/// 字體的寬度
		int fonth			= fm.height();
		
		xxx -= fontw / 2.0;
		yyy += fonth / 4.0;
		/// -----------------------------------------------------------------

		painter.drawText( xxx, yyy,  QString::number( dial_num));
	}

	painter.restore();
}

效果

下面我們讓指針動起來

開始之前

  • UI增加控件 QSlider,設置其范圍是0 ~360
  • 關聯滑塊的信號,當值發生變化,則觸發重繪
void QtGuiApplication1::slot_slider_value_changed_( int value )
{
	/// 指針當前角度
	circle_config_.cur_angle_ = ( double ) value;
	QWidget::update();
}

繪制指針

這里只需要將 起始角度 -3 改為 當前滑塊的值即可。 完整代碼如下

代碼

void QtGuiApplication1::draw_indicator_( QPainter& painter )
{
	painter.save();
	/// -----------------------------------------------------------------

	painter.setPen(QPen(QColor(0, 0, 239)));			 
	QBrush brush( QBrush( QColor( 0, 0, 239, 100) ) );
	painter.setBrush(brush);

	/// 在矩形內切橢圓上畫,3點鍾為0度,逆時針為正,角度要乘以16(參考文檔)
	/// 起點, 
	int startAngle	= (circle_config_.cur_angle_ - 3.0) * 16;
	/// 圓弧的角度大小
	int spanAngle	= 6 * 16;	
	painter.drawPie(circle_config_.start_x_, circle_config_.start_y_, circle_config_.radius_ * 2, circle_config_.radius_ * 2, 
					 startAngle, spanAngle);

	/// -----------------------------------------------------------------
	painter.restore();
}

效果

補充

  • 實戰中,發現使用 扇形繪制的指針,指針填充漸變色的時候 不太理想, 因為按照公式計算的半徑得到的坐標總是不對。 於是 換了中繪制指針的方法: 畫兩根線, 兩根線中間用畫刷刷成指定的漸變色。
  • 為了見刻度顯示在指針的上面, 因此,先與刻度繪制指針。

效果圖

代碼

這里是先繪制的指針,再繪制的周圍的刻度



	draw_indicator2_(painter);
	/// 繪制指針
	//draw_indicator_(painter);

	/// 繪制圓
	draw_ellipse_(painter);
	/// 繪制刻度
	draw_dial_(painter);


	/// 繪制刻度值
	draw_scale_number_(painter);

繪制指針

/// --------------------------------------------------------------------------------------------------------
///	@brief: 繪制指針
/// --------------------------------------------------------------------------------------------------------
void QtGuiApplication1::draw_indicator2_(QPainter& painter)
{
	painter.save();
	/// -----------------------------------------------------------------

	/// 計算指針兩條線的坐標, 方法: 以圓心為起點,旋轉指定角度繪制兩條只想, 夾角暫定為6度
	/// 起始角度為當前角度 - 3(3 = 6 / 2.0)
	auto draw_line = [&](const double& startx, const double& starty, const double& radius, const double& angle)
	{
		painter.resetTransform();
		painter.translate(startx, starty);
		painter.rotate(angle);

		painter.drawLine(0, 0, radius, 0);
	};
	
	/// 因為是逆時針繪制的夾角,
	draw_line(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, circle_config_.cur_angle_ - 3);
	draw_line(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, circle_config_.cur_angle_ + 3);

	/// -----------------------------------------------------------------
	painter.restore();
}

加填充色填充

  • 這里以填充漸變色, 還能學學 漸變色 QLinearGradient類的用法
  • 老規矩, 上效果:

代碼

/// --------------------------------------------------------------------------------------------------------
///	@brief: 繪制指針
/// --------------------------------------------------------------------------------------------------------
void QtGuiApplication1::draw_indicator2_(QPainter& painter)
{
	painter.save();
	/// -----------------------------------------------------------------

	/// 計算指針兩條線的坐標, 方法: 以圓心為起點,旋轉指定角度繪制兩條只想, 夾角暫定為6度
	/// 起始角度為當前角度 - 3(3 = 6 / 2.0)
	auto draw_line = [&](const double& startx, const double& starty, const double& radius, const double& angle)
	{
		painter.save();
		
		painter.resetTransform();
		painter.translate(startx, starty);
		painter.rotate(angle);

		painter.drawLine(0, 0, radius, 0);
		
		painter.restore();
	};
	
	/// 因為是逆時針繪制的夾角,
	draw_line(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, circle_config_.cur_angle_ - 3);
	draw_line(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, circle_config_.cur_angle_ + 3);


	/// 計算漸變色的坐標
	/// 漸變色的坐標起點是圓心 終點 就是 指針與圓周邊的交點
	/// 這里的指針是用一個三角形代替指針的,當周圍的刻度繪制在指針的上面,哈哈遮住了 圓弧那段沒有填充色的部分

	/// 指針的兩條直線之一A與圓周邊的焦點A
	double lg_a_end_x = 0;
	double lg_a_end_y = 0;
	get_dial_config_(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, lg_a_end_x, lg_a_end_y, circle_config_.cur_angle_ - 3);
	
	/// 指針的兩條直線之一B與圓周邊的焦點A
	double lg_b_end_x = 0;
	double lg_b_end_y = 0;
	get_dial_config_(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, lg_b_end_x, lg_b_end_y, circle_config_.cur_angle_ + 3);

	/// 三角區域
	const QPointF triganle_points[3] = 
	{
		QPointF(circle_config_.center_x_, circle_config_.center_y_), 
		QPointF(lg_a_end_x, lg_a_end_y),
		QPointF(lg_b_end_x, lg_b_end_y)
	};

	/// 當前角度與圓的交點坐標,用於確定填充區域
	double lg_end_x = 0;
	double lg_end_y = 0;
	get_dial_config_(circle_config_.center_x_, circle_config_.center_y_, circle_config_.radius_, lg_end_x, lg_end_y, circle_config_.cur_angle_);


	QLinearGradient lg(circle_config_.center_x_, circle_config_.center_y_, lg_end_x, lg_end_y);

	/// 0 ~ 0.3的區域填充 紅色
	lg.setColorAt(0.3, QColor(230, 0, 0));
	/// 0.3 ~ 0.6的區域填充綠色
	lg.setColorAt(0.6, QColor(0, 230, 0));
	/// 0.6~1之間的區域填充藍色
	lg.setColorAt(1.0, QColor(0, 0, 230));

	/// 設置划線的顏色
	painter.setPen(QPen(QColor(30, 30, 30), 2));

	/// 設置畫刷顏色
	painter.setBrush(QBrush(lg));

	painter.drawPolygon(triganle_points, 3, Qt::OddEvenFill);

	/// -----------------------------------------------------------------
	painter.restore();
}

gif效果圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM