QWidget 坐标变换案例

QFontMetrics

可以用下面的代码获得字符串的 bounding rect

QRect fontBoundingRect = QFontMetrics(font).boundingRect(tr("Qt"));

案例代码

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPainterPath>
#include <QComboBox>
#include "renderarea.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void shapeChanged(int index);
    void transChanged();

private:
    Ui::MainWindow *ui;
    static constexpr int numOfTrans = 3;
    QList<QPainterPath> shapes;
    RenderArea* originalRenderArea;
    RenderArea* transRenderAreas[numOfTrans];
    QComboBox* shapeComboBox;
    QComboBox* transComboBoxes[numOfTrans];

    void setupShapes();

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QGridLayout>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    originalRenderArea = new RenderArea(this);
    shapeComboBox = new QComboBox(this);
    shapeComboBox->addItem("Clock");
    shapeComboBox->addItem("House");
    shapeComboBox->addItem("Text");
    shapeComboBox->addItem("Truck");

    connect(shapeComboBox, &QComboBox::activated,
            this, &MainWindow::shapeChanged);

    QGridLayout* layout = new QGridLayout;
    centralWidget()->setLayout(layout);
    layout->addWidget(originalRenderArea, 0, 0);
    layout->addWidget(shapeComboBox, 1, 0);

    for (int i = 0; i < numOfTrans; ++i) {
        transRenderAreas[i] = new RenderArea(this);
        transComboBoxes[i] = new QComboBox(this);
        transComboBoxes[i]->addItem(tr("No transformation"));
        transComboBoxes[i]->addItem(tr("Translate by (50, 50)"));
        transComboBoxes[i]->addItem(tr("Rotate by 60\xC2\xB0"));
        transComboBoxes[i]->addItem(tr("Scale to 75%"));

        connect(transComboBoxes[i], &QComboBox::activated,
                this, &MainWindow::transChanged);

        layout->addWidget(transRenderAreas[i], 0, i + 1);
        layout->addWidget(transComboBoxes[i], 1, i + 1);
    }

    setupShapes();
    shapeChanged(0);
}

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

void MainWindow::shapeChanged(int index)
{
    QPainterPath shape = shapes[index];
    originalRenderArea->setShape(shape);
    for (auto renderArea : transRenderAreas) {
        renderArea->setShape(shape);
    }
}

void MainWindow::transChanged()
{
    QList<RenderArea::Transformation> trans;
    for (int i = 0; i < numOfTrans; ++i) {
        int index = transComboBoxes[i]->currentIndex();
        trans.append(RenderArea::Transformation(index));
        transRenderAreas[i]->setTrans(trans);
    }
}

void MainWindow::setupShapes()
{
    QPainterPath truck;
    truck.setFillRule(Qt::WindingFill);
    truck.moveTo(0.0, 87.0);
    truck.lineTo(0.0, 60.0);
    truck.lineTo(10.0, 60.0);
    truck.lineTo(35.0, 35.0);
    truck.lineTo(100.0, 35.0);
    truck.lineTo(100.0, 87.0);
    truck.lineTo(0.0, 87.0);
    truck.moveTo(17.0, 60.0);
    truck.lineTo(55.0, 60.0);
    truck.lineTo(55.0, 40.0);
    truck.lineTo(37.0, 40.0);
    truck.lineTo(17.0, 60.0);
    truck.addEllipse(17.0, 75.0, 25.0, 25.0);
    truck.addEllipse(63.0, 75.0, 25.0, 25.0);

    QPainterPath clock;
    clock.addEllipse(-50.0, -50.0, 100.0, 100.0);
    clock.addEllipse(-48.0, -48.0, 96.0, 96.0);
    clock.moveTo(0.0, 0.0);
    clock.lineTo(-2.0, -2.0);
    clock.lineTo(0.0, -42.0);
    clock.lineTo(2.0, -2.0);
    clock.lineTo(0.0, 0.0);
    clock.moveTo(0.0, 0.0);
    clock.lineTo(2.732, -0.732);
    clock.lineTo(24.495, 14.142);
    clock.lineTo(0.732, 2.732);
    clock.lineTo(0.0, 0.0);

    QPainterPath house;
    house.moveTo(-45.0, -20.0);
    house.lineTo(0.0, -45.0);
    house.lineTo(45.0, -20.0);
    house.lineTo(45.0, 45.0);
    house.lineTo(-45.0, 45.0);
    house.lineTo(-45.0, -20.0);
    house.addRect(15.0, 5.0, 20.0, 35.0);
    house.addRect(-35.0, -15.0, 25.0, 25.0);

    QPainterPath text;
    QFont font;
    font.setPixelSize(50);
    QRect fontBoundingRect = QFontMetrics(font).boundingRect(tr("Qt"));
    text.addText(-QPointF(fontBoundingRect.center()), font, tr("Qt"));

    shapes.append(clock);
    shapes.append(house);
    shapes.append(text);
    shapes.append(truck);

    connect(shapeComboBox, &QComboBox::activated,
            this, &MainWindow::shapeChanged);
}

