QPixmap QWidget::grab(const QRect &rectangle = QRect(QPoint(0, 0), QSize(-1, -1)))
Renders the widget into a pixmap restricted by the given rectangle. If the widget has any children, then they are also painted in the appropriate positions.
If a rectangle with an invalid size is specified (the default), the entire widget is painted.
QGraphicsProxyWidget* item = _scene->addWidget(new QLineEdit("hello"));
item->resize(50,50);
item->setPos(_scene->sceneRect().center());
item->setFlags(QGraphicsItem::ItemIsMovable|
QGraphicsItem::ItemIsSelectable|
QGraphicsItem::ItemIsFocusable);
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include
/** 可以移动QGraphicsProxyWidget的scene
* @brief The GraphicsScene class
*/
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = nullptr);
void setMoveMode(bool move){m_isMoveMode=move;}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
private:
QGraphicsItem *m_pItemSelected{Q_NULLPTR}; // 鼠标单选item
QPoint m_shiftOrg; // 鼠标点选item中位置相对于item原点的偏移量
bool m_isMoveMode{false};
};
#endif // GRAPHICSSCENE_H
#include "GraphicsScene.h"
#include
#include
#include
#include
#include
GraphicsScene::GraphicsScene(QObject *parent) : QGraphicsScene(parent)
{
}
// 鼠标按下获取当前单选中的QGraphicsProxyWidget图元对象
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// qDebug() << "scene mouse press";
QGraphicsScene::mousePressEvent(event);
//左键则弹出menu框
if (event->button() == Qt::RightButton){
m_pItemSelected = nullptr;
if(items(event->scenePos()).isEmpty())
{
QMenu *menu = new QMenu();
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addAction("editMode",this,[this](){
setMoveMode(false);
});
menu->addAction("moveMode",this,[this](){
setMoveMode(true);
});
menu->popup(event->screenPos());
}
}
//进入edit模式则直接退出
if(m_isMoveMode==false){
m_pItemSelected = nullptr;
return;
}
//进入move模式则不可编辑
if (event->button() == Qt::LeftButton)
{
// 检测光标下是否有 item
m_pItemSelected = nullptr;
foreach (QGraphicsItem *item, items(event->scenePos()))
{
if (item->type() == QGraphicsProxyWidget::Type) // 代理Widget
{
QGraphicsProxyWidget *proxyWidget = qgraphicsitem_cast<QGraphicsProxyWidget *>(item);
QPointF point = proxyWidget->mapToScene(QPointF(0.0, 0.0));
m_shiftOrg.setX(event->scenePos().x() - point.x());
m_shiftOrg.setY(event->scenePos().y() - point.y());
m_pItemSelected = item;
break;
}
}
}
}
// 鼠标移动过程中跟随位置改变
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
// qDebug() << "scene mouse move";
QGraphicsScene::mouseMoveEvent(event);
if(m_pItemSelected != nullptr)
{
m_pItemSelected->setPos(event->scenePos().x() - m_shiftOrg.x(), event->scenePos().y() - m_shiftOrg.y());
}
}
// 鼠标释放后作为最后的位置
void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
// qDebug() << "scene mouse release";
QGraphicsScene::mouseReleaseEvent(event);
if(m_pItemSelected != nullptr)
{
m_pItemSelected->setPos(event->scenePos().x() - m_shiftOrg.x(), event->scenePos().y() - m_shiftOrg.y());
m_pItemSelected = nullptr;
}
}
需要重写 QRectF boundingRect()
和 void paint()
函数
需要添加头尾两个item指针
头文件
#ifndef CONNECTLINE_H
#define CONNECTLINE_H
#include
class ConnectLine : public QGraphicsLineItem
{
public:
ConnectLine();
ConnectLine(QGraphicsItem* startitem, QGraphicsItem* enditem);
~ConnectLine();
double getlength(){return _length;}
private:
QGraphicsItem* _startItem;
QGraphicsItem* _endItem;
double _length=0;
// QGraphicsItem interface
public:
virtual QRectF boundingRect() const;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};
#endif // CONNECTLINE_H
源文件
#include "ConnectLine.h"
#include
#include
ConnectLine::ConnectLine()
{
}
ConnectLine::ConnectLine(QGraphicsItem *startitem, QGraphicsItem *enditem)
{
_startItem = startitem;
_endItem = enditem;
this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
}
ConnectLine::~ConnectLine()
{
}
QRectF ConnectLine::boundingRect() const
{
return QRectF(_startItem->pos()-QPointF(10,10),_endItem->pos()+QPointF(10,10));
}
void ConnectLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
// painter->drawLine(_startItem->pos(),_endItem->pos());
QPainterPath path;
path.moveTo(_startItem->pos());
path.cubicTo(_startItem->pos()+QPointF(50,0),_endItem->pos()-QPointF(50,0),_endItem->pos());
_length = path.length();
painter->drawPath(path);
}
知识点:
flags Qt::TextInteractionFlags
QGraphicsTextItem::setTextInteractionFlags(Qt::TextInteractionFlag::TextEditorInteraction);
头文件
#ifndef GRAPHICSEDITABLETEXTITEM_H
#define GRAPHICSEDITABLETEXTITEM_H
#include
class GraphicsEditableTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
GraphicsEditableTextItem();
GraphicsEditableTextItem(const QString& str);
// QGraphicsItem interface
protected:
void focusOutEvent(QFocusEvent *event) override;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
};
#endif // GRAPHICSEDITABLETEXTITEM_H
源文件
#include "GraphicsEditableTextItem.h"
GraphicsEditableTextItem::GraphicsEditableTextItem()
{
this->setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable|
QGraphicsItem::ItemIsSelectable);
}
GraphicsEditableTextItem::GraphicsEditableTextItem(const QString &str)
{
this->setPlainText(str);
this->setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable|
QGraphicsItem::ItemIsSelectable);
}
void GraphicsEditableTextItem::focusOutEvent(QFocusEvent *event)
{
this->setSelected(false);
if(this->textInteractionFlags()==Qt::TextInteractionFlag::TextEditorInteraction){
this->setTextInteractionFlags(Qt::TextInteractionFlag::NoTextInteraction);
}
QGraphicsTextItem::focusOutEvent(event);
}
void GraphicsEditableTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if(this->textInteractionFlags()==Qt::TextInteractionFlag::NoTextInteraction){
this->setTextInteractionFlags(Qt::TextInteractionFlag::TextEditorInteraction);
}
QGraphicsTextItem::mouseDoubleClickEvent(event);
}
// 绘制轮廓
void ConnectLine::drawOutline(QPainter *painter, QPainterPath path)
{
// 生成可填充的轮廓
QPainterPathStroker stroker;
stroker.setCapStyle(Qt::RoundCap); // 端点风格
stroker.setJoinStyle(Qt::RoundJoin); // 连接样式
stroker.setDashPattern(Qt::DashLine); // 虚线图案
stroker.setWidth(10); // 宽度
// 生成一个新路径(可填充区域),表示原始路径 path 的轮廓
QPainterPath outlinePath = stroker.createStroke(path);
// 绘制轮廓时所用的画笔(轮廓外边框灰色部分)
QPen pen = painter->pen();
pen.setColor(QColor(0, 160, 230));
pen.setWidth(10);
// 用指定的画笔 pen 绘制 outlinePath
// painter->strokePath(outlinePath, pen);
painter->setPen(pen);
painter->drawPath(outlinePath);
// 用指定的画刷 brush 填充路径 outlinePath
painter->fillPath(outlinePath, QBrush(Qt::yellow));
}
void MainWindow::on_pushButton_clicked()
{
QGraphicsItem* previtem=0;
qDebug()<<_scene->selectedItems();
if(!_scene->selectedItems().isEmpty()) {
previtem = _scene->selectedItems().at(0);
qDebug()<<"selected previtem";
}
GraphicsEditableTextItem* textitem = new GraphicsEditableTextItem("hello");
_scene->addItem(textitem);
if(previtem){
qDebug()<<"draw line";
textitem->setPos(previtem->pos()+QPointF(100,0));
ConnectLine* line = new ConnectLine(previtem,textitem);
_scene->addItem(line);
}
}
保存:
void MainWindow::on_actsave_triggered()
{
//保存为图片
QPixmap pic = ui->View->grab(scene->sceneRect().toRect());
pic.save("D:\\lesliex\\test.jpg");
qDebug()<<scene->items(scene->itemsBoundingRect());
//保存图元的数据
QJsonArray array;
for(auto item:scene->items(scene->itemsBoundingRect())){
QJsonObject obj;
obj.insert("x",item->pos().x());
obj.insert("y",item->pos().y());
obj.insert("type",item->type());
obj.insert("zValue",item->zValue());
array.append(obj);
}
QJsonDocument Doc(array);
QFile file("D:\\lesliex\\test.txt");
file.open(QIODevice::ReadWrite);
file.write(Doc.toJson());
file.close();
}
Json数据
[
{
"type": 4,//图元类型
"x": 325,//x坐标
"y": 277,//y坐标
"zValue": 0//z坐标
},
{
"type": 4,
"x": 297,
"y": 277,
"zValue": 0
}
]
读取:
void MainWindow::on_actload_triggered()
{
//读取图元的数据
QFile file("D:\\lesliex\\test.txt");
file.open(QIODevice::ReadWrite);
QByteArray data = file.readAll();
file.close();
QJsonDocument Doc(QJsonDocument::fromJson(data));
QJsonArray array(Doc.array());
for(auto i:array){
QJsonObject obj = i.toObject();
drawItem(obj["type"].toInt(),obj["x"].toDouble(),\
obj["y"].toDouble(),obj["zValue"].toInt());
}
}
还可以不使用json,使用QTextStream
内容:
Rectangle Rectangle #ff0000 198 401 207 50
Circle Circle #ff0000 117 160 307 128
解析
bool Document::load(QTextStream &stream)
{
m_shapeList.clear();
while (!stream.atEnd()) {//逐行操作
QString shapeType, shapeName, colorName;
int left, top, width, height;
//此处会以空白符分割一行,将字符串输入变量中
stream >> shapeType >> shapeName >> colorName >> left >> top >> width >> height;
if (stream.status() != QTextStream::Ok)
return false;
void GraphicsEditableTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setBrush(Qt::yellow);
painter->drawRect(this->boundingRect());
QGraphicsTextItem::paint(painter,option,widget);
}
void ConnectLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPointF startpos = mapFromItem(_startItem, _startItem->boundingRect().center());
QPointF endpos =mapFromItem( _endItem,_endItem->boundingRect().center());
painter->setPen(this->pen());
QPainterPath path;
path.moveTo(startpos);
if(startpos.x()>endpos.x())
path.cubicTo(startpos-QPointF(100,0),endpos+QPointF(100,0),endpos);
else
path.cubicTo(startpos+QPointF(100,0),endpos-QPointF(100,0),endpos);
painter->drawPath(path);
}
if(previtem){
qDebug()<<"draw line";
textitem->setPos(previtem->pos()+QPointF(100,0));
while(!textitem->collidingItems().isEmpty()){
textitem->setPos(textitem->collidingItems().first()->pos()+QPointF(0,100));
}
ConnectLine* line = new ConnectLine(previtem,textitem);
_scene->addItem(line);
item是根据 boundingRect 和 shape 来检测碰撞的,简单的方形图使用boundingRect 检测,复杂的如三角等使用 shape 来检测
scene->itemsBoundingRect()
为所有items的集合的总的大小
connect(_scene,&QGraphicsScene::changed,this,[this](const QList<QRectF>& area){
_scene->update();//使连线重画
if(_scene->sceneRect().width() < _scene->itemsBoundingRect().width() ||
_scene->sceneRect().height() < _scene->itemsBoundingRect().height() )
_scene->setSceneRect(_scene->itemsBoundingRect());
});
头文件
#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H
#include
QT_BEGIN_NAMESPACE
class QGraphicsLineItem;
QT_END_NAMESPACE
class MyGraphicsScene : public QGraphicsScene
{
public:
enum ITEMMODE{
LINEITEM,
LAST
};
explicit MyGraphicsScene(QObject *parent = nullptr);
void setmode(int mode){_mode = mode;}
private:
int _mode=LAST;
QGraphicsLineItem* _line{Q_NULLPTR};
// QGraphicsScene interface
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};
#endif // MYGRAPHICSSCENE_H
源文件
#include "MyGraphicsScene.h"
#include
#include
MyGraphicsScene::MyGraphicsScene(QObject *parent) : QGraphicsScene(parent)
{
}
void MyGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button()!=Qt::LeftButton)return;
if(_mode==LINEITEM){
_line = new QGraphicsLineItem(QLineF(event->scenePos(),event->scenePos()));//在点击处添加线段
this->addItem(_line);//在场景中加入线段
}else{
QGraphicsScene::mousePressEvent(event);
}
}
void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(_line!=0 && _mode==LINEITEM){
_line->setLine(QLineF(_line->line().p1(),event->scenePos()));
}else{
QGraphicsScene::mouseMoveEvent(event);
}
}
void MyGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(_line!=0 && _mode==LINEITEM){
QLineF newline = _line->line();
QGraphicsLineItem* newlineitem = new QGraphicsLineItem(newline);
this->addItem(newlineitem);
delete _line;
_line=0;
_mode = LAST;
}else{
QGraphicsScene::mouseReleaseEvent(event);
}
}
使用
//action可以设置为checkable
void MainWindow::on_actdrawline_triggered(bool checked)
{
if(checked) scene->setmode(MyGraphicsScene::LINEITEM);
else scene->setmode(MyGraphicsScene::LAST);
}
根据type插入item
item定位前随鼠标移动
头文件
#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H
#include
QT_BEGIN_NAMESPACE
class QGraphicsLineItem;
QT_END_NAMESPACE
class MyGraphicsScene : public QGraphicsScene
{
public:
enum ITEMMODE{
COMMONITEM,
LINEITEM,
BEZIERITEM,
SPECIALITEM,
LAST
};
enum SHAPE{
RECT,
ELLIPSE,
CIRCLE,
TRIANGLE,
POLYGON,
TEXT,
LINE,
BezierCur,
CONNECTLINE,
SHAPE_LAST
};
explicit MyGraphicsScene(QObject *parent = nullptr);
void setMode(int mode){_mode = mode;}
void setType(int type);
private:
int _mode=LAST;
//添加普通图元,获取图元类型
int _type=0;
QGraphicsItem* _curItem{Q_NULLPTR};//为实时追踪鼠标的图元
//画线
QGraphicsLineItem* _line{Q_NULLPTR};
//画贝塞尔曲线
// QGraphicsScene interface
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};
#endif // MYGRAPHICSSCENE_H
源文件
void MyGraphicsScene::setType(int type)
{//如果只使用已有形状判断的话,无法判断椭圆和圆的区别,还是自己写一个类型枚举的好
_type = type;
QGraphicsItem* item{Q_NULLPTR};
switch (_type) {
case SHAPE::RECT :
item = new QGraphicsRectItem(0,0,100,100);
break;
case SHAPE::CIRCLE :
item = new QGraphicsEllipseItem(0,0,100,100);
break;
case SHAPE::TRIANGLE :
item = new QGraphicsPolygonItem(QPolygonF(QVector<QPointF>{{0,0},{120,0},{0,-160}}));
break;
default:break;
}
this->addItem(item);
_curItem=item;
}
void MyGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button()!=Qt::LeftButton){//右键取消摆放
if(_curItem){
_mode = LAST;
delete _curItem;
_curItem=0;
}
return;
}
switch (_mode) {
case LINEITEM:
{
_line = new QGraphicsLineItem(QLineF(event->scenePos(),event->scenePos()));//在点击处添加线段
this->addItem(_line);//在场景中加入线段
}break;
case COMMONITEM:
{
// _mode = LAST;
_curItem->setFlags(QGraphicsItem::ItemIsMovable|
QGraphicsItem::ItemIsSelectable|
QGraphicsItem::ItemIsFocusable);
setType(_type);//使他连续创建,直到右键取消
}break;
default:
{
QGraphicsScene::mousePressEvent(event);
}break;
}
}
void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(_line!=0 && _mode==LINEITEM){
_line->setLine(QLineF(_line->line().p1(),event->scenePos()));
}else if(_mode == COMMONITEM){
//使鼠标在图形的中心点
_curItem->setPos(event->scenePos()-_curItem->boundingRect().center());
}
else{
QGraphicsScene::mouseMoveEvent(event);
}
}
给直线图元加两个控制点
重写控制点的图元:
#include
#include
class ContrlPoint : public QGraphicsItem
{
public:
ContrlPoint(QGraphicsItem* parent,QPointF pos);
// QGraphicsItem interface
public:
virtual QRectF boundingRect() const;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
QPointF _pos;
};
ContrlPoint::ContrlPoint(QGraphicsItem *parent, QPointF pos)
:QGraphicsItem(parent),_pos(pos)
{
this->setPos(_pos);
this->setFlags(QGraphicsItem::ItemIsMovable);
}
QRectF ContrlPoint::boundingRect() const{
return QRectF(0, 0, 10, 10);
}
void ContrlPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
// if(!this->parentItem()->isSelected())return;
painter->setPen(QColor(255,0,255));
painter->setBrush(QBrush(Qt::blue));
painter->drawRect(QRectF(0, 0, 10, 10));
}
重写线的图元:主要是添加一个控制点链表,然后根据图元与控制点的关系重写paint函数
#include
#include "ContrlPoint.h"
#include
class ContrlableLine : public QGraphicsLineItem
{
public:
ContrlableLine(const QLineF &line, QGraphicsItem *parent = nullptr);
// QGraphicsItem interface
public:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
QList<ContrlPoint*> _ctlPoints;
};
ContrlableLine::ContrlableLine(const QLineF &line, QGraphicsItem *parent)
{
QGraphicsLineItem(line, parent);
//添加控制点
_ctlPoints.append(new ContrlPoint(this,line.p1()));
_ctlPoints.append(new ContrlPoint(this,line.p2()));
}
void ContrlableLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
//线随着控制点变化
//painter->drawLine(_ctlPoints[0]->scenePos(),_ctlPoints[1]->scenePos());
//若要在中心点
painter->drawLine(mapFromItem(_ctlPoints[0],_ctlPoints[0]->boundingRect().center()),
mapFromItem(_ctlPoints[1],_ctlPoints[1]->boundingRect().center()));
}
在连线中加入控制点成为四阶贝塞尔曲线
static void createNBezierCurve(const QVector<QPointF> &src, QVector<QPointF> &dest, qreal precision)
{
///
}
ConnectLine::ConnectLine(QGraphicsItem *startitem, QGraphicsItem *enditem)
{
_startItem = startitem;
_endItem = enditem;
this->setFlags(QGraphicsLineItem::ItemIsSelectable | QGraphicsLineItem::ItemIsFocusable);
//添加控制点
_ctlPoints.append(new ContrlPoint(this,_startItem->pos()+QPointF(50,0)));
_ctlPoints.append(new ContrlPoint(this,QPointF((_startItem->pos().x()+_endItem->pos().x())/2,
(_startItem->pos().y()+_endItem->pos().y())/2)));
_ctlPoints.append(new ContrlPoint(this,_endItem->pos()-QPointF(50,0)));
}
QRectF ConnectLine::boundingRect() const
{
return QRectF(_startItem->pos()-QPointF(10,10),_endItem->pos()+QPointF(10,10));
}
void ConnectLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
// 根据控制点绘制贝塞尔曲线
QVector<QPointF> src;
src.append(_startItem->pos());
for(auto i:_ctlPoints)
src.append(i->pos());
src.append(_endItem->pos());
QVector<QPointF> dest;
createNBezierCurve(src,dest,0.01);
dest.insert(0,_startItem->pos());
dest.append(_endItem->pos());
QPainterPath path;
path.moveTo(_startItem->pos());
for(auto i:dest)path.lineTo(i);
QPen pen(QBrush(Qt::green),5);
painter->setPen(pen);
painter->drawPath(path);
QPainterPath pathAssist;
pathAssist.moveTo(_startItem->pos());
for(auto i:src)pathAssist.lineTo(i);
QPen penAssist(QBrush(Qt::yellow),3,Qt::DashLine);
painter->setPen(penAssist);
painter->drawPath(pathAssist);
}
需要判断线的端点处是否存在 item,首先需要在端点处清除线图元自身,然后再判断
void MyGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(_line!=0 && _mode==LINEITEM){
QLineF newline = _line->line();
QList<QGraphicsItem *> startitems = items(_line->line().p1());
if (startitems.count() && startitems.first() == _line)
startitems.removeFirst();
QList<QGraphicsItem *> enditems = items(_line->line().p2());
if (enditems.count() && enditems.first() == _line)
enditems.removeFirst();
this->removeItem(_line);
delete _line;
if(startitems.count() > 0 && enditems.count() > 0 && startitems.first() != enditems.first()){
ConnectLine* item = new ConnectLine(startitems.at(0),enditems.at(0));
this->addItem(item);
}else{
QGraphicsLineItem* newlineitem = new QGraphicsLineItem(newline);
this->addItem(newlineitem);
}
_line=0;
}else{
QGraphicsScene::mouseReleaseEvent(event);
}
}
头文件
#ifndef CONTRLPOINT_H
#define CONTRLPOINT_H
#include
class ContrlPoint : public QGraphicsItem
{
public:
//不同类型的控制点在移动时有不同的动作,对于不同的图元还需要判断类型执行相应操作
enum PointTYPE{
NORMAL,
RESIZE,
ROTATE,
SPECIAL,
LAST
};
ContrlPoint(QGraphicsItem* parent,QPointF pos,int type);
// QGraphicsItem interface
public:
virtual QRectF boundingRect() const;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
QPointF _pos;
int _type = LAST;
// QGraphicsItem interface
protected:
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};
#endif // CONTRLPOINT_H
源文件
#include "ContrlPoint.h"
#include
#include
#include
ContrlPoint::ContrlPoint(QGraphicsItem *parent, QPointF pos,int type)
:QGraphicsItem(parent),_pos(pos)
{
this->setPos(_pos);
this->setFlags(QGraphicsItem::ItemIsMovable|
QGraphicsItem::ItemIsSelectable|
QGraphicsItem::ItemIsFocusable);
_type = type;
}
QRectF ContrlPoint::boundingRect() const{
return QRectF(0, 0, 10, 10);
}
void ContrlPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
// if(!this->parentItem()->isSelected())return;
painter->setPen(QColor(255,0,255));
painter->setBrush(QBrush(Qt::blue));
painter->drawRect(QRectF(0, 0, 10, 10));
}
void ContrlPoint::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
switch (_type) {
case NORMAL:{
QGraphicsItem::mouseMoveEvent(event);
}break;
case ROTATE:{
QGraphicsItem* item = this->parentItem();
qreal nx = event->scenePos().x();
qreal ny = event->scenePos().y();
qreal ddx = nx - item->sceneBoundingRect().center().x();
qreal ddy = ny - item->sceneBoundingRect().center().y();
qreal angle = qAtan2(-ddy,-ddx)*180/3.14; //qAtan2()输出为reg圆周率数,不是度数
item->setRotation(angle);
//这里不添加QGraphicsItem::mouseMoveEvent(event);控制点就不会移动
}break;
default:{
QGraphicsItem::mouseMoveEvent(event);
break;
}
}
}
思路:画一个水平区域,检测出区域内即水平线上的各图元,将当前图元的“中点”和检测到的图元“中点”进行对比,若在范围内则将当前图元的y坐标重置,再将两个中点连线。垂直同上。注意对画的线要有合理的释放。
水平加垂直:
//使鼠标在图形的中心点
_curItem->setPos(event->scenePos() - _curItem->boundingRect().center());
if(_magLine) {delete _magLine; _magLine = 0;}
if(_magVLine) {delete _magVLine; _magVLine = 0;}
{//水平磁吸线
//磁吸线判断区域
//判断区域越小越好,最好所有在区域内的图元都在同一水平线上,这样相近的两个图元不会互相干扰
QRectF magarea(QPointF(this->sceneRect().left(),event->scenePos().y()-0.5),QSizeF(this->width(),1));
//判断区域内有无图元
QList<QGraphicsItem*> areaitems = items(magarea);
//去除掉自己,可增加去除掉控制点类型图元
// if(areaitems.contains(_curItem))areaitems.removeOne(_curItem);
areaitems.removeFirst();//需要去除初始产生的 0,0 位置图元,有bug
for(auto i:areaitems) if(i->type()==QGraphicsItem::UserType+11)areaitems.removeOne(i);
//判断有无图元
if(!areaitems.isEmpty()){
//因为判断的图元基本都在一条水平线,所以只要对准第一个图元即可
// QGraphicsItem* i = areaitems.first();
for(auto i:areaitems){
//根据鼠标y坐标和另一个图元判断,水平对齐
float y = event->scenePos().y();
float ny = i->sceneBoundingRect().center().y();
if(_magLine) {delete _magLine; _magLine = 0;}
//判断在范围内则将图元吸附到水平线上,范围要足够大最好是整个图元大小,这样保证判断区域内的图元都在同一水平线
if( y > ny-25 && y < ny+25){
_curItem->setPos(QPointF(_curItem->sceneBoundingRect().center().x(),ny) - _curItem->boundingRect().center());
//画线
_magLine = new QGraphicsLineItem(QLineF(_curItem->sceneBoundingRect().center(),
i->sceneBoundingRect().center()));
_magLine->setPen(QPen(Qt::DashLine));
this->addItem(_magLine);
}
}
}
}
{//垂直磁吸线
QRectF magarea(QPointF(event->scenePos().x()-0.5,this->sceneRect().top()),QSizeF(1,this->height()));
QList<QGraphicsItem*> areaitems = items(magarea);
areaitems.removeFirst();
for(auto i:areaitems) if(i->type()==QGraphicsItem::UserType+11)areaitems.removeOne(i);
if(!areaitems.isEmpty()){
for(auto i:areaitems){
float x = event->scenePos().x();
float nx = i->sceneBoundingRect().center().x();
if(_magVLine) {delete _magVLine; _magVLine = 0;}
if( x > nx-25 && x < nx+25){
_curItem->setPos(QPointF(nx,_curItem->sceneBoundingRect().center().y()) - _curItem->boundingRect().center());
_magVLine = new QGraphicsLineItem(QLineF(_curItem->sceneBoundingRect().center(),
i->sceneBoundingRect().center()));
_magVLine->setPen(QPen(Qt::DashLine));
this->addItem(_magVLine);
}
}
}
}
void Widget::saveToImage(const QString& imagePath)
{
int width = scene->width();
int height = scene->height();
QPixmap pixSaveImage(width, height);
pixSaveImage.fill(QColor(0, 0, 0, 0));//用透明色填充
QPainter painterTanns(&pixSaveImage);
painterTanns.setRenderHint(QPainter::Antialiasing, true);
painterTanns.setRenderHint(QPainter::TextAntialiasing, true);
painterTanns.setRenderHint(QPainter::SmoothPixmapTransform, true);
scene->clearSelection();
scene->render(&painterTanns);
pixSaveImage.save(imagePath);
return;
}