QPainter提供绘图机制,可以在QPainterDevice上进行绘制,QPainterEngine提供不同设备的绘图接口,供以上内部使用,一般情况下用不到。
一般情况下通过重写QWidget中的
void QWidget::paintEvent(QPaintEvent *event)
方法来实现绘图
- QPen用于控制线条颜色,宽度,线型等
- QBrush设置一块区域的填充特性,颜色,填充方式,渐变类型
- QFont绘制文字,样式,大小,属性
- 其他还有旋转,缩放等功能。
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing); //反锯齿
p.setRenderHint(QPainter::TextAntialiasing); //文字反锯齿
int W = this->width();
int H = this->height();
QPen pen ;
QBrush brush;
QFont font ;
pen.setWidth(2); //宽度
pen.setColor(Qt::black); //颜色
pen.setStyle(Qt::PenStyle::SolidLine); //线的样式,实线,虚线等
pen.setCapStyle(Qt::PenCapStyle::FlatCap); //端点的样式
pen.setJoinStyle(Qt::PenJoinStyle::BevelJoin);//连接点的样式
brush.setColor(Qt::white); //画刷颜色
brush.setStyle(Qt::BrushStyle::SolidPattern); //画刷填充样式
p.setPen(pen);
p.setBrush(brush);
//p.drawEllipse(150,150,80,80);
QRect topRect(150,150,80,80); // 绘图区域
p.drawArc(topRect,16*0,16*180);
QRect underRect(150,150,80,110);
p.drawArc(underRect,16*180,180*16);
QRect leftEarRect(140,190,20,15);
QRect RightEarRect(220,190,20,15);
p.drawArc(leftEarRect,90*16,180*16);
p.drawArc(RightEarRect,270*16,180*16);
QRect browRect(160,170,30,30);
p.drawArc(browRect,45*16,120*16);
QRect rightbrowRect(190,170,30,30);
p.drawArc(rightbrowRect,25*16,120*16);
QRect eyeAreaRect(162,176,25,20);
p.drawArc(eyeAreaRect,0*16,360*16);
QRect eye2AreaRect(192,176,25,20);
p.drawArc(eye2AreaRect,0*16,360*16);
p.drawLine(190,200,180,220);
p.drawLine(180,220,190,220);
QRect mouth(175,210,30,30);
p.drawArc(mouth,210*16,120*16);
QPen pen2;
pen2.setColor(Qt::black);
p.setPen(pen2);
渐变填充
- QLinearGradient线性渐变
- QRadialGradient 辐射渐变和扩展辐射渐变
- QConicalGradient 圆锥形渐变
以上均继承自QGradent,可通过setSpread(QGradient::Spread method)
* PadSpread模式是用结束点的颜色填充外部区域,这是缺省的方式
* RepeatSpread重复使用渐变方式填充外部区域
- RelfectSpread反射式重复使用渐变方式填充外部区域
对圆锥型渐变不起作用
QRadialGradient radialGrad(W/2,H/2/*中心点*/,qMax(W/8,H/8)/*辐射填充区半径*/,W/2,H/2/*焦点坐标*/);
radialGrad.setColorAt(0,Qt::red); //0表示起点
// radialGrad.setColorAt(0.5,Qt::green);
radialGrad.setColorAt(1,Qt::yellow); //1表示终点
radialGrad.setSpread(QGradient::RepeatSpread);
p.setBrush(radialGrad);
当QGradient::QGradient时
使用线性渐变
QLinearGradient linearGradient(rect().topLeft(),rect().bottomRight());
linearGradient.setSpread(QGradient::RepeatSpread);
linearGradient.setColorAt(0,Qt::blue);
linearGradient.setColorAt(0.5,Qt::red);
linearGradient.setColorAt(1,Qt::green);
p.setBrush(linearGradient);
锥型渐变
QConicalGradient conicalGradient(W/2,H/2,45);
conicalGradient.setColorAt(0,Qt::blue);
conicalGradient.setColorAt(1,Qt::green);
p.setBrush(conicalGradient);
- 叠加模式–详情看文档
QPainterPath
可用于绘制一系列操作并保存,便于重复使用
QString rocketNames[3] = {"长江一号","长江二号","长江三号"}; //火箭数组
QPen pen ;
pen.setColor(Qt::blue);
pen.setWidth(3);
const QPoint rockets[8] = { //自制🚀的所有坐标点
QPoint(100,100),
QPoint(50,150),
QPoint(50,300),
QPoint(30,330),
QPoint(100,300),
QPoint(180,330),
QPoint(150,300),
QPoint(150,150)
};
QPainterPath path;
path.moveTo(rockets[0]);
for(int i = 0; i<8;i++){
path.lineTo(rockets[i]);
}
path.closeSubpath();
brush.setColor(Qt::red);
brush.setStyle(Qt::SolidPattern);
p.setBrush(brush);
// p.drawPath(path);
QFont serifFont("Times", 30, QFont::Bold);
{
qreal sxy = 0.6;
for(int i = 0, offset=0;i < 3;i++,offset+=300,sxy+=0.2){
p.resetTransform();
QPainterPath path2 = path;
path2.addText(rockets[0]+QPoint(-55,-15),serifFont,rocketNames[i]);
p.translate(QPointF(offset,0));
p.scale(sxy,sxy);
p.drawPath(path2);
}
}
Graphics View
QPainter适用于绘制一些简单的图形。不能实现图件的选择、编辑、拖放、修改等功能。
Graphics View是基于图形项的模型(model)视图(view)模式,类似于数据显示的model/view架构,由场景(QGraphicsScene),视图(QGraphicsView),图形项(QGraphicsItem)构成。
1. QGraphicsScene
不可见的一个管理图形项的容器,可以向场景中加入(获取)图形项,主要功能:
* 提供管理图形项的快速接口
* 将事件传播到每个图形项
* 管理每个图形项的状态
* 管理未经变换的渲染功能,主要用于打印
2. QGraphicsView
用于显示场景中的内容,可为一个场景设置几个视图,用于对同一个数据集提供不同的视口
视图接受鼠标键盘事件转化为场景事件,并进行坐标转换后传送给可视场景
3. QGraphicsItem
基本的图形元件,QGraphicsItem为图形项的基类,Qt提供了一些基本图形项,如QGraphicEllipseItem、QGraphicRectItem等。QGraphicsItem主要操作:
* 鼠标事件响应
* 键盘事件
* 拖放操作
* 支持组合,父子项关系组合或通过QGraphicsItemGroup类进行组合
若编写信号和槽,还能实现各种编辑功能
QGraphicsView 坐标系统
- 图形项坐标
- 使用自己的局部坐标,以其中心(0,0),鼠标事件是以局部坐标来表示创建自定义图形项,只需考虑局部坐标,Scene于View会进行自动转换
- 图形项的位置是其中心坐标,在父坐标中的坐标,如果没有父图形,父对象就是场景,位置就是场景中的坐标
- 父图形项坐标变换子图形进行相同的坐标变换
- 视图坐标
窗口的物理坐标,坐标只与widget或视口有关,与观察场景无关,QGraphicsView左上角总是(0,0)
所有的事件首先通过视图坐标定义,然后将这些坐标映射为场景坐标,便于与图形项交互。 -
场景坐标
所有图形项的基础坐标,描述了每个顶层Item的位置。
每个图形项在场景中都有一个位置坐标(QGraphicsView::scenePos()
);图形项边界矩形(QGraphicsView::sceneBoundingRect()
;场景发生变化会发出QGraphicsView::change
信号Demo
#include "GraphicsDemo.h"
#include <QLabel>
#include <QGraphicsItem>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
GraphicsDemo::GraphicsDemo(QWidget *parent)
: QMainWindow(parent)
{
ui->setupUi(this);
init();
}
GraphicsDemo::~GraphicsDemo() {}
void GraphicsDemo::init() {
sceneLabel = new QLabel("Scene coord:");
viewLabel = new QLabel("view coord:");
itemLabel = new QLabel("item coord:");
sceneLabel->setMinimumWidth(150);
viewLabel->setMinimumWidth(150);
itemLabel->setMinimumWidth(150);
statusBar()->addWidget(viewLabel);
statusBar()->addWidget(sceneLabel);
statusBar()->addWidget(itemLabel);
ui->graphicsView->setCursor(Qt::CrossCursor);
ui->graphicsView->setMouseTracking(true);
ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
QObject::connect(ui->graphicsView, &MyGraphicsView::mouseMovePoint, this,
&GraphicsDemo::on_mouseMovePoint);
QObject::connect(ui->graphicsView, SIGNAL(mousePressPoint(QPoint)), this,
SLOT(on_mousePressPoint(QPoint)));
initGriphicsItems();
}
void GraphicsDemo::on_mousePressPoint(QPoint point) {
QPointF scenePoint = ui->graphicsView->mapToScene(point);//映射场景坐标
QGraphicsItem *item = scene->itemAt(scenePoint,
ui->graphicsView->transform());
if (item != NULL) {
QPointF pointItem = item->mapFromScene(scenePoint); //图形象局部坐标
itemLabel->setText( QString::asprintf("item Coord:%.0f,%.0f",
pointItem.x(), pointItem.y()));
}
}
void GraphicsDemo::on_mouseMovePoint(QPoint point) {
viewLabel->setText(QString::asprintf("View Coord:%d,%d", point.x(),
point.y()));
QPointF scenePoint = ui->graphicsView->mapToScene(point); //映射场景坐标
sceneLabel->setText(QString::asprintf("Scene Coord:%.0f,%.0f",
scenePoint.x(), scenePoint.y()));
}
void GraphicsDemo::initGriphicsItems() {
QRectF rect(-200, -100, 400, 200); //左上角坐标,宽度,高度
scene = new QGraphicsScene(rect);
ui->graphicsView->setScene(scene);//设置场景
QGraphicsRectItem* item = new QGraphicsRectItem(rect);
item->setFlags(QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsFocusable);
QPen pen;
pen.setWidth(2);
item->setPen(pen);
scene->addItem(item);
QGraphicsEllipseItem* item2 = new QGraphicsEllipseItem(-100, -50, 200,
100);
item2->setPos(0, 0);
item2->setBrush(QBrush(Qt::blue));
item2->setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable
| QGraphicsItem::ItemIsFocusable);//可移动
scene->addItem(item2);
QGraphicsEllipseItem* item3 = new QGraphicsEllipseItem(-50, -50, 100, 100);
item3->setPos(rect.right(), rect.bottom());
item3->setBrush(Qt::green);
item3->setFlags(QGraphicsItem::ItemIsSelectable | //可选择
QGraphicsItem::ItemIsFocusable | //可获取焦点
QGraphicsItem::ItemIsMovable); //可移动
scene->addItem(item3);
scene->clearSelection();
}
void GraphicsDemo::resizeEvent(QResizeEvent* event)
{ //窗口变化大小时的事件
Q_UNUSED(event);
ui->label->setText(QString::asprintf("Graphics View coord,top left conner
always(0,0),width=%d,height=%d",
ui->graphicsView->width(), ui->graphicsView->height()));
QRectF rectF = ui->graphicsView->sceneRect(); //Scene的矩形区
ui->label_2->setText(QString::asprintf("QGraphicsView::sceneRect=(Left,Top,Width,Height)=%.0f,%.0f,%.0f,%.0f",
rectF.left(), rectF.top(), rectF.width(), rectF.height()));
}
(window原生界面是真丑啊)
view:setScene;Scene addItem
view 通过mapToScene将坐标转换为Scene,Scene通过mapFromScene将坐标转换到item