QPainter 基本绘制案例

基本概念

  • QPainter 可以绘制简单的线条,复杂的形状,文本和贴图,并支持坐标变换
  • QPainter 可以对继承 QPaintDevice 类的任何对象进行操作
  • QPainter 最常见的用法是在 QWidget 的 paintEvent 中,构造和自定义 QPainter 对象,然后进行绘制

QPainter 用法

  • save(); // 保存状态
  • restore(); // 恢复状态
  • setPen(); // 设置线条
  • setBrush(); // 设置填充
  • setFont(); // 只对文字有效,设置字体
  • translate(); // 平移
  • rotate(); // 旋转
  • scale(); // 缩放
  • setRenderHinter(QPainter::Antialiasing, true); // 抗锯齿
  • QPainterPath 可以绘制路径

QPen 设置

  • color,width,style,cap,join 等设置都包含在 QPen 中
  • style,cap 和 join 等选项都在 Qt 命名空间中

其它注意事项

设置 QWidget 背景色

  • setAutoFillBackground(true);
  • setbackgroundRole(QPalette::Base);

QPixmap 载入图片

  • pixmap.load(path);

QComboBox 用法

  • comboBox->addItem("name", item);
  • comboBox->itemData(index, Qt::UserRole);

类型转换

  • Qvariant 首先要用 toInt() 转化成成型,之后才能用 static_cast 转换成 enum class

布局

  • layoutSizeConstraint 设置成 setMinimumSize 可以保持布局最小

案例代码

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QApplication>
#include <QStyle>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void shapeChanged();
    void penChanged();
    void brushChanged();
};
#endif // MAINWINDOW_H

mainwindow.cpp

mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("基本绘制");
    this->setWindowIcon(qApp->style()->standardIcon(QStyle::SP_DialogHelpButton));

    ui->comboBoxShape->addItem(tr("Polygon"), static_cast<int>(RenderArea::Shape::Polygon));
    ui->comboBoxShape->addItem(tr("Rectangle"), static_cast<int>(RenderArea::Shape::Rect));
    ui->comboBoxShape->addItem(tr("Rounded Rectangle"), static_cast<int>(RenderArea::Shape::RoundedRect));
    ui->comboBoxShape->addItem(tr("Ellipse"), static_cast<int>(RenderArea::Shape::Ellipse));
    ui->comboBoxShape->addItem(tr("Pie"), static_cast<int>(RenderArea::Shape::Pie));
    ui->comboBoxShape->addItem(tr("Chord"), static_cast<int>(RenderArea::Shape::Chord));
    ui->comboBoxShape->addItem(tr("Path"), static_cast<int>(RenderArea::Shape::Path));
    ui->comboBoxShape->addItem(tr("Line"), static_cast<int>(RenderArea::Shape::Line));
    ui->comboBoxShape->addItem(tr("Polyline"), static_cast<int>(RenderArea::Shape::Polyline));
    ui->comboBoxShape->addItem(tr("Arc"), static_cast<int>(RenderArea::Shape::Arc));
    ui->comboBoxShape->addItem(tr("Points"), static_cast<int>(RenderArea::Shape::Points));
    ui->comboBoxShape->addItem(tr("Text"), static_cast<int>(RenderArea::Shape::Text));
    ui->comboBoxShape->addItem(tr("Pixmap"), static_cast<int>(RenderArea::Shape::Pixmap));

    ui->spinBoxPenWidth->setRange(0, 20);
    ui->spinBoxPenWidth->setSpecialValueText(tr("0 (cosmetic pen)"));

    ui->comboBoxPenSytle->addItem(tr("Solid"), static_cast<int>(Qt::SolidLine));
    ui->comboBoxPenSytle->addItem(tr("Dash"), static_cast<int>(Qt::DashLine));
    ui->comboBoxPenSytle->addItem(tr("Dot"), static_cast<int>(Qt::DotLine));
    ui->comboBoxPenSytle->addItem(tr("Dash Dot"), static_cast<int>(Qt::DashDotLine));
    ui->comboBoxPenSytle->addItem(tr("Dash Dot Dot"), static_cast<int>(Qt::DashDotDotLine));
    ui->comboBoxPenSytle->addItem(tr("None"), static_cast<int>(Qt::NoPen));

    ui->comboBoxPenCap->addItem(tr("Flat"), Qt::FlatCap);
    ui->comboBoxPenCap->addItem(tr("Square"), Qt::SquareCap);
    ui->comboBoxPenCap->addItem(tr("Round"), Qt::RoundCap);

    ui->comboBoxPenJoin->addItem(tr("Miter"), Qt::MiterJoin);
    ui->comboBoxPenJoin->addItem(tr("Bevel"), Qt::BevelJoin);
    ui->comboBoxPenJoin->addItem(tr("Round"), Qt::RoundJoin);

    ui->comboBoxBrushStyle->addItem(tr("Linear Gradient"), static_cast<int>(Qt::LinearGradientPattern));
    ui->comboBoxBrushStyle->addItem(tr("Radial Gradient"), static_cast<int>(Qt::RadialGradientPattern));
    ui->comboBoxBrushStyle->addItem(tr("Conical Gradient"), static_cast<int>(Qt::ConicalGradientPattern));
    ui->comboBoxBrushStyle->addItem(tr("Texture"), static_cast<int>(Qt::TexturePattern));
    ui->comboBoxBrushStyle->addItem(tr("Solid"), static_cast<int>(Qt::SolidPattern));
    ui->comboBoxBrushStyle->addItem(tr("Horizontal"), static_cast<int>(Qt::HorPattern));
    ui->comboBoxBrushStyle->addItem(tr("Vertical"), static_cast<int>(Qt::VerPattern));
    ui->comboBoxBrushStyle->addItem(tr("Cross"), static_cast<int>(Qt::CrossPattern));
    ui->comboBoxBrushStyle->addItem(tr("Backward Diagonal"), static_cast<int>(Qt::BDiagPattern));
    ui->comboBoxBrushStyle->addItem(tr("Forward Diagonal"), static_cast<int>(Qt::FDiagPattern));
    ui->comboBoxBrushStyle->addItem(tr("Diagonal Cross"), static_cast<int>(Qt::DiagCrossPattern));
    ui->comboBoxBrushStyle->addItem(tr("Dense 1"), static_cast<int>(Qt::Dense1Pattern));
    ui->comboBoxBrushStyle->addItem(tr("Dense 2"), static_cast<int>(Qt::Dense2Pattern));
    ui->comboBoxBrushStyle->addItem(tr("Dense 3"), static_cast<int>(Qt::Dense3Pattern));
    ui->comboBoxBrushStyle->addItem(tr("Dense 4"), static_cast<int>(Qt::Dense4Pattern));
    ui->comboBoxBrushStyle->addItem(tr("Dense 5"), static_cast<int>(Qt::Dense5Pattern));
    ui->comboBoxBrushStyle->addItem(tr("Dense 6"), static_cast<int>(Qt::Dense6Pattern));
    ui->comboBoxBrushStyle->addItem(tr("Dense 7"), static_cast<int>(Qt::Dense7Pattern));
    ui->comboBoxBrushStyle->addItem(tr("None"), static_cast<int>(Qt::NoBrush));

    connect(ui->comboBoxShape, &QComboBox::activated, this, &MainWindow::shapeChanged);
    connect(ui->spinBoxPenWidth, &QSpinBox::valueChanged, this, &MainWindow::penChanged);
    connect(ui->comboBoxPenSytle, &QComboBox::activated, this, &MainWindow::penChanged);
    connect(ui->comboBoxPenCap, &QComboBox::activated, this, &MainWindow::penChanged);
    connect(ui->comboBoxPenJoin, &QComboBox::activated, this, &MainWindow::penChanged);
    connect(ui->comboBoxBrushStyle, &QComboBox::activated, this, &MainWindow::brushChanged);
    connect(ui->checkBoxAntiAliasing, &QAbstractButton::toggled,
            ui->renderArea, &RenderArea::setAntialiased);
    connect(ui->checkBoxTransformation, &QAbstractButton::toggled,
            ui->renderArea, &RenderArea::setTransformed);

    shapeChanged();
    penChanged();
    brushChanged();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::shapeChanged()
{
    auto shape = static_cast<RenderArea::Shape>(ui->comboBoxShape->itemData(
                                                    ui->comboBoxShape->currentIndex()
                                                    ).toInt());
    ui->renderArea->setShape(shape);
}

void MainWindow::penChanged()
{
    int width = ui->spinBoxPenWidth->value();
    auto style = static_cast<Qt::PenStyle>(ui->comboBoxPenSytle->itemData(
                                               ui->comboBoxPenSytle->currentIndex()
                                               ).toInt());
    auto cap = static_cast<Qt::PenCapStyle>(ui->comboBoxPenCap->itemData(
                                                ui->comboBoxPenCap->currentIndex()
                                                ).toInt());
    auto join = static_cast<Qt::PenJoinStyle>(ui->comboBoxPenJoin->itemData(
                                                  ui->comboBoxPenJoin->currentIndex()
                                                  ).toInt());
    ui->renderArea->setPen(QPen(Qt::blue, width, style, cap, join));
}

void MainWindow::brushChanged()
{
    auto style = static_cast<Qt::BrushStyle>(ui->comboBoxBrushStyle->itemData(
                                                 ui->comboBoxBrushStyle->currentIndex()
                                                 ).toInt());
    if (style == Qt::LinearGradientPattern)
    {
        QLinearGradient linearGradient(0, 0, 100, 100);
        linearGradient.setColorAt(0.0, Qt::white);
        linearGradient.setColorAt(0.2, Qt::green);
        linearGradient.setColorAt(1.0, Qt::black);
        ui->renderArea->setBrush(linearGradient);
    }
    else if (style == Qt::RadialGradientPattern)
    {
        QRadialGradient radialGradient(50, 50, 50, 70, 70);
        radialGradient.setColorAt(0.0, Qt::white);
        radialGradient.setColorAt(0.2, Qt::green);
        radialGradient.setColorAt(1.0, Qt::black);
        ui->renderArea->setBrush(radialGradient);
    }
    else if (style == Qt::ConicalGradientPattern)
    {
        QConicalGradient conicalGradient(50, 50, 150);
        conicalGradient.setColorAt(0.0, Qt::white);
        conicalGradient.setColorAt(0.2, Qt::green);
        conicalGradient.setColorAt(1.0, Qt::black);
        ui->renderArea->setBrush(conicalGradient);
    }
    else if (style == Qt::TexturePattern)
    {
        ui->renderArea->setBrush(QBrush(ui->renderArea->getPixmap()));
    }
    else
    {
        ui->renderArea->setBrush(QBrush(Qt::green, style));
    }
}

renderarea.h

renderarea.h
#ifndef RENDERAREA_H
#define RENDERAREA_H

#include <QWidget>
#include <QPen>
#include <QPainterPath>
#include <QPainter>

namespace Ui {
class RenderArea;
}

class RenderArea : public QWidget
{
    Q_OBJECT

public:
    explicit RenderArea(QWidget *parent = nullptr);
    ~RenderArea();

    enum class Shape {
        Line, Points, Polyline, Polygon, Rect, RoundedRect, Arc,
        Ellipse, Chord, Pie, Path, Text, Pixmap,
    };

    const QPixmap &getPixmap() const;

public slots:
    void setShape(const Shape newShape);
    void setPen(const QPen &newPen);
    void setBrush(const QBrush &newBrush);
    void setAntialiased(bool newAntialiased);
    void setTransformed(bool newTransformed);

private:
    Shape shape;
    QPen pen;
    QBrush brush;
    bool antialiased;
    bool transformed;
    QPixmap pixmap;

    Ui::RenderArea *ui;

    // QWidget interface
protected:
    void paintEvent(QPaintEvent *event) override;
};

#endif // RENDERAREA_H

renderarea.cpp

renderarea.cpp
#include "renderarea.h"
#include "ui_renderarea.h"

RenderArea::RenderArea(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::RenderArea)
{
    ui->setupUi(this);
    setAutoFillBackground(true);
    setBackgroundRole(QPalette::Base);

    shape = Shape::Polygon;
    antialiased = false;
    transformed = false;
    pixmap.load(":/pixmap.jpg");
}

RenderArea::~RenderArea()
{
    delete ui;
}

void RenderArea::setShape(const Shape newShape)
{
    shape = newShape;
    update();
}

void RenderArea::setPen(const QPen &newPen)
{
    pen = newPen;
    update();
}

void RenderArea::setBrush(const QBrush &newBrush)
{
    brush = newBrush;
    update();
}

void RenderArea::setAntialiased(bool newAntialiased)
{
    antialiased = newAntialiased;
    update();
}

void RenderArea::setTransformed(bool newTransformed)
{
    transformed = newTransformed;
    update();
}

const QPixmap &RenderArea::getPixmap() const
{
    return pixmap;
}

void RenderArea::paintEvent(QPaintEvent *event)
{
    static const QPoint points[4] = {
        QPoint(10, 80),
        QPoint(20, 10),
        QPoint(80, 30),
        QPoint(90, 70)
    };
    QRect rect(10, 20, 80, 60);

    QPainterPath path;
    path.moveTo(20, 80);
    path.lineTo(20, 30);
    path.cubicTo(80, 0, 50, 50, 80, 80);

    int startAngle = 20 * 16;
    int arcLength = 120 * 16;

    QPainter painter(this);
    painter.setPen(pen);
    painter.setBrush(brush);
    if (antialiased)
    {
        painter.setRenderHint(QPainter::Antialiasing, true);
    }

    for (int x = 0; x < width(); x += 100) {
        for (int y = 0; y < height(); y += 100) {
            painter.save();
            painter.translate(x, y);

            if (transformed)
            {
                painter.translate(50, 50);
                painter.rotate(60.0);
                painter.scale(0.6, 0.9);
                painter.translate(-50, -50);
            }

            switch (shape) {
            case Shape::Line:
                painter.drawLine(rect.bottomLeft(), rect.topRight());
                break;
            case Shape::Arc:
                painter.drawArc(rect, startAngle, arcLength);
                break;
            case Shape::Chord:
                painter.drawChord(rect, startAngle, arcLength);
                break;
            case Shape::Ellipse:
                painter.drawEllipse(rect);
                break;
            case Shape::Path:
                painter.drawPath(path);
                break;
            case Shape::Pie:
                painter.drawPie(rect, startAngle, arcLength);
                break;
            case Shape::Pixmap:
                painter.drawPixmap(10, 10, pixmap);
                break;
            case Shape::Points:
                painter.drawPoints(points, 4);
                break;
            case Shape::Polygon:
                painter.drawPolygon(points, 4);
                break;
            case Shape::Polyline:
                painter.drawPolyline(points, 4);
                break;
            case Shape::Rect:
                painter.drawRect(rect);
                break;
            case Shape::RoundedRect:
                painter.drawRoundedRect(rect, 25, 25, Qt::RelativeSize);
                break;
            case Shape::Text:
                painter.drawText(rect, Qt::AlignCenter, "安小静");
                break;
            }
            painter.restore();
        }
    }

}