Qt5 C++14教程----绘画

在Qt5 C++编程教程的这一部分,我们将做一些绘画。

当我们在Qt5中进行绘画时,QPainter类起着重要作用。绘画是通过QPainter类对paintEvent方法的反应来完成的。

Lines

在第一个示例中,我们将在窗口的客户区绘制一些线条。

lines.h
#pragma once

#include <QWidget>

class Lines : public QWidget {

  public:
    Lines(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *event);
    void drawLines(QPainter *qp);
};


lines.cpp
#include <QPainter>
#include "lines.h"

Lines::Lines(QWidget *parent)
    : QWidget(parent)
{ }

void Lines::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);
  
  QPainter qp(this);
  drawLines(&qp);
}

void Lines::drawLines(QPainter *qp) {
  
  QPen pen(Qt::black, 2, Qt::SolidLine);  
  qp->setPen(pen);
  qp->drawLine(20, 40, 250, 40);

  pen.setStyle(Qt::DashLine);
  qp->setPen(pen);
  qp->drawLine(20, 80, 250, 80);

  pen.setStyle(Qt::DashDotLine);
  qp->setPen(pen);
  qp->drawLine(20, 120, 250, 120);

  pen.setStyle(Qt::DotLine);
  qp->setPen(pen);
  qp->drawLine(20, 160, 250, 160);

  pen.setStyle(Qt::DashDotDotLine);
  qp->setPen(pen);
  qp->drawLine(20, 200, 250, 200);

  QVector<qreal> dashes;
  qreal space = 4;

  dashes << 1 << space << 5 << space;

  pen.setStyle(Qt::CustomDashLine);
  pen.setDashPattern(dashes);
  
  qp->setPen(pen);
  qp->drawLine(20, 240, 250, 240);
}

我们在窗口上绘制了六条线条,每个线条都有不同的笔触风格。

void Lines::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);
  
  QPainter qp(this);
  drawLines(&qp);
}

当小部件被更新时,将调用paintEvent方法。这是我们创建QPainter对象并进行绘图的地方。由于我们没有使用QPaintEvent对象,我们使用Q_UNUSED宏来抑制编译器警告。真正的绘图委托给了drawLines方法。

QPen pen(Qt::black, 2, Qt::SolidLine);

qp->setPen(pen);

我们创建了一个QPen对象。笔触是实线,厚度为2个像素,颜色为黑色。笔触用于绘制线条和形状的轮廓。使用setPen方法将笔触设置为绘图对象。

qp->drawLine(20, 40, 250, 40);

drawLine方法绘制一条线。四个参数是窗口上两个点的坐标。

pen.setStyle(Qt::DashLine);

QPen的setStyle方法设置笔触风格,这里设置为Qt::DashLine。

main.cpp
#include <QApplication>
#include "lines.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Lines window;
  
  window.resize(280, 270);
  window.setWindowTitle("Lines");
  window.show();

  return app.exec();
}

颜色

颜色是表示红色、绿色和蓝色(RGB)强度值组合的对象。有效的RGB值范围为0到255。在下面的示例中,我们绘制了九个填充有不同颜色的矩形。

colours.h
#pragma once 

#include <QWidget>

class Colours : public QWidget {
    
  public:
    Colours(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *e);
    
  private:
    void doPainting();  
};


colours.cpp
#include <QPainter>
#include "colours.h"

Colours::Colours(QWidget *parent)
    : QWidget(parent)
{ }

void Colours::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);  
  
  doPainting();
}

void Colours::doPainting() {
    
  QPainter painter(this);
  painter.setPen(QColor("#d4d4d4"));

  painter.setBrush(QBrush("#c56c00"));
  painter.drawRect(10, 15, 90, 60);

  painter.setBrush(QBrush("#1ac500"));
  painter.drawRect(130, 15, 90, 60);

  painter.setBrush(QBrush("#539e47"));
  painter.drawRect(250, 15, 90, 60);
  
  painter.setBrush(QBrush("#004fc5"));
  painter.drawRect(10, 105, 90, 60);

  painter.setBrush(QBrush("#c50024"));
  painter.drawRect(130, 105, 90, 60);

  painter.setBrush(QBrush("#9e4757"));
  painter.drawRect(250, 105, 90, 60);

  painter.setBrush(QBrush("#5f3b00"));
  painter.drawRect(10, 195, 90, 60);

  painter.setBrush(QBrush("#4c4c4c"));
  painter.drawRect(130, 195, 90, 60);

  painter.setBrush(QBrush("#785f36"));
  painter.drawRect(250, 195, 90, 60);
}

我们绘制了九个填充有不同颜色的矩形。矩形的轮廓为灰色。

painter.setBrush(QBrush("#c56c00"));

painter.drawRect(10, 15, 90, 60);

QBrush类定义了QPainter绘制的形状的填充模式。drawRect方法绘制一个矩形。它在指定的x,y点处绘制一个左上角为起点的矩形,并给定宽度和高度。我们使用十六进制表示法指定颜色值。

main.cpp
#include <QApplication>
#include "colours.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Colours window;
  
  window.resize(360, 280);
  window.setWindowTitle("Colours");
  window.show();
  
  return app.exec();
}

这是主文件。

模式


下面的编程代码例子与前一个类似。这一次我们用各种预定义的图案来填充矩形。

patterns.h
#pragma once

#include <QWidget>

class Patterns : public QWidget {
    
  public:
    Patterns(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *e);

  private:
    void doPainting();
};


patterns.cpp
#include <QApplication>
#include <QPainter>
#include "patterns.h"

Patterns::Patterns(QWidget *parent)
    : QWidget(parent)
{ }

void Patterns::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);  
  
  doPainting();
}

void Patterns::doPainting() {
    
  QPainter painter(this);
  painter.setPen(Qt::NoPen);

  painter.setBrush(Qt::HorPattern);
  painter.drawRect(10, 15, 90, 60);

  painter.setBrush(Qt::VerPattern);
  painter.drawRect(130, 15, 90, 60);

  painter.setBrush(Qt::CrossPattern);
  painter.drawRect(250, 15, 90, 60);
  
  painter.setBrush(Qt::Dense7Pattern);
  painter.drawRect(10, 105, 90, 60);

  painter.setBrush(Qt::Dense6Pattern);
  painter.drawRect(130, 105, 90, 60);

  painter.setBrush(Qt::Dense5Pattern);
  painter.drawRect(250, 105, 90, 60);

  painter.setBrush(Qt::BDiagPattern);
  painter.drawRect(10, 195, 90, 60);

  painter.setBrush(Qt::FDiagPattern);
  painter.drawRect(130, 195, 90, 60);

  painter.setBrush(Qt::DiagCrossPattern);
  painter.drawRect(250, 195, 90, 60);
}


我们用各种画笔模式画出九个矩形。

painter.setBrush(Qt::HorPattern);
painter.drawRect(10, 15, 90, 60);
我们用一个特定的图案绘制一个矩形。Qt::HorPattern是一個常數,用來創建一個水平線的圖案。

main.cpp
#include <QDesktopWidget>
#include <QApplication>
#include "patterns.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Patterns window;
  
  window.resize(350, 280);
  window.setWindowTitle("Patterns");
  window.show();

  return app.exec();
}


这是主文件。

透明矩形

透明性是指能够透视材料的属性。理解透明性的最简单方法是想象一块玻璃或水。从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。

在计算机图形学中,我们可以使用alpha混合来实现透明效果。Alpha混合是将图像与背景组合以创建部分透明的外观的过程。合成过程使用alpha通道。

transparent_rectangles.h
#pragma once

#include <QWidget>

class TransparentRectangles : public QWidget {

  public:
    TransparentRectangles(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *event);
    void doPainting();
};


transparent_rectangles.cpp
#include <QApplication>
#include <QPainter>
#include <QPainterPath>
#include "transparent_rectangles.h"

TransparentRectangles::TransparentRectangles(QWidget *parent)
    : QWidget(parent)
{ }

void TransparentRectangles::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);
  
  doPainting();
}

void TransparentRectangles::doPainting() {
    
  QPainter painter(this);
  
  for (int i=1; i<=11; i++) {
    painter.setOpacity(i*0.1);
    painter.fillRect(50*i, 20, 40, 40, Qt::darkGray);
  }    
}

该示例绘制了十个具有不同透明度级别的矩形。

painter.setOpacity(i*0.1);

setOpacity方法设置了绘制器的不透明度。该值应在0.0到1.0的范围内,其中0.0表示完全透明,1.0表示完全不透明。

main.cpp
#include <QDesktopWidget>
#include <QApplication>
#include "transparent_rectangles.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  TransparentRectangles window;

  window.resize(630, 90);  
  window.setWindowTitle("Transparent rectangles");
  window.show();

  return app.exec();
}

这是主文件。

甜甜圈

在下面的示例中,我们将创建一个甜甜圈形状。

donut.h
#pragma once

#include <QWidget>

class Donut : public QWidget {
    
  public:
    Donut(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *e);
    
  private:
    void doPainting();  
};


donut.cpp
#include <QApplication>
#include <QPainter>
#include "donut.h"

Donut::Donut(QWidget *parent)
    : QWidget(parent)
{ }

void Donut::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);

  doPainting();
}

void Donut::doPainting() {
  
  QPainter painter(this);

  painter.setPen(QPen(QBrush("#535353"), 0.5));
  painter.setRenderHint(QPainter::Antialiasing);

  int h = height();
  int w = width();

  painter.translate(QPoint(w/2, h/2));

  for (qreal rot=0; rot < 360.0; rot+=5.0 ) {
      painter.drawEllipse(-125, -40, 250, 80);
      painter.rotate(5.0);
  }
}

"甜甜圈"是一种类似食物的高级几何形状。我们通过绘制72个旋转的椭圆来创建它。

painter.setRenderHint(QPainter::Antialiasing);

我们将以抗锯齿模式绘制。渲染质量会更高。

int h = height();

int w = width();

painter.translate(QPoint(w/2, h/2));

这些行将坐标系的起始点移动到窗口的中间。默认情况下,它位于0, 0点。换句话说,位于窗口的左上角。通过移动坐标系,绘图会更容易。

for (qreal rot=0; rot < 360.0; rot+=5.0 ) {
    painter.drawEllipse(-125, -40, 250, 80);
    painter.rotate(5.0);
}

在这个循环中,我们绘制了72个旋转的椭圆。

main.cpp
#include <QDesktopWidget>
#include <QApplication>
#include "donut.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Donut window;

  window.resize(350, 280);  
  window.setWindowTitle("Donut");
  window.show();

  return app.exec();
}

这是主文件。

形状

Qt5绘图API可以绘制各种形状。下面的编程代码示例展示了其中一些。

shapes.h
#pragma once

#include <QWidget>

class Shapes : public QWidget {
    
  public:
    Shapes(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *e);

  private:
    void doPainting();
};


shapes.cpp
#include <QApplication>
#include <QPainter>
#include <QPainterPath>
#include "shapes.h"

Shapes::Shapes(QWidget *parent)
    : QWidget(parent)
{ }

void Shapes::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);

  doPainting();
}

void Shapes::doPainting() {
  
  QPainter painter(this);

  painter.setRenderHint(QPainter::Antialiasing);
  painter.setPen(QPen(QBrush("#888"), 1));
  painter.setBrush(QBrush(QColor("#888")));

  QPainterPath path1;

  path1.moveTo(5, 5);
  path1.cubicTo(40, 5,  50, 50,  99, 99);
  path1.cubicTo(5, 99,  50, 50,  5, 5);
  painter.drawPath(path1);  

  painter.drawPie(130, 20, 90, 60, 30*16, 120*16);
  painter.drawChord(240, 30, 90, 60, 0, 16*180);
  painter.drawRoundRect(20, 120, 80, 50);

  QPolygon polygon({QPoint(130, 140), QPoint(180, 170), QPoint(180, 140),
      QPoint(220, 110), QPoint(140, 100)});

  painter.drawPolygon(polygon);

  painter.drawRect(250, 110, 60, 60);

  QPointF baseline(20, 250);
  QFont font("Georgia", 55);
  QPainterPath path2;
  path2.addText(baseline, font, "Q");
  painter.drawPath(path2);

  painter.drawEllipse(140, 200, 60, 60);
  painter.drawEllipse(240, 200, 90, 60);
}

我们绘制了九种不同的形状。

QPainterPath path1;

path1.moveTo(5, 5);

path1.cubicTo(40, 5, 50, 50, 99, 99);

path1.cubicTo(5, 99, 50, 50, 5, 5);

painter.drawPath(path1);

QPainterPath是用于创建复杂形状的对象。我们使用它来绘制贝塞尔曲线。

painter.drawPie(130, 20, 90, 60, 3016, 12016);

painter.drawChord(240, 30, 90, 60, 0, 16*180);

painter.drawRoundRect(20, 120, 80, 50);

这些代码行绘制了一个扇形、一个弦和一个圆角矩形。

QPolygon polygon({QPoint(130, 140), QPoint(180, 170), QPoint(180, 140), QPoint(220, 110), QPoint(140, 100)});

painter.drawPolygon(polygon);

这里我们使用drawPolygon方法绘制了一个多边形,该多边形由五个点组成。

QPointF baseline(20, 250);

QFont font("Georgia", 55);

QPainterPath path2;

path2.addText(baseline, font, "Q");

painter.drawPath(path2);

Qt5允许基于字体字符创建路径。

painter.drawEllipse(140, 200, 60, 60);

painter.drawEllipse(240, 200, 90, 60);

drawEllipse绘制了一个椭圆和一个圆。圆是椭圆的特殊情况。参数是椭圆边界矩形的起点的x和y坐标以及宽度和高度。

main.cpp
#include <QDesktopWidget>
#include <QApplication>
#include "shapes.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Shapes window;

  window.resize(350, 280);  
  window.setWindowTitle("Shapes");
  window.show();

  return app.exec();
}

这是示例的主文件。

渐变

在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的平滑混合。在二维绘图程序和绘画程序中,渐变被用于创建丰富多彩的背景和特效,以及模拟光线和阴影。

下面的代码示例展示了如何创建线性渐变。

linear_gradients.h
#pragma once

#include <QWidget>

class LinearGradients : public QWidget {

  public:
    LinearGradients(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *e);
    
  private:
    void doPainting();  
};


linear_gradients.cpp
#include <QApplication>
#include <QPainter>
#include "linear_gradients.h"

LinearGradients::LinearGradients(QWidget *parent)
    : QWidget(parent)
{ }

void LinearGradients::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);
  
  doPainting();
}  

void LinearGradients::doPainting() {
         
  QPainter painter(this);
  
  QLinearGradient grad1(0, 20, 0, 110);

  grad1.setColorAt(0.1, Qt::black);
  grad1.setColorAt(0.5, Qt::yellow);
  grad1.setColorAt(0.9, Qt::black);

  painter.fillRect(20, 20, 300, 90, grad1);

  QLinearGradient grad2(0, 55, 250, 0);

  grad2.setColorAt(0.2, Qt::black);
  grad2.setColorAt(0.5, Qt::red);
  grad2.setColorAt(0.8, Qt::black);

  painter.fillRect(20, 140, 300, 100, grad2);
}

在代码示例中,我们绘制了两个矩形,并用线性渐变来填充它们。

QLinearGradient grad1(0, 20, 0, 110);

QLinearGradient使用提供的参数构建一个线性渐变,其中插值区域在两个点之间。

grad1.setColorAt(0.1, Qt::black);

grad1.setColorAt(0.5, Qt::yellow);

grad1.setColorAt(0.9, Qt::black);

渐变中的颜色是使用停止点来定义的。setColorAt在给定的位置使用给定的颜色创建一个停止点。

painter.fillRect(20, 20, 300, 90, grad1);

我们用渐变填充矩形。

main.cpp
#include <QApplication>
#include "linear_gradients.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  LinearGradients window;

  window.resize(350, 260);  
  window.setWindowTitle("Linear gradients");
  window.show();

  return app.exec();
}

这是主文件。

径向渐变

径向渐变是在两个圆之间混合颜色或色调的效果。

radial_gradient.h
#pragma once

#include <QWidget>

class RadialGradient : public QWidget {

  public:
    RadialGradient(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *e);
    
  private:
    void doPainting();  
};


radial_gradient.cpp
#include <QApplication>
#include <QPainter>
#include "radial_gradient.h"

RadialGradient::RadialGradient(QWidget *parent)
    : QWidget(parent)
{ }

void RadialGradient::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);
  
  doPainting();
}

void RadialGradient::doPainting() {
  
  QPainter painter(this);
  
  int h = height();
  int w = width();

  QRadialGradient grad1(w/2, h/2, 80);

  grad1.setColorAt(0, QColor("#032E91"));
  grad1.setColorAt(0.3, Qt::white);
  grad1.setColorAt(1, QColor("#032E91"));

  painter.fillRect(0, 0, w, h, grad1);
}

该示例创建了一个径向渐变;渐变从窗口的中心扩散出来。

QRadialGradient grad1(w/2, h/2, 80);

QRadialGradient 创建了一个径向渐变;它在一个围绕着一个焦点和端点的圆上插值颜色。参数是圆的中心点坐标和半径。焦点位于圆的中心。

grad1.setColorAt(0, QColor("#032E91"));

grad1.setColorAt(0.3, Qt::white);

grad1.setColorAt(1, QColor("#032E91"));

setColorAt 方法定义了彩色的停止点。

painter.fillRect(0, 0, w, h, grad1);

将整个窗口区域用径向渐变填充。

main.cpp
#include <QApplication>
#include "radial_gradient.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  RadialGradient window;

  window.resize(300, 250);  
  window.setWindowTitle("Radial gradient");
  window.show();

  return app.exec();
}

这是主文件。

膨胀效果

在这个C++ Qt5教程章节的最后一个例子中,我们创建了一个膨胀效果。该示例显示一个不断增长的居中文本,并逐渐从某个点淡出。这是一个非常常见的效果,在网页上的Flash动画中经常看到。

puff.h
#pragma once

#include <QWidget>

class Puff : public QWidget {

  public:
    Puff(QWidget *parent = 0);

  protected:
    void paintEvent(QPaintEvent *event);
    void timerEvent(QTimerEvent *event);

  private:
    int x;
    qreal opacity;
    int timerId;
    
    void doPainting();
};


puff.cpp
#include <QPainter>
#include <QTimer>
#include <QTextStream>
#include "puff.h"

Puff::Puff(QWidget *parent)
    : QWidget(parent) {
        
  x = 1;
  opacity = 1.0;
  timerId = startTimer(15);
}

void Puff::paintEvent(QPaintEvent *e) {
    
  Q_UNUSED(e);  
  
  doPainting();
}

void Puff::doPainting() {
  
  QPainter painter(this);
  QTextStream out(stdout);

  QString text = "ZetCode";

  painter.setPen(QPen(QBrush("#575555"), 1));

  QFont font("Courier", x, QFont::DemiBold);
  QFontMetrics fm(font);
  int textWidth = fm.width(text);

  painter.setFont(font);

  if (x > 10) {
    opacity -= 0.01;
    painter.setOpacity(opacity);
  }

  if (opacity <= 0) {
    killTimer(timerId);
    out << "timer stopped" << endl;
  }

  int h = height();
  int w = width();

  painter.translate(QPoint(w/2, h/2));
  painter.drawText(-textWidth/2, 0, text);
}

void Puff::timerEvent(QTimerEvent *e) {
    
  Q_UNUSED(e);
  
  x += 1;
  repaint();
}

在头文件中,我们定义了两个事件处理程序:绘图事件处理程序和定时器事件处理程序。

Puff::Puff(QWidget *parent)
    : QWidget(parent) {
        
  x = 1;
  opacity = 1.0;
  timerId = startTimer(15);
}

在构造函数中,我们启动了定时器。每15毫秒生成一个定时器事件。

void Puff::timerEvent(QTimerEvent *e) {
    
  Q_UNUSED(e);
  
  x += 1;
  repaint();
}

在timerEvent中,我们增加字体大小并重绘小部件。

if (x > 10) {
  opacity -= 0.01;
  painter.setOpacity(opacity);
}

如果字体大小大于10点,我们逐渐降低不透明度;文本开始淡出。

if (opacity <= 0) {
  killTimer(timerId);
  out << "timer stopped" << endl;
}

如果文本完全淡出,我们停止定时器。

main.cpp
#include <QApplication>
#include "puff.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv); 

  Puff window;

  window.resize(350, 280);
  window.setWindowTitle("Puff");
  window.show();

  return app.exec();
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值