QT5开发及实例学习之十六Qt5基础图形的绘制

您所在的位置:网站首页 基础图形绘画 QT5开发及实例学习之十六Qt5基础图形的绘制

QT5开发及实例学习之十六Qt5基础图形的绘制

2024-07-12 05:48| 来源: 网络整理| 查看: 265

文章目录 前言一、绘图框架设计二、绘图区的实现三、主窗口的实现

前言

  设计界面,区分各种形状及画笔颜色、画笔线宽、画笔风格、画笔顶帽、画笔连接点、填充模式、铺展效果、画刷颜色、画刷风格设置等。

一、绘图框架设计

  绘制各种图形实例的框架: 在这里插入图片描述   首先在 PaintArea 类中完成各种图形显示功能的 Widget,重绘 paintEvent() 函数。然后在主窗口 MainWidget 类中完成各种图形参数的选择。

二、绘图区的实现

  PaintArea 类继承自 QWidget 类,在类声明中,首先声明一个枚举型数据 Shape,列举了所有实例可能用到的图形形状;其次声明 setShape() 函数用于设置形状,setPen() 函数用于设置画笔,setBrush() 函数用于设置画刷,setFillRule() 函数用于设置填充模式,以及重绘事件 paintEvent() 函数;最后声明表示各种属性的私有变量。   PaintArea 类的构造函数用于完成初始化工作,设置图形显示区域的背景色及最小显示尺寸,具体代码如下:

PaintArea::PaintArea(QWidget *parent) : QWidget(parent) { setPalette(QPalette(Qt::white)); setAutoFillBackground(true); setMinimumSize(400, 400); }

  PaintArea 类的重画函数代码如下:

