首页 Qt 学习之路 2 Qt 学习之路 2(25):画刷和画笔

Qt 学习之路 2(25):画刷和画笔

23 3.2K

前面一章我们提到,Qt 绘图系统定义了两个绘制时使用的关键属性:画刷和画笔。前者使用QBrush描述,大多用于填充;后者使用QPen描述,大多用于绘制轮廓线。

QBrush定义了QPainter的填充模式,具有样式、颜色、渐变以及纹理等属性。

画刷的style()定义了填充的样式,使用Qt::BrushStyle枚举,默认值是Qt::NoBrush,也就是不进行任何填充。我们可以从下面的图示中看到各种填充样式的区别:

画刷填充模式

画刷的color()定义了填充模式的颜色。这个颜色可以是 Qt 预定义的颜色常量,也就是Qt::GlobalColor,也可以是任意QColor对象。

画刷的gradient()定义了渐变填充。这个属性只有在样式是Qt::LinearGradientPatternQt::RadialGradientPattern或者Qt::ConicalGradientPattern之一时才有效。渐变可以由QGradient对象表示。Qt 提供了三种渐变:QLinearGradientQConicalGradientQRadialGradient,它们都是QGradient的子类。我们可以使用如下代码片段来定义一个渐变的画刷:

QRadialGradient gradient(50, 50, 50, 50, 50);
gradient.setColorAt(0, QColor::fromRgbF(0, 1, 0, 1));
gradient.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0));

QBrush brush(gradient);

当画刷样式是 Qt::TexturePattern时,texture()定义了用于填充的纹理。注意,即使你没有设置样式为Qt::TexturePattern,当你调用setTexture()函数时,QBrush会自动将style()设置为Qt::TexturePattern

QPen定义了用于QPainter应该怎样画线或者轮廓线。画笔具有样式、宽度、画刷、笔帽样式和连接样式等属性。画笔的样式style()定义了线的样式。画刷brush()用于填充画笔所绘制的线条。笔帽样式capStyle()定义了使用QPainter绘制的线的末端;连接样式joinStyle()则定义了两条线如何连接起来。画笔宽度width()widthF()定义了画笔的宽。注意,不存在宽度为 0 的线。假设你设置 width 为 0,QPainter依然会绘制出一条线,而这个线的宽度为 1 像素。也就是说,画笔宽度通常至少是 1 像素。

这么多参数既可以在构造时指定,也可以使用 set 函数指定,完全取决于你的习惯,例如:

QPainter painter(this);
QPen pen(Qt::green, 3, Qt::DashDotLine, Qt::RoundCap, Qt::RoundJoin);
painter.setPen(pen);

等价于

QPainter painter(this);
QPen pen;  // creates a default pen

pen.setStyle(Qt::DashDotLine);
pen.setWidth(3);
pen.setBrush(Qt::green);
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::RoundJoin);

painter.setPen(pen);

使用构造函数的优点是代码较短,但是参数含义不明确;使用 set 函数则正好反过来。

默认的画笔属性是纯黑色,0 像素,方形笔帽(Qt::SquareCap),斜面型连接(Qt::BevelJoin)。

下面是画笔样式的示例:

画笔样式 Pen Style

你也可以使用setDashPattern()函数自定义样式,例如如下代码片段:

 QPen pen;
 QVector<qreal> dashes;
 qreal space = 4;

 dashes << 1 << space << 3 << space << 9 << space
        << 27 << space << 9 << space;

 pen.setDashPattern(dashes);

笔帽定义了画笔末端的样式,例如:

笔帽样式 Cap Style

他们之间的区别是,Qt::SquareCap是一种包含了最后一个点的方形端点,使用半个线宽覆盖;Qt::FlatCap不包含最后一个点;Qt::RoundCap是包含最后一个点的圆形端点。具体可以参考下面的示例(出自《C++ GUI Programming with Qt 4, 2nd Edition》):

笔帽样式细节

连接样式定义了两条线连接时的样式,例如:

连接样式

同样,可以参考下面图示来理解这几种连接样式的细节(出自《C++ GUI Programming with Qt 4, 2nd Edition》):

连接样式细节

注意,我们前面说了,QPainter也是一个状态机,这里我们所说的这些属性都是处于这个状态机之中的,因此,我们应该记得是否要将其保存下来或者是重新构建。

23 评论

39919 2012年11月5日 - 17:27

学习了, 我正在想着弄个平铺背景呢

回复
羊八井 2012年11月20日 - 10:24

QVariant(Qt::GlobalColor) Q_DECL_EQ_DELETE;
QVariant(Qt::BrushStyle) Q_DECL_EQ_DELETE;
QVariant(Qt::PenStyle) Q_DECL_EQ_DELETE;
QVariant(Qt::CursorShape) Q_DECL_EQ_DELETE;

看Qt5,把这几个还有其它的QVariant自动转换给去掉了。而从http://qt-project.org/doc/qt-5.0/painting-basicdrawing.html这里的例子看都是手动static_cast的,不知为什么要这么做?

回复
DevBean 2012年11月20日 - 14:45

按照代码注释:These constructors don't create QVariants of the type associcated with the enum, as expected, but they would create a QVariant of type int with the value of the enum value. Use QVariant v = QColor(Qt::red) instead of QVariant v = Qt::red for example. 我的理解是,如果不禁用这几个构造函数,QVariant 会将这些枚举当做普通的 int,而实际这些枚举是有自己的含义的,例如,Qt::GlobalColor 其实代表一个 QColor 对象。这样的话,可能会有隐藏的 bug,因为 Qt::GlobalColor 会退化为一个普通的 int。所以,Qt 5 将这些有特殊含义的枚举的构造函数禁止掉了。

回复
羊八井 2012年11月20日 - 15:12

http://qt-project.org/doc/qt-5.0/painting-basicdrawing.html

但是像这个例子,把它手动转换为static_cast了。那不是在QVariant里还是存为int。取出来时还是手动static_cast才行。

回复
DevBean 2012年11月20日 - 19:49

这个例子里面,之所以转换成 int,是因为它仅是作为 QComboBox 一个项的 value 使用,没必要存储一个完整的 QBrush 对象而占用内存空间。当然,你也可以当做 QBrush 存储,这只是一个例子而已,你可以选择自己的实现。

回复
一梦千寻 2014年2月18日 - 21:55

Errata:
setDashPattern():===>
QPen pen;
QVector dashes;//
qreal space = 4;
dashes << 1 << space << 3 << space << 9 << space
<< 27 << space << 9 << space;
pen.setDashPattern(dashes);

回复
Const_Lin 2014年3月8日 - 16:41

我也是这里编译不通过

回复
Const_Lin 2014年3月8日 - 16:42

错误提示:
C:\Users\linxiaotao1993\Desktop\Qt\untitled\mainwindow.cpp:23: 错误:missing template arguments before 'dashes'
QVector dashes;
^

回复
Const_Lin 2014年3月8日 - 16:52

查了一下
把QVector dashes; 改成 QVector dashes;
就可以了

回复
豆子 2014年3月10日 - 10:50

页面将模板参数过滤掉了,已经修改过

回复
zhangxian 2014年8月24日 - 21:24

我想制作一个时间轴,就是一个以时间为刻度的轴,把相关项目按照时间刻度排放在轴的两边,并且可以通过拖动改变其时间,请问使用什么方法比较好?在网上看了一些说使用qwt比较方便,但是不是很会,请问您有何高见

回复
豆子 2014年8月26日 - 09:14

QWT 应该是不错的选择,既然你要绘制坐标轴,很有可能还有其它的图标,比如线图、饼图之类,QWT 就是这类图库。如果你要全部自己绘制,需要计算一些数据。所以如果项目的确是这种统计图形,为开发方便起见,最好考虑研究一下 QWT。如果 QWT 还是无法满足要求,再考虑自己实现吧。

回复
Ycoronene 2015年9月2日 - 21:48

豆子老师,还想再问你一个问题,就是:
QBrush brush(gradient);
/*。。。*/
painter.setBrush(brush);
//为什么文档里写这里需要一个QBrush*类型的参数,可是直接用brush就可以呢?

回复
豆子 2015年9月4日 - 12:56

QPainter 的 setBrush() 函数接受一个 QBrush 类型,可能是你看错参数类型了吧?

回复
石磊 2016年5月14日 - 21:07

豆子大大,我想问下现在我需要在QtextEdit下画点,这个文本文档是在主窗口下的一个控件,利用QPaint能不能实现?请教下,谢谢!

回复
Robin Lin 2017年7月6日 - 10:37

这段代码什么意思
dashes << 1 << space << 3 << space << 9 << space
<< 27 << space << 9 << space;
请教

回复
豆子 2017年7月8日 - 10:57

setDashPattern() 函数需要一个包含偶数个元素的列表,这段代码就是为了构建这个列表。具体来说,这个数组包含偶数个元素,其中,奇数位置的元素代表线段长度;偶数位置的元素代表间隔长度。可以查看 setDashPattern() 函数的文档。

回复
Ace_D_Shu 2018年8月29日 - 16:20

怎么实现画笔的绘制功能,就是点着鼠标左键不松,移动鼠标绘制图形,松掉鼠标左键,结束绘制。

回复
豆子 2018年8月29日 - 21:56

这种做法一般是在 mousePressEvent() 事件中记录下起始坐标,然后在 mouseMoveEvent() 中实时修改终点坐标,不停绘制图形,然后在 mouseReleaseEvent() 中获得最终坐标,然后绘制最终图形

回复
lanyan 2020年6月17日 - 12:49

大大我想问一下为什么我的QPainter painter(this);以及QPen pen中的painter和pen的地方会报错啊?

回复
豆子 2020年7月27日 - 22:15

是报什么错误呢?

回复
David Z 2022年2月8日 - 10:17

请问大神,painter.setBrush(Qt::red),参数传入的是颜色,而该函数的形参类型应该是Qt::BrushStyle,为什么可以这样用呢?我看Qt原码里也没有对参数进行判断,是发生了什么隐式转换吗?麻烦您啦,谢谢您。

回复
豆子 2022年6月4日 - 22:03

setBrush()有两个重载,参数类型分别是QBrushQt::BrushStyle;而QBrush有很多构造函数,其中就有QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style = Qt::SolidPattern)。当传入Qt::GlobalColor(即文中的Qt::red)时,C++ 会尝试使用这个参数去创建对应的QBrush实例(如果创建失败就会报错),这样就可以满足setBrush()了。

回复

回复 Const_Lin 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2