renderarea.h

#ifndef RENDERAREA_H
#define RENDERAREA_H

#include <QWidget>
#include <QPainterPath>

class RenderArea : public QWidget
{
    Q_OBJECT
public:
    enum class Operation { NoTransformation, Rotate, Scale, Translate };
    explicit RenderArea(QWidget *parent = nullptr); 
    void setShape(QPainterPath& shape);
    void setOperations(const QList<Operation>& operations);

signals:


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

private:
    QPainterPath shape;
    QList<Operation> operations;

private:
    void drawCoordinates(QPainter& painter);
    void drawOutline(QPainter& painter);
    void drawShape(QPainter& painter);
    void transformPainter(QPainter& painter);
};

#endif // RENDERAREA_H

renderarea.cpp

#include "renderarea.h"
#include <QPainter>
#include <QPaintEvent>

RenderArea::RenderArea(QWidget *parent)
    : QWidget{parent}
{
    setFixedSize(232, 232);
}

void RenderArea::setShape(const QPainterPath &shape)
{
    this->shape = shape;
    update();
}

void RenderArea::setTrans(const QList<RenderArea::Transformation> trans)
{
    this->trans = trans;
    update();
}

void RenderArea::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(event->rect(), Qt::white);
    painter.translate(66, 66);
    drawBoundingBox(painter);

    painter.save();
    transformPainter(painter);
    drawCoordinates(painter);
    drawShape(painter);
    painter.restore();
}

void RenderArea::drawBoundingBox(QPainter& painter)
{
    painter.setPen(Qt::darkGreen);
    painter.setPen(Qt::DashLine);
    painter.setBrush(Qt::NoBrush);
    painter.drawRect(0, 0, 100, 100);
}

void RenderArea::drawCoordinates(QPainter& painter)
{
    painter.setPen(Qt::red);
    painter.drawLine(0, 0, 50, 0);
    painter.drawLine(48, -2, 50, 0);
    painter.drawLine(48, 2, 50, 0);
    painter.drawText(55, 3, "x");

    painter.drawLine(0, 0, 0, 50);
    painter.drawLine(-2, 48, 0, 50);
    painter.drawLine(2, 48, 0, 50);
    painter.drawText(-3, 60, "y");
}

void RenderArea::drawShape(QPainter& painter)
{
    painter.fillPath(shape, Qt::blue);
    update();
}

void RenderArea::transformPainter(QPainter& painter)
{
    for (auto tran : trans) {
        switch (tran) {
        case RenderArea::Transformation::Translate:
            painter.translate(50, 50);
            break;
        case RenderArea::Transformation::Scale:
            painter.scale(0.75, 0.75);
            break;
        case RenderArea::Transformation::Rotate:
            painter.rotate(60);
            break;
        case RenderArea::Transformation::NoTrans:
        default:
            break;
        }
    }
}