void PaintArea::paintEvent(QPaintEvent *) { QPainter p(this); //新建一个 QPainter 对象 p.setPen(pen); //设置 QPainter 对象的画笔 p.setBrush(brush); //设置 QPainter 对象的画刷 QRect rect(50, 100, 300, 200); //设定一个方形区域,为画长方形、圆角方形、椭圆形等做准备 static const QPoint points[4] = //创建一个 QPoint 的数组,包含四个点,为画多边形、多边线及点做准备 { QPoint(150, 100), QPoint(300, 150), QPoint(350, 250), QPoint(100, 300) }; int startAngle = 30*16; //(c) int spanAngle = 120*16; QPainterPath path; //新建一个 QPainterPath 对象为画路径做准备 path.addRect(150, 150, 100, 100); path.moveTo(100, 100); path.cubicTo(300, 100, 200, 200, 300, 300); path.cubicTo(100, 300, 200, 200, 100, 100); path.setFillRule(fillRule); switch(shape) //(d) { case Line: //直线 p.drawLine(rect.topLeft(), rect.bottomRight()); break; case Rectangle: //长方形 p.drawRect(rect); break; case RoundRect: //圆角方形 p.drawRoundRect(rect); break; case Ellipse: //椭圆形 p.drawEllipse(rect); break; case Polygon: //多边形 p.drawPolygon(points, 4); break; case Polyline: //多边线 p.drawPolyline(points, 4); break; case Points: //点 p.drawPoints(points, 4); break; case Arc: //弧 p.drawArc(rect, startAngle, spanAngle); break; case Path: //路径 p.drawPath(path); break; case Text: //文字 p.drawText(rect, Qt::AlignCenter, QObject::tr("Hello Qt!")); break; case Pixmap: //图片 p.drawPixmap(150, 150, QPixmap("butterfly.png")); break; } }

(c)其中,参数 startAngle 表示起始角,为弧形的起始点与圆心之间连线与水平方向的夹角;参数 spanAngle 表示的是跨度角,为弧形起点、终点分别与圆心连线之间的夹角。在这里插入图片描述   注意:用 QPainter 画弧形所使用的角度值,是以 1/16° 为单位的,在画弧时即 1° 用 16 表示。 (d)switch(shape){…}:使用一个 switch() 语句,对所要画的形状做判断,调用 QPainter 的各个 draw() 函数完成图形的绘制。  (1)利用 QPainter 绘制图形 (Shape) 。   Qt为开发者提供了丰富的绘制基本图形的 draw() 函数,如下所示: 在这里插入图片描述   除此之外,QPainter 类还提供了一个 drawPixmap() 函数,可以直接将图片画到刻画控件上。  (2)利用 QPainterPath 绘制简单图形。   利用 QPainterPath 绘制简单图形,QPainterPath 类为 QPainter 类提供了一个存储容器,里面包含了所要绘制的内容的集合及绘制的顺序,如长方形、多边形、曲线等各种任意图形。当需要绘制此预先存储在 QPainterPath 对象中的内容时,只需调用 QPainter 类的 drawPath() 函数即可。   QPainterPath 类提供了许多函数接口,可以很方便地加入一些规则图形。例如,addRect() 函数加入一个方形,addEllipse() 函数加入一个椭圆形,addText() 函数加入一个字符串,addPolygon() 函数加入一个多边形等。同时,QPainterPath 类还提供了 addPath() 函数,用于加入另一个 QPainterPath 对象中保存的内容。   QPainterPath 对象的当前点自动处在上一部分图形内容的结束点上,若下一部分图形的起点不在此结束点,则需调用 moveTo() 函数将当前点移动到下一部分图形的起点。

  cubicTo() 函数绘制的是贝塞尔曲线,如下所示。在这里插入图片描述   需要三个参数,分别表示三个点 cubicTo(c1, c2, endPoint);c1,c2 为贝塞尔曲线的控制点。

  利用 QPainterPath 类可以实现 QPainter 类的 draw() 函数能够实现的所有图形。   例如,对于 QPainter::drawRect() 函数,除可用上面介绍的 QPainterPath::addRect() 的方式实现外,还可以用如下方式实现:

QPainterPath path; path.moveTo(0, 0); path.lineTo(200, 0); path.lineTo(200, 100); path.lineTo(0, 100); path.lineTo(0, 0);

  这是一个更通用的方法,其他(如多边形等)图形都能够使用这种方式实现。

三、主窗口的实现

  主窗口类 MainWidget 继承自 QWidget 类,包含完成各种图形参数选择的控制区的声明、一系列设置与画图相关参数的槽函数的声明,以及一个绘图区 PaintArea 对象的声明。   MainWidget 类得构造函数中创建了各参数选择控件,代码如下:

MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { paintArea = new PaintArea(); shapeLabel = new QLabel(tr("形状:")); //形状选择下拉列表框 shapeComboBox = new QComboBox(); shapeComboBox->addItem(tr("Line"), PaintArea::Line); //(a) shapeComboBox->addItem(tr("Rectangle"), PaintArea::Rectangle); shapeComboBox->addItem(tr("RoundedRect"), PaintArea::RoundRect); shapeComboBox->addItem(tr("Ellipse"), PaintArea::Ellipse); shapeComboBox->addItem(tr("Polygon"), PaintArea::Polygon); shapeComboBox->addItem(tr("Polyline"), PaintArea::Polyline); shapeComboBox->addItem(tr("Points"), PaintArea::Points); shapeComboBox->addItem(tr("Arc"), PaintArea::Arc); shapeComboBox->addItem(tr("Path"), PaintArea::Path); shapeComboBox->addItem(tr("Text"), PaintArea::Text); shapeComboBox->addItem(tr("Pixmap"), PaintArea::Text); connect(shapeComboBox, SIGNAL(activated(int)), this, SLOT(showShape(int))); penColorLabel = new QLabel(tr("画笔颜色:")); //画笔颜色选择控件 penColorFrame = new QFrame(); penColorFrame->setFrameStyle(QFrame::Panel|QFrame::Sunken); penColorFrame->setAutoFillBackground(true); penColorFrame->setPalette(QPalette(Qt::blue)); penColorBtn = new QPushButton(tr("更改")); connect(penColorBtn, SIGNAL(clicked()), this, SLOT(showPenColor())); penWidthLabel = new QLabel(tr("画笔线宽:")); //画笔线宽选择控件 penWidthSpinBox = new QSpinBox(); penWidthSpinBox->setRange(0, 20); connect(penWidthSpinBox, SIGNAL(valueChanged(int)), this, SLOT(showPenWidth(int))); penStyleLabel = new QLabel(tr("画笔风格:")); //画笔风格选择下拉列表框 penStyleComboBox = new QComboBox(); penStyleComboBox->addItem(tr("SolidLine"), static_cast(Qt::SolidLine)); //(b) penStyleComboBox->addItem(tr("DashLine"), static_cast(Qt::DashLine)); penStyleComboBox->addItem(tr("DotLine"), static_cast(Qt::DotLine)); penStyleComboBox->addItem(tr("DashDotLine"), static_cast(Qt::DashDotLine)); penStyleComboBox->addItem(tr("DashDotDotLine"), static_cast(Qt::DashDotDotLine)); penStyleComboBox->addItem(tr("CustomDashLine"), static_cast(Qt::CustomDashLine)); connect(penStyleComboBox, SIGNAL(activated(int)), this, SLOT(showPenStyle(int))); penCapLabel = new QLabel(tr("画笔顶帽:")); //画笔顶帽风格选择下拉列表框 penCapComboBox = new QComboBox(); penCapComboBox->addItem(tr("SquareCap"), Qt::SquareCap); //(c) penCapComboBox->addItem(tr("FlatCap"), Qt::FlatCap); penCapComboBox->addItem(tr("RoundCap"), Qt::RoundCap); connect(penCapComboBox, SIGNAL(activated(int)), this, SLOT(showPenCap(int))); penJoinLabel = new QLabel(tr("画笔连接点:")); //画笔连接点风格选择下拉列表框 penJoinComboBox = new QComboBox(); penJoinComboBox->addItem(tr("BevelJoin"), Qt::BevelJoin); //(d) penJoinComboBox->addItem(tr("MiterJoin"), Qt::MiterJoin); penJoinComboBox->addItem(tr("RoundJoin"), Qt::RoundJoin); connect(penJoinComboBox, SIGNAL(activated(int)), this, SLOT(showPenJoin(int))); fillRuleLabel = new QLabel(tr("填充模式:")); //填充模式选择下拉列表框 fillRuleComboBox = new QComboBox(); fillRuleComboBox->addItem(tr("Odd Even"), Qt::OddEvenFill); //(e) fillRuleComboBox->addItem(tr("Winding"), Qt::WindingFill); connect(fillRuleComboBox, SIGNAL(activated(int)), this, SLOT(showFillRule())); spreadLabel = new QLabel(tr("铺展效果:")); //铺展效果选择下拉列表框 spreadComboBox = new QComboBox(); spreadComboBox->addItem(tr("PadSpread"), QGradient::PadSpread); //(f) spreadComboBox->addItem(tr("RepeatSpread"), QGradient::RepeatSpread); spreadComboBox->addItem(tr("ReflectSpread"), QGradient::ReflectSpread); connect(spreadComboBox, SIGNAL(activated(int)), this, SLOT(showSpreadStyle())); brushColorLabel = new QLabel(tr("画刷颜色:")); //画刷颜色选择控件 brushColorFrame = new QFrame(); brushColorFrame->setFrameStyle(QFrame::Panel|QFrame::Sunken); brushColorFrame->setAutoFillBackground(true); brushColorFrame->setPalette(QPalette(Qt::green)); brushColorBtn = new QPushButton(tr("更改")); connect(brushColorBtn, SIGNAL(clicked()), this, SLOT(showBrushColor())); brushStyleLabel = new QLabel(tr("画刷风格:")); //画刷风格选择下拉列表框 brushStyleComboBox = new QComboBox(); brushStyleComboBox->addItem(tr("SolidPattern"), static_cast(Qt::SolidPattern)); //(g) brushStyleComboBox->addItem(tr("Densel1Pattern"), static_cast(Qt::Dense1Pattern)); brushStyleComboBox->addItem(tr("Densel2Pattern"), static_cast(Qt::Dense2Pattern)); brushStyleComboBox->addItem(tr("Densel3Pattern"), static_cast(Qt::Dense3Pattern)); brushStyleComboBox->addItem(tr("Densel4Pattern"), static_cast(Qt::Dense4Pattern)); brushStyleComboBox->addItem(tr("Densel5Pattern"), static_cast(Qt::Dense5Pattern)); brushStyleComboBox->addItem(tr("Densel6Pattern"), static_cast(Qt::Dense6Pattern)); brushStyleComboBox->addItem(tr("Densel7Pattern"), static_cast(Qt::Dense7Pattern)); brushStyleComboBox->addItem(tr("HorPattern"), static_cast(Qt::HorPattern)); brushStyleComboBox->addItem(tr("VerPattern"), static_cast(Qt::VerPattern)); brushStyleComboBox->addItem(tr("CrossPattern"), static_cast(Qt::CrossPattern)); brushStyleComboBox->addItem(tr("BDiagPattern"), static_cast(Qt::BDiagPattern)); brushStyleComboBox->addItem(tr("FDiagPattern"), static_cast(Qt::FDiagPattern)); brushStyleComboBox->addItem(tr("DiagCrossPattern"), static_cast(Qt::DiagCrossPattern)); brushStyleComboBox->addItem(tr("LinearGradientPattern"), static_cast(Qt::LinearGradientPattern)); brushStyleComboBox->addItem(tr("ConicalGradientPattern"), static_cast(Qt::ConicalGradientPattern)); brushStyleComboBox->addItem(tr("RadialGradientPattern"), static_cast(Qt::RadialGradientPattern)); brushStyleComboBox->addItem(tr("TexturePattern"), static_cast(Qt::TexturePattern)); connect(brushStyleComboBox, SIGNAL(activated(int)), this, SLOT(showBrush(int))); rightLayout = new QGridLayout(); //控制面板布局 rightLayout->addWidget(shapeLabel, 0, 0); rightLayout->addWidget(shapeComboBox, 0, 1); rightLayout->addWidget(penColorLabel, 1, 0); rightLayout->addWidget(penColorFrame, 1, 1); rightLayout->addWidget(penColorBtn, 1, 2); rightLayout->addWidget(penWidthLabel, 2, 0); rightLayout->addWidget(penWidthSpinBox, 2, 1); rightLayout->addWidget(penStyleLabel, 3, 0); rightLayout->addWidget(penStyleComboBox, 3, 1); rightLayout->addWidget(penCapLabel, 4, 0); rightLayout->addWidget(penCapComboBox, 4, 1); rightLayout->addWidget(penJoinLabel, 5, 0); rightLayout->addWidget(penJoinComboBox, 5, 1); rightLayout->addWidget(fillRuleLabel, 6, 0); rightLayout->addWidget(fillRuleComboBox, 6, 1); rightLayout->addWidget(spreadLabel, 7, 0); rightLayout->addWidget(spreadComboBox, 7, 1); rightLayout->addWidget(brushColorLabel, 8, 0); rightLayout->addWidget(brushColorFrame, 8, 1); rightLayout->addWidget(brushColorBtn, 8, 2); rightLayout->addWidget(brushStyleLabel, 9, 0); rightLayout->addWidget(brushStyleComboBox, 9, 1); QHBoxLayout *mainLayout = new QHBoxLayout(this); //整体布局 mainLayout->addWidget(paintArea); mainLayout->addLayout(rightLayout); mainLayout->setStretchFactor(paintArea, 1); mainLayout->setStretchFactor(rightLayout, 0); showShape(shapeComboBox->currentIndex()); //显示默认的图形 }

(a)QComboBox 的 additem() 函数可以仅插入文本,也可以同时插入与文本相对应的具体数据,通常为枚举类型数据,便于后面操作时确定选择的是哪个数据。 (b)选用不同的参数,对应画笔的不同风格。 (c)选用不同的参数,对应画笔顶帽的不同风格。其中,Qt::SquareCap 表示在线条的顶点处是方形的,且线条绘制的区域包括了端点,并且再往外延伸半个线宽的长度;Qt::FlatCap 表示在线条的顶点处是方形的,但线条绘制区域不包括端点在内;Qt::RoundCap 表示在线条的顶点处是圆形的,且线条绘制区域包含了端点。 (d)选用不同的参数,对应画笔连接点的不同风格。其中,Qt::BevelJoin 风格连接点是指两条线的中心线顶点相汇,相连处依然保留线条各自的方形顶端;Qt::MiterJoin 风格连接点是指两条线的中心线顶点相汇,相连处线条延长到线的外侧汇集至点,形成一个尖顶的连接;Qt::RoundJoin 风格连接点是指两条线的中心线顶点相汇,相连处以圆弧形连接。 (e)Qt 为 QPainterPath 类提供了两种填充规则,分别是 Qt::OddEvenFill 和 Qt::WindingFill。这两种填充规则在判定图形中某一点是处于内部还是外部时,判断依据不同。   其中,Qt::OddEvenFill 填充规则判断的依据是从图形中某一点画一条水平线到图形外。若这条水平线与图形边线的交点数目为奇数,则说明此点位于图形的内部;若交点数目为偶数,则此点位于图形的外部。   而 Qt::WindingFill 填充规则的判断依据则是从图形中某一点画一条水平线到图形外,每个交点外边线的方向可能向上,也可能向下,将这些交点数累加,方向相反的相互抵消,若最后结果不为 0 则说明此点在图形内,若最后结果为 0 则说明在图形外。   其中,边线的方向是由 QPainterPath 创建时根据描述的顺序决定的。如果采用 addRect() 或 addPolygon() 等函数加入的图形,默认是按顺时针方向。 (f)铺展效果有三种,分别为 QGradient::PadSpread、QGradient::RepeatSpread 和 QGradient::ReflectSpread。其中,PadSpread 是默认的铺展效果,也是最常见的铺展效果,没有被渐变覆盖的区域填充单一的起始颜色或终止颜色;RepeatSpread 效果与 ReflectSpread 效果只对线性渐变和圆形渐变起作用。   使用 QGradient 的 setColorAt() 函数设置起止的颜色,其中,第 1 个参数表示所设颜色点的位置,取值范围为 0.0~1.0,0.0 表示起点,1.0 表示终点;第 2 个参数表示该点的颜色值。除可设置起点和终点的颜色外,如有需要还可设置中间任意位置的颜色,例如:setColorAt(0.3, Qt::white),设置起、终点之间 1/3 位置的颜色为白色。 (g)选用不同的参数,对应画刷的不同风格。

  showShape() 槽函数,根据当前下拉列表框中选择的选项,调用 PaintArea 类的 setShape() 函数设置 PaintArea 对象的形状参数,代码如下:

void MainWidget::showShape(int value) { PaintArea::Shape shape = PaintArea::Shape(shapeComboBox->itemData(value, Qt::UserRole).toInt()); paintArea->setShape(shape); }

  其中,QComboBox 类的 itemData 方法返回当前显示的下拉列表框数据,是一个 QVariant 对象,此对象与控件初始化时插入的枚举型数据相关,调用 QVariant 类的 toInt() 函数获得此数据在枚举型数据集合中的序号。

  showBrush() 槽函数的具体实现代码如下:

void MainWidget::showBrush(int value) { //获得画刷的颜色 QColor color = brushColorFrame->palette().color(QPalette::Window); Qt::BrushStyle style = Qt::BrushStyle(brushStyleComboBox->itemData(value, Qt::UserRole).toInt()); //(a) if(style == Qt::LinearGradientPattern) //(b) { QLinearGradient linearGradient(0, 0, 400, 400); linearGradient.setColorAt(0.0, Qt::white); linearGradient.setColorAt(0.2, color); linearGradient.setColorAt(1.0, Qt::black); linearGradient.setSpread(spread); paintArea->setBrush(linearGradient); } else if(style == Qt::RadialGradientPattern) //(c) { QRadialGradient radialGradient(200, 200, 150, 150, 100); radialGradient.setColorAt(0.0, Qt::white); radialGradient.setColorAt(0.2, color); radialGradient.setColorAt(1.0, Qt::black); radialGradient.setSpread(spread); paintArea->setBrush(radialGradient); } else if(style == Qt::ConicalGradientPattern) //(d) { QConicalGradient conicalGradient(200, 200, 30); conicalGradient.setColorAt(0.0, Qt::white); conicalGradient.setColorAt(0.2, color); conicalGradient.setColorAt(1.0, Qt::black); paintArea->setBrush(conicalGradient); } else if(style == Qt::TexturePattern) { paintArea->setBrush(QBrush(QPixmap("image.png"))); } else { paintArea->setBrush(QBrush(color, style)); } }

(a)获得所选的画刷风格,若选择的是渐变或者纹理图案,则需要进行一定的处理。 (b)主窗口的 style 变量值为 Qt::LinearGradientPattern 时,表明选择的是线形渐变。   QLinearGradient linearGradient(startPoint, endPoint) 创建线形渐变类对象需要两个参数,分别表示起止点位置。 (c)主窗口的 style 变量值为 Qt::RadialGradientPattern 时,表明选择的是圆形渐变。   QRadialGradiend radialGradient(startPoint, r, endPoint) 创建圆形渐变类对象需要三个参数,分别表示圆心位置、半径值和焦点位置。表示以 startPoint 作为圆心和焦点的位置,以 startPoint 和 endPoint 之间的距离 r 为半径,当然圆心和焦点的位置也可以不重合。 (d)主窗口的 style 变量值为 Qt::ConicalGradientPattern 时,表明选择的是锥形渐变。   QConicalGradient conicalGradient(startPoint, -(180*angle)/PI) 创建锥形渐变类对象需要两个参数,分别是锥形的顶点位置和渐变分界线与水平方向的夹角。锥形渐变不需要设置铺展效果,它的铺展效果只能是 QGradient::PadSpread。   注意:锥形渐变的方向默认是逆时针方向。

  Qt 画图的坐标系默认以左上角为原点,X 轴向右,Y 轴向下。此坐标系可受 QPainter 类控制,对其进行变形,QPainter 类提供了相应的变形函数,包括旋转、缩放、平移和切变。调用这些函数时,显示设备的坐标系会发生相应变形,但绘制内容相对坐标系的位置并不会发生改变,因此看起来像是对绘制内容进行了变形,而实质上是坐标系的变形。若还需要更加复杂的变形,则可以采用 QMatrix 类实现。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3