前言
(1)QPainter用来执行具体的绘图相关的操作,用来画点,线,填充,变换,alpha/阿尔法通道(透明度)
Appha的值越大,就越不透明,范围是0-255,255就是不透明,0就是完全透明;当对应 RGB 颜色时,
Alpha 会叠加到颜色上面;只有当 Alpha 通道是255时,才是其真正的颜色。
(2)QPainterDevice是Qpainter用来绘图设备,Qt中有几种绘图的设备,如QWidget,Qpainter,QPaxmip
都是从QPainterDevice继承的。例如QPainter painter(this); 把绘图设备指针传递painter,把当前控件指针初始化。
(3)QPainterEngine类提供了不同类型设备接口,对程序员不透明,由QPainter,QpaintDevive类进行交互
一个绘图的操作流程就是:对一个QPainterDevice直接或间接调用Qpainter类。Qpainter类内部调用QpaintEngine进行绘图,而(4)QPainterEngine类通常是由QPainterDevice类负责创建和管理。
Qwidget类,最低层的类,接收鼠标,键盘和从其他敞口系统的事件,并且绘制在屏幕上。
(5)QPaintDevice类是所有可以绘制的对象的基类。个绘制设备就是一个可以使用QPainter来绘制的二维空间的抽象。绘画的能力由子类QWidget、QPixmap、QPicture和QPrinter来实现。绘制设备的默认坐标系统的原点在左上角。X向右增加,Y向下增加。单位是一个像素。这里有几种方法在使用绘制工具时来设置用户自定义的坐标系统,
(6)Qt 绘图系统定义了两个绘制时使用的关键属性:画刷和画笔。(1)画刷使用QBrush描述,大多用于填充;(2)画笔使用QPen描述,大多用于绘制轮廓线。
(7)QBrush定义了QPainter的填充模式,具有样式、颜色、渐变以及纹理等属性。画刷的style()定义了填充的样式,使用Qt::BrushStyle枚举,默认值是Qt::NoBrush,也就是不进行任何填充。QPen定义了用于QPainter应该怎样画线或者轮廓线。画笔具有样式、宽度、画刷、笔帽样式和连接样式等属性。画笔的样式style()定义了线的样式。画刷brush()用于填充画笔所绘制的线条。
绘图的基本函数
speed.h:
#ifndef SPEED_H
#define SPEED_H
#include <QtWidgets/QWidget>
#include "ui_speed.h"
class Speed : public QWidget
{
Q_OBJECT
public:
Speed(QWidget *parent = 0);
~Speed();
protected:
void paintEvent(QPaintEvent *);
void drawCrown(QPainter *painter);
void drawBackground(QPainter *painter);
void drawScale(QPainter *painter);
void drawScaleNum(QPainter *painter);
void drawTitle(QPainter *painter);
void drawIndicator(QPainter *painter);
void drawNumericValue(QPainter *painter);
private:
QColor m_background;
QColor m_foreground;
int m_maxValue;
int m_minValue;
int m_startAngle;
int m_endAngle;
int m_scaleMajor;
int m_scaleMinor;
double m_value;
int m_precision;
QTimer *m_updateTimer;
QString m_units;
QString m_title;
public Q_SLOTS:
void UpdateAngle();
private:
Ui::SpeedClass ui;
};
#endif // SPEED_H
上面的代码是能够绘画出图片的重要函数-重绘函数。只要出现以下几种情况,系统就会自动调用paintEvent方法。
a)当窗口部件第一次显示时,系统会自动产生一个绘图事件
b)重新调整窗口部件大小
c)当窗口部件被其他部件遮挡,然后又再次显示出来时,就会对隐藏的区域产生一个重绘事件
translate(qreal dx, qreal dy);坐标变换可以看做是painter当前的一个状态,我们可以用save()方法把当前的状态存到一个堆栈里,在用过 之后,再用restore()恢复
注:
坐标系统在绘制的过程中是至关重要的,首先做好坐标变换,把窗体的中心设置成坐标原点,(默认的坐标原点的窗体的左上方(0,0)向右是x->增长,向下是y->增长。)
speed.cpp:
#include "speed.h"
#include "ui_speed.h"
Speed::Speed(QWidget *parent) :
QWidget(parent),
ui(new Ui::Speed)
{
ui->setupUi(this);
//0.构造函数的初始化
m_background = Qt::black;
m_foreground = Qt::green;
m_startAngle = 60;
m_endAngle = 60;
m_scaleMajor = 10;
m_minValue = 0;
m_maxValue = 100;
m_scaleMajor = 10;//分度
m_scaleMinor = 10;
m_units = "km/h";
m_title = "Speed Meter";
m_precision = 0;
m_value = 0;
m_updateTimer = new QTimer(this);
m_updateTimer->setInterval(50);//间隔,微妙微单位,大家可以改一下这个值看看转动速度。
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(UpdateAngle()));
m_updateTimer->start();//启动定时器
setWindowFlags(Qt::FramelessWindowHint);//无窗体
setAttribute(Qt::WA_TranslucentBackground);//背景透明
resize(400, 400);
}
void Speed:: UpdateAngle()
{
m_startAngle = 0;
}
Speed::~Speed()
{
delete ui;
}
//1.绘制表冠
void Speed::drawCrown(QPainter *painter) //绘制表冠
{
painter->save();
int radius = 100;
//首先就要绘制出两个个圆形,设置圆形的背景色,背景色采用渐变填充的方式,通过两个设置两个圆不同的填充颜色来形成一个色环,形成表冠。函数如下。使用的是线性渐变 QLinearGradient
QLinearGradient lg1(0, -radius, 0, radius);
//使用这个类实例化一个对象。对象的四个参数,分别是 两个点的坐标,表示渐变的方向。
//setColorAt() first parm:pos是一个[0,1]的闭区间的数字。设置渐变坐标所表示的距离。的比例大小,0.2就是长度的1/5
lg1.setColorAt(0, Qt::white); //设置渐变的颜色和路径比例
lg1.setColorAt(1, Qt::gray); //只是粗略的颜色,具体的可以参考RGB颜色查询对照表
painter->setBrush(lg1); // 创建QBrush对象,把这个渐变对象传递进去:
painter->setPen(Qt::NoPen); //边框线无色
painter->drawEllipse(-radius, -radius, radius << 1, radius << 1);
painter->setBrush(m_background = Qt::black);
painter->drawEllipse(-92, -92, 184, 184);
painter->restore();
}
//2.绘制刻度数字
//通过一套算法来实现绘制数字刻度
void Speed::drawScaleNum(QPainter *painter) //绘制刻度数字
{
painter->save();
painter->setPen(m_foreground);
//m_startAngle是起始角度,m_endAngle是结束角度,m_scaleMajor在一个量程中分成的刻度数
double startRad = ( 270-m_startAngle) * (3.14 / 180);
double deltaRad = (360 - m_startAngle - m_endAngle) * (3.14 / 180) / m_scaleMajor;
double sina,cosa;
int x, y;
QFontMetricsF fm(this->font());
double w, h, tmpVal;
QString str;
for (int i = 0; i <= m_scaleMajor; i++)
{
sina = sin(startRad - i * deltaRad);
cosa = cos(startRad - i * deltaRad);
tmpVal = 1.0 * i *((m_maxValue - m_minValue) / m_scaleMajor) + m_minValue;
// tmpVal = 50;
str = QString( "%1" ).arg(tmpVal); //%1作为占位符 arg()函数比起 sprintf()来是类型安全的
w = fm.size(Qt::TextSingleLine,str).width();
h = fm.size(Qt::TextSingleLine,str).height();
x = 82 * cosa - w / 2;
y = -82 * sina + h / 4;
painter->drawText(x, y, str); //函数的前两个参数是显示的坐标位置,后一个是显示的内容,是字符类型""
}
}
//3.绘制刻度线
//rotate(qreal a);//函数实现角度的旋转
void Speed::drawScale(QPainter *painter) //绘制刻度线
{
painter->save();
painter->rotate(m_startAngle);//rotate(qreal a);//函数实现角度的旋转
int steps = (m_scaleMajor * m_scaleMinor); //相乘后的值是分的份数
double angleStep = (360.0 - m_startAngle - m_endAngle) / steps; //每一个份数的角度
// painter->setPen(m_foreground); //m_foreground是颜色的设置
// QPen pen = painter->pen(); //第一种方法
QPen pen ;
pen.setColor(Qt::green); //推荐使用第二种方式
for (int i = 0; i <= steps; i++)
{
if (i % m_scaleMinor == 0)//整数刻度显示加粗
{
pen.setWidth(1); //设置线宽
painter->setPen(pen); //使用面向对象的思想,把画笔关联上画家。通过画家画出来
painter->drawLine(0, 62, 0, 72); //两个参数应该是两个坐标值
}
else
{
pen.setWidth(0);
painter->setPen(pen);
painter->drawLine(0, 67, 0, 72);
}
painter->rotate(angleStep);
}
}
//4.绘制标题
void Speed::drawTitle(QPainter *painter)
{
painter->save();
painter->setPen(m_foreground);
//painter->setBrush(m_foreground);
QString str(m_title); //显示仪表的功能
QFontMetricsF fm(this->font());
double w = fm.size(Qt::TextSingleLine,str).width();
painter->drawText(-w/2, -30, str);
painter->restore();
}
//5.显示的单位,与数值
void Speed::drawNumericValue(QPainter *painter)
{
QString str = QString("%1 %2").arg(m_value, 0, 'f', m_precision).arg(m_units);
QFontMetricsF fm(font());
double w = fm.size(Qt::TextSingleLine,str).width();
painter->setPen(m_foreground);
painter->drawText(-w / 2, 42, str);
}
//6.绘制表针,和中心点
void Speed::drawIndicator(QPainter *painter)
{
painter->save();
QPolygon pts;
pts.setPoints(3, -2, 0, 2, 0, 0, 60); /* (-2,0)/(2,0)/(0,60) *///第一个参数是 ,坐标的个数。后边的是坐标
painter->rotate(m_startAngle);
double degRotate = (360.0 - m_startAngle - m_endAngle) / (m_maxValue - m_minValue)*(m_value - m_minValue);
//画指针
painter->rotate(degRotate); //顺时针旋转坐标系统
QRadialGradient haloGradient(0, 0, 60, 0, 0); //辐射渐变
haloGradient.setColorAt(0, QColor(60, 60, 60));
haloGradient.setColorAt(1, QColor(160, 160, 160)); //灰
painter->setPen(Qt::white); //定义线条文本颜色 设置线条的颜色
painter->setBrush(haloGradient);//刷子定义形状如何填满 填充后的颜色
painter->drawConvexPolygon(pts); //这是个重载函数,绘制多边形。
painter->restore();
//画中心点
QColor niceBlue(150, 150, 200);
QConicalGradient coneGradient(0, 0, -90.0); //角度渐变
coneGradient.setColorAt(0.0, Qt::darkGray);
coneGradient.setColorAt(0.2, niceBlue);
coneGradient.setColorAt(0.5, Qt::white);
coneGradient.setColorAt(1.0, Qt::darkGray);
painter->setPen(Qt::NoPen); //没有线,填满没有边界
painter->setBrush(coneGradient);
painter->drawEllipse(-5, -5, 10, 10);
}
//7.重绘函数
void Speed ::paintEvent(QPaintEvent *)
{
QPainter painter(this);//一个类中的this表示一个指向该类自己的指针
painter.setRenderHint(QPainter::Antialiasing); /* 使用反锯齿(如果可用) */
painter.translate(width() / 2, height() / 2); /* 坐标变换为窗体中心 */
int side = qMin(width(), height());
painter.scale(side / 200.0, side / 200.0); /* 比例缩放 */
drawCrown(&painter); /* 画表盘边框 */
drawScaleNum(&painter); /* 画刻度数值值 */
drawScale(&painter); /* 画刻度线 */
drawTitle(&painter); /* 画单位 */
drawNumericValue(&painter); /* 画数字显示 */
drawIndicator(&painter); /* 画表针 */
}
效果图
表盘中还添加了定时器,来实现动态的效果
附录
附录1.指针旋转还可以使用QThransform类实现,细节如下
QTransform 是 Qt 框架中的一个类,主要用于进行二维图形的变换操作。它封装了矩阵的概念,使得程序员可以方便地进行平移、旋转、缩放等变换操作,而无需直接处理复杂的矩阵计算。
功能:QTransform 提供了对二维图形进行变换的功能,包括平移(translate())、旋转(rotate())、缩放(scale())等操作。
使用场景:QTransform 广泛应用于绘图、图形操作等场景,如实现图元的放大缩小、图形的不规则展示、重复图形但有规律的绘制等。
与 QPainter 的关系:虽然 QPainter 本身具有平移、缩放、剪切和旋转坐标系的功能,但如果需要执行多个转换操作,构建 QTransform 并调用 QPainter::setTransform() 会更加高效。
坐标系统:Qt 的坐标系统默认以左上角为原点,x 轴向右增加,y 轴向下增加。QTransform 允许程序员在屏幕上建立自己的坐标系用于绘制。
创建 QTransform 对象:可以使用默认构造函数创建一个空的 QTransform 对象,也可以通过传递参数来创建具有初始变换参数的对象。
注意事项:虽然 QTransform 只支持 2D 场景下的使用,但为了更好地理解其背后的矩阵概念,有时可以使用 3D 坐标作为示例(在 2D 场景下,将 z 值视为 0)。
以下是一个简单的示例代码,首先创建QTransform 对象 transform,并使用 rotate() 方法将其旋转 45 度。然后,创建一个 QPainter 对象 painter,并使用 setTransform() 方法将 transform 设置为 painter 的变换。最后调用 drawRect() 方法绘制一个矩形,它将根据之前设置的 transform 进行旋转。:
#include <QTransform>
// ...
QTransform transform;
transform.rotate(45); // 旋转 45 度
QPainter painter(this);
painter.setTransform(transform); // 设置变换
painter.drawRect(0, 0, 100, 100); // 绘制一个矩形,它将根据 transform 进行旋转
附录2:QPainter的rotate方法与QTransform的区别
QPainter的rotate方法用于在绘图过程中将坐标系统绕坐标原点顺时针旋转一定的角度。这个方法是在QPainter的绘图上下文中进行的,直接作用于绘图操作。当使用rotate方法时,后续的绘图操作都会基于旋转后的坐标系统进行。
而QTransform是一个用于指定坐标系的2D转换的类,它包括了平移、缩放、扭曲(剪切)、旋转或投影等多种变换操作。**QTransform并不直接进行绘图,而是提供了一种方式来描述坐标系的变换。**在实际使用中,可以通过QPainter的setTransform方法来设置QTransform对象,从而实现图形的旋转、平移等变换。
因此,虽然QPainter的rotate方法和QTransform都可以实现图形的旋转操作,但它们的使用方式和上下文有所不同。rotate方法更直接地影响绘图操作,而QTransform则提供了一种更灵活的方式来描述坐标系的变换。
附录3:QBrush()的用法
QBrush在Qt框架中主要用于设置绘图时的填充样式和颜色。以下是关于QBrush的一些常见用法:
创建和设置QBrush对象:
使用预定义的样式:例如,你可以创建一个纯色填充样式的QBrush,并设置其颜色。
QBrush brush(Qt::SolidPattern); // 创建一个纯色填充样式的QBrush
brush.setColor(Qt::red); // 设置填充颜色为红色
使用纹理:你可以使用QPixmap对象作为纹理来填充形状。
QBrush brush(QPixmap("texture.png")); // 使用纹理填充
使用渐变:QBrush还支持渐变填充,你可以创建线性渐变、径向渐变或圆锥渐变等。
QLinearGradient gradient(0, 0, 100, 100);
gradient.setColorAt(0, Qt::red);
gradient.setColorAt(1, Qt::blue);
QBrush brush(gradient); // 使用渐变填充
设置填充样式:
QBrush类支持多种填充样式,包括纯色填充(Qt::SolidPattern)、线性渐变填充(Qt::LinearGradientPattern)、径向渐变填充(Qt::RadialGradientPattern)、圆锥渐变填充(Qt::ConicalGradientPattern)和图案填充(Qt::TexturePattern)。你可以使用setStyle()函数来设置填充样式。
获取和设置属性:
color()函数用于获取当前设置的填充颜色。
setColor()函数用于设置填充颜色。
gradient()函数用于获取当前设置的渐变(如果样式为渐变填充)。
setGradient()函数用于设置渐变。
texture()函数用于获取当前设置的纹理(如果样式为图案填充)。
setTexture()函数用于设置纹理。
使用QBrush进行绘图:
一旦创建了QBrush对象并设置了其样式和颜色等属性,就可以在QPainter对象中使用它来绘制填充的形状。例如可以使用QPainter::setBrush()方法来设置绘图时使用的QBrush对象,然后使用QPainter::drawRect()、QPainter::drawEllipse()等方法来绘制形状。