文章目录
Coordinate System 坐标系统
The coordinate system is controlled by the QPainter class. Together with the QPaintDevice and QPaintEngine classes, QPainter form the basis of Qt’s painting system, Arthur. QPainter is used to perform drawing operations, QPaintDevice is an abstraction of a two-dimensional space that can be painted on using a QPainter, and QPaintEngine provides the interface that the painter uses to draw onto different types of devices.
翻译:
坐标系统由QPainter类控制,QPaintDevice、QPaintEngine和QPainter构成了Qt的绘图系统的基础。 QPainter执行画图操作,QPaintDevice是一个抽象概念 可以使用QPainter画到QPaintDevice上面, QPaintEngine提供了接口 painter使用它可以画到不同的设备devices上。
The QPaintDevice class is the base class of objects that can be painted: Its drawing capabilities are inherited by the QWidget, QImage, QPixmap, QPicture, and QOpenGLPaintDevice classes. The default coordinate system of a paint device has its origin at the top-left corner. The x values increase to the right and the y values increase downwards. The default unit is one pixel on pixel-based devices and one point (1/72 of an inch) on printers.
翻译:
QPaintDevice是绘图设备的基类: 它的绘图能力被QWidget, QImage, QPixmap, QPicture, 和 QOpenGLPaintDevice类继承。 一个绘图设备的默认坐标系 原点在左上角。 x的值向右增加 y值向下增加。 默认的一个单元在 基于像素的设备上 是一个像素 在打印机上是一个点(1/72英寸)
The mapping of the logical QPainter coordinates to the physical QPaintDevice coordinates are handled by QPainter’s transformation matrix, viewport and “window”. The logical and physical coordinate systems coincide by default. QPainter also supports coordinate transformations (e.g. rotation and scaling).
翻译:
逻辑QPainter坐标系 到 物理QPaintDevice坐标系 的映射 是被 QPainter的转换矩阵、视口、窗口 操作的。默认的 逻辑坐标系和物理坐标系 是一致的。 QPainter也支持坐标系转换(例如:旋转和缩放)
Rendering 渲染
Logical Representation 逻辑表现
The size (width and height) of a graphics primitive always correspond to its mathematical model, ignoring the width of the pen it is rendered with:
翻译:
原始的图形 尺寸(宽和高)通常 与数学模型一致, 忽略画笔的宽度
Aliased Painting 锯齿绘制
When drawing, the pixel rendering is controlled by the QPainter::Antialiasing render hint.
翻译:
当绘图时, 像素渲染被QPainter::Antialiasing渲染提示 控制。
The RenderHint enum is used to specify flags to QPainter that may or may not be respected by any given engine. The QPainter::Antialiasing value indicates that the engine should antialias edges of primitives if possible, i.e. smoothing the edges by using different color intensities.
翻译:
枚举RenderHint原来指定 QPainter的标志 是不是被 受到尊敬 被给定的引擎。 QPainter::Antialiasing的值指定引擎应该抗锯齿绘制边缘 尽可能的。 也就是说, 通过使用不同的颜色程度使边缘平滑
But by default the painter is aliased and other rules apply: When rendering with a one pixel wide pen the pixels will be rendered to the right and below the mathematically defined points. For example:
但是默认的painter是有锯齿的并且有其他的规则应用: 当用1像素宽的画笔时,像素会渲染到定义这个点的数学的 右边和下边 , 例如:
When rendering with a pen with an even number of pixels, the pixels will be rendered symetrically around the mathematical defined points, while rendering with a pen with an odd number of pixels, the spare pixel will be rendered to the right and below the mathematical point as in the one pixel case. See the QRectF diagrams below for concrete examples.
翻译:
当画笔宽度是偶数时,像素将对称性的渲染在定义点的周围, 而当画笔宽度是偶数的时候,空余的像素将被渲染在 数学点 的右下方 就像1pix一样。 看下面QRectF图示 具体案例。
Note that for historical reasons the return value of the QRect::right() and QRect::bottom() functions deviate from the true bottom-right corner of the rectangle.
翻译:注意因为历史原因 QRect::right() 和 QRect::bottom()的返回值偏离了 真实的矩形的右下角。
QRect’s right() function returns left() + width() - 1 and the bottom() function returns top() + height() - 1. The bottom-right green point in the diagrams shows the return coordinates of these functions.
翻译:QRect的right()返回值 为 left() + width() - 1, bottom()返回值为 top() + height() - 1, 图示中的右下角绿点显示了这些函数的返回坐标。
We recommend that you simply use QRectF instead: The QRectF class defines a rectangle in the plane using floating point coordinates for accuracy (QRect uses integer coordinates), and the QRectF::right() and QRectF::bottom() functions do return the true bottom-right corner.
我们建议你简单的使用QRectF来代替:QRectF类定义了在平面的一个矩形,为了精确使用float点(QRect使用了int), QRectF::right()和QRectF::bottom()函数发挥了真正的 右下角点。
Alternatively, using QRect, apply x() + width() and y() + height() to find the bottom-right corner, and avoid the right() and bottom() functions.
或者使用QRect, 使用x() + width()和y() + height() 去发现右下角坐标, 避免right() 和 bottom()函数。
Anti-aliased Painting 抗锯齿绘制
If you set QPainter’s anti-aliasing render hint, the pixels will be rendered symetrically on both sides of the mathematically defined points:
翻译:
如果你设置了QPainter的反锯齿 渲染提示, 像素将均匀的渲染在 数学上定义的点的两边。
Transformations 转换
By default, the QPainter operates on the associated device’s own coordinate system, but it also has complete support for affine coordinate transformations.
默认的,QPainter操作在 相关联的设备 的自己的坐标系统,但是也完全支持反射坐标系转换。
You can scale the coordinate system by a given offset using the QPainter::scale() function, you can rotate it clockwise using the QPainter::rotate() function and you can translate it (i.e. adding a given offset to the points) using the QPainter::translate() function.
你可以缩放坐标系统 使用QPainter::scale()函数 通过一个偏移量, 你可以 旋转它顺时针 使用QPainter::rotate()函数 , 你可以平移它(也就是说 加一个给定的偏移量 对点) 使用QPainter::translate()函数
You can also twist the coordinate system around the origin using the QPainter::shear() function. All the transformation operations operate on QPainter’s transformation matrix that you can retrieve using the QPainter::worldTransform() function. A matrix transforms a point in the plane to another point.
你也可以 扭曲 这个坐标系统 围绕原点 使用QPainter::shear()函数。 所有转换 在QPainter的转换矩阵(你可以用QPainter::worldTransform()函数取到)中操作 。 矩阵吧平面上的一个点转换为另外一个点。
If you need the same transformations over and over, you can also use QTransform objects and the QPainter::worldTransform() and QPainter::setWorldTransform() functions. You can at any time save the QPainter’s transformation matrix by calling the QPainter::save() function which saves the matrix on an internal stack. The QPainter::restore() function pops it back.
如果你需要反复的的使用 相同的转换, 你也可以使用QTransform对象 和 QPainter::worldTransform() 和 QPainter::setWorldTransform() 函数。 你可以随时保存 QPainter的转换矩阵 通过调用QPainter::save()函数 这会把这个矩阵保存在内部的栈上。 QPainter::restore() 函数会出栈。
One frequent need for the transformation matrix is when reusing the same drawing code on a variety of paint devices. Without transformations, the results are tightly bound to the resolution of the paint device. Printers have high resolution, e.g. 600 dots per inch, whereas screens often have between 72 and 100 dots per inch.
一个常见的需求对于 转换矩阵 是 重用相同的绘图代码 在多个绘图设备上。 没有转换,结果与 绘图设备的分辨率 密切相关。 打印机有高的分辨率,比如:600点/英尺, 而屏幕在72-100/英尺。
Analog Clock Example 虚拟时钟例子
The Analog Clock example shows how to draw the contents of a custom widget using QPainter’s transformation matrix.
We recommend compiling and running this example before you read any further. In particular, try resizing the window to different sizes.
虚拟时钟例子 展示了 如何去 画自定义窗体的内容 使用QPainter的转换矩阵
我们建议编译和运行这个例子 在你更深层次阅读前。 特别是,尝试去设置窗体为不同尺寸
void AnalogClockWindow::render(QPainter *p)
{
static const QPoint hourHand[3] = {
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -40)
};
static const QPoint minuteHand[3] = {
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -70)
};
QColor hourColor(127, 0, 127);
QColor minuteColor(0, 127, 127, 191);
p->setRenderHint(QPainter::Antialiasing);
p->translate(width() / 2, height() / 2);
int side = qMin(width(), height());
p->scale(side / 200.0, side / 200.0);
We translate the coordinate system so that point (0, 0) is in the widget’s center, instead of being at the top-left corner. We also scale the system by side / 100, where side is either the widget’s width or the height, whichever is shortest. We want the clock to be square, even if the device isn’t.
我们平移了这个坐标系统 以便 point (0, 0)在窗体的中心, 而不是在左上角。 我们也缩放了 这个系统 通过side / 100 ,side是窗体的宽或高比较短的一个。 我们想要这个时钟是正方形的,即使设备不一定是。
This will give us a 200 x 200 square area, with the origin (0, 0) in the center, that we can draw on. What we draw will show up in the largest possible square that will fit in the widget.
这将给我们一个200x200的矩形区域, 原点(0,0)在中心, 我们可以在上面画。 我们画的将最大可能显示在这个区域 将适应这个窗体。
See also the Window-Viewport Conversion section.
也可以阅读 窗口-视口转换章节。
QTime time = QTime::currentTime();
p->save();
p->rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
p->drawConvexPolygon(hourHand, 3);
p->restore();
We draw the clock’s hour hand by rotating the coordinate system and calling QPainter::drawConvexPolygon(). Thank’s to the rotation, it’s drawn pointed in the right direction.
我们画了时钟 小时的手柄 通过旋转坐标系统 并且调用QPainter::drawConvexPolygon()函数。 幸亏这个旋转,画的点在正确的方向。
The polygon is specified as an array of alternating x, y values, stored in the hourHand static variable (defined at the beginning of the function), which corresponds to the four points (2, 0), (0, 2), (-2, 0), and (0, -25).
多边形用一个 数组点(x,y)来指定,储存在静态变量hourHand中(在函数的开始处定义), 这与4个点相符合(2, 0), (0, 2), (-2, 0), 和 (0, -25)
The calls to QPainter::save() and QPainter::restore() surrounding the code guarantees that the code that follows won’t be disturbed by the transformations we’ve used.
我们调用QPainter::save() 和QPainter::restore() 包围这个代码 以确保接下来的代码 不会被我们已经使用的转换影响。
p->save();
p->rotate(6.0 * (time.minute() + time.second() / 60.0));
p->drawConvexPolygon(minuteHand, 3);
p->restore();
We do the same for the clock’s minute hand, which is defined by the four points (1, 0), (0, 1), (-1, 0), and (0, -40). These coordinates specify a hand that is thinner and longer than the minute hand.
我们对于 时钟的分钟手柄 做同样的事, 被4个点定义(1, 0), (0, 1), (-1, 0), and (0, -40)。 这些定义了一个手柄 比hour手柄更细更长。
p->setPen(minuteColor);
for (int j = 0; j < 60; ++j) {
if ((j % 5) != 0)
p->drawLine(92, 0, 96, 0);
p->rotate(6.0);
}
Finally, we draw the clock face, which consists of twelve short lines at 30-degree intervals. At the end of that, the painter is rotated in a way which isn’t very useful, but we’re done with painting so that doesn’t matter.
最后,我们画时钟的面容, 这包含12个短线,30度的间隔。 在最后,painter被旋转到了 一个不是很有用 的位置, 但是我们已经完成了绘图 因此 没有关系。
For more information about the transformation matrix, see the QTransform documentation.
想要得到更多关于转换矩阵的信息,查看QTransform 文档。
Window-Viewport Conversion 窗口-视口转换
视口—物理指标
窗口—逻辑坐标
When drawing with QPainter, we specify points using logical coordinates which then are converted into the physical coordinates of the paint device.
当用QPainter画的时候, 我们指定点使用 逻辑坐标 然后转换为绘图设备的物理坐标
The mapping of the logical coordinates to the physical coordinates are handled by QPainter’s world transformation worldTransform() (described in the Transformations section), and QPainter’s viewport() and window(). The viewport represents the physical coordinates specifying an arbitrary rectangle. The “window” describes the same rectangle in logical coordinates. By default the logical and physical coordinate systems coincide, and are equivalent to the paint device’s rectangle.
逻辑坐标到物理坐标的映射是被 QPainter的世界转换worldTransform()【在转换章节中描述的】 和QPainter的viewport() 和 window() 操作的。 视口代表在物理坐标系上随意的矩形。 窗口代表在逻辑坐标系中同样的矩形。 默认的,逻辑和物理坐标系统一致, 并且相当于 绘图设备的矩形。
Using window-viewport conversion you can make the logical coordinate system fit your preferences. The mechanism can also be used to make the drawing code independent of the paint device. You can, for example, make the logical coordinates extend from (-50, -50) to (50, 50) with (0, 0) in the center by calling the QPainter::setWindow() function:
使用窗口-视口转换 你可以让 逻辑坐标系 适合你的偏爱。 这个机制也被用作 使绘图代码独立于绘图设备。 比如,你可以使逻辑坐标系从(-50, -50) 到 (50, 50) (0,0)在中间 通过调用QPainter::setWindow() 函数:
QPainter painter(this);
painter.setWindow(QRect(-50, -50, 100, 100));
Now, the logical coordinates (-50,-50) correspond to the paint device’s physical coordinates (0, 0). Independent of the paint device, your painting code will always operate on the specified logical coordinates.
现在,逻辑坐标系(-50,-50)代表 绘图设备的物理坐标系(0, 0)。 独立于绘图设备, 你的绘图代码 将总是工作在 指定的逻辑坐标系中。
By setting the “window” or viewport rectangle, you perform a linear transformation of the coordinates. Note that each corner of the “window” maps to the corresponding corner of the viewport, and vice versa. For that reason it normally is a good idea to let the viewport and “window” maintain the same aspect ratio to prevent deformation:
通过设置窗口或者视口矩形, 你执行了一个线性转换 在坐标系中。 注意窗口的每个角 映射于 视口的对应角,反之亦然。 因为这个原因, 让视口和窗口 保持相同的长宽比例 是个好主意 以防变形:
int side = qMin(width(), height())
int x = (width() - side / 2);
int y = (height() - side / 2);
painter.setViewport(x, y, side, side);
If we make the logical coordinate system a square, we should also make the viewport a square using the QPainter::setViewport() function. In the example above we make it equivalent to the largest square that fit into the paint device’s rectangle. By taking the paint device’s size into consideration when setting the window or viewport, it is possible to keep the drawing code independent of the paint device.
如果我们让逻辑坐标系是一个正方形, 我们也应该让视口成为一个正方形 使用QPainter::setViewport()函数。 在上面的例子中,我们把它和 设备矩形的最大面积 等同。设置窗口和视口时 考虑了设备尺寸 , 就可以让绘图代码 独立于 绘图设备。
Note that the window-viewport conversion is only a linear transformation, i.e. it does not perform clipping. This means that if you paint outside the currently set “window”, your painting is still transformed to the viewport using the same linear algebraic approach.
注意窗口与视口的转换 仅仅是一个线性转换, 也就是,他不会剪切。 这意味着如果你 画的超出当前的窗口, 你的画图仍然会被转换到 视口 使用同样的线性代数方法
The viewport, “window” and transformation matrix determine how logical QPainter coordinates map to the paint device’s physical coordinates. By default the world transformation matrix is the identity matrix, and the “window” and viewport settings are equivalent to the paint device’s settings, i.e. the world, “window” and device coordinate systems are equivalent, but as we have seen, the systems can be manipulated using transformation operations and window-viewport conversion. The illustration above describes the process.
视口,窗口和转换矩阵决定了QPainter逻辑坐标系 如何 映射到画图设备的物理坐标系。 默认的世界转换矩阵是单位矩阵, 窗口和视口的设置等同于绘图设备的设置, 也就是说,世界、窗口和设备坐标系是等同的, 但是正如我们所看到的, 系统可以被影响 通过使用转换操作和窗口-视口转换。 上面的图示描述了这个过程。
See also Analog Clock Window Example.
附录
代码例子 Analog Clock Window Example
Clock Window example
This example demonstrates how the transformation and scaling features of QPainter can be used to make drawing easier.
这个案例展示了如何 QPainter的转换和缩放特征 可以被使用 让绘图更容易。
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QTime>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
resize(200, 200);
}
Widget::~Widget()
{
delete ui;
}
//画图事件
//! [1] //! [14]
void Widget::paintEvent(QPaintEvent *e)
{
QPainter* p = new QPainter(this);
//! [14]
//! [8]
static const QPoint hourHand[3] = {
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -40)
};
static const QPoint minuteHand[3] = {
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -70)
};
QColor hourColor(127, 0, 127);
QColor minuteColor(0, 127, 127, 191);
//! [8]
//! [9]
p->setRenderHint(QPainter::Antialiasing);
//! [9] //! [10]
p->translate(width() / 2, height() / 2);
int side = qMin(width(), height());
p->scale(side / 200.0, side / 200.0);
//! [1] //! [10]
//! [11]
p->setPen(Qt::NoPen);
p->setBrush(hourColor);
//! [11]
//! [2]
QTime time = QTime::currentTime();
p->save();
p->rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
p->drawConvexPolygon(hourHand, 3);
p->restore();
//! [2]
//! [12]
p->setPen(hourColor);
for (int i = 0; i < 12; ++i) {
p->drawLine(88, 0, 96, 0);
p->rotate(30.0);
}
//! [12] //! [13]
p->setPen(Qt::NoPen);
p->setBrush(minuteColor);
//! [13]
//! [3]
p->save();
p->rotate(6.0 * (time.minute() + time.second() / 60.0));
p->drawConvexPolygon(minuteHand, 3);
p->restore();
//! [3]
//! [4]
p->setPen(minuteColor);
for (int j = 0; j < 60; ++j) {
if ((j % 5) != 0)
p->drawLine(92, 0, 96, 0);
p->rotate(6.0);
}
//! [4]
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
protected:
void paintEvent(QPaintEvent *e) override;
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
运行结果:
2024-02-16
对demo进行了分析: