QT运用QGraphicsView框架实现矢量图形绘制软件
概要:
本样例使用 Qt中的QGraphicsView框架实现矢量图形绘制软件框架,本程序共分为4部分,其中DrawingUI矢量图形框架是主体应用程序,graphface是视图场景层库、shape是图元库、property是用来实现对属性编辑的自定义组件。
功能实现流程
要使用QGraphicsView框架实现一个矢量图形绘制功能,则需要如下步骤:
1)继承QGraphicsItem实现对应的图元基类ShapeBase_HX。
2)继承基类实现具体的图元类,在该文中我们将通过描述 ShapeLine_HX,ShapeRect_HX的实现来描述功能实现步骤。
3)在图元库中实现图元工厂。
4)实现场景类,通过继承QGraphicsScene实现我们自定义场景类,这样可以将场景限定在 编辑态或者是运行态,如: GraphScene_HX 编辑态场景类, GraphScene_Client运行态场景类。 然后我们通过一个外壳窗口来包含我们的场景,通过不同的选项,来应用编辑态或者运行态场景,这样就可以提供一个灵活的场景层库运用于编辑软件或者客户端软件。
5)实现属性编辑组件
6)实现我们的矢量图形框架Ui应用软件。主要实现图元菜单选项,包容场景层,属性编辑框等。
软件运行效果
启动
图元拖拽绘制
通过鼠标拖拽可完成图元缩放或移动
通过属性编辑框修改对应属性变化图元显示
可通过组块菜单实现图元组块,拆块菜单完成块拆分
可以通过鼠标拖拽块对应的拖拽句柄,可以完成块对象的缩放。
可以通过鼠标按下拖拽进行框选多个图元
可以通过修改窗口背景色属性,改变场景背景色
通过修改图元角度属性可以完成图元的旋转
还有很多功能,就不一一贴图,具体参看源码部分。
不多说了,直接上代码:
1、图元基类的实现
首先我们继承QGraphicsItem来实现一个 图元基类,这样可以统一管理我们的图元库以及预留扩展,避免图元基类的不同,导致在实例化时需要使用不同的创建工厂对象,从而导致编码复杂度增加。
基类头文件如下:
#ifndef SHAPEBASE_HX_H
#define SHAPEBASE_HX_H
#include "shape_global.h"
#include "shapebase_def_hx.h"
#include <QGraphicsItem>
#include <QDomDocument>
#include <QDomElement>
#include <map>
using std::map;
#include "../property/propertybasedef_hx.h"
/*
* @date: 2020/3/4
* @author wxj
* @func 全部矢量图形图元的基类
*/
using namespace wxj;
#define HitOffset 30
#define HitShapeOffset 5
#define HitShapeOffsetWidth 10
#define extendVal 20 //拓扑关联检查的模糊范围值
typedef map<int, QColor> StateColorMap; //拓扑图元,拓扑线状态颜色键值对容器【map】
//图元选中后呈现的拖拽句柄位置标号
enum DRAG_HANDLE {
DRAG_UNKNOWN = -1,
DRAG_LEFT_TOP = 0,
DRAG_TOP,
DRAG_RIGHT_TOP,
DRAG_RIGHT,
DRAG_RIGHT_BOTTOM,
DRAG_BOTTOM,
DRAG_LEFT_BOTTOM,
DRAG_LEFT,
//DRAG_SELECT
};
class ShapeBase_HX;
//拓扑线与拓扑图元的拓扑点位置以及拓扑线的起点标志---使用在拓扑图元中记录
typedef struct _DIRECTTOPOREC { //点方向--主要用于记录图元拓扑点归属于拓扑线的首端或者末端
QPointF topologyPoint; //拓扑线在拓扑图元的关联点
bool isHead; //拓扑线于拓扑图元关联点是首端还是尾端
ShapeBase_HX* topologyObj; //拓扑线对象
QPointF offset; //用来临时保存xy偏差量的点, 目前仅仅在在拓扑图元中会被使用。
}TopoRec, *ptrTopoRec;
#define SELHandleSize 10.00
#define SELHandleSize_HALF 5.00 //中间的句柄使用
#define SELHandleSize_LeftTop 2.00 //靠左边的靠上边的句柄使用
#define SELHandleSize_RightBotom 8.00 //靠右边的靠下边的句柄使用
class SHAPESHARED_EXPORT ShapeBase_HX :
public QGraphicsItem
{
public:
//构造
ShapeBase_HX(QGraphicsItem * parent = nullptr );
//返回对象的类型编号
virtual int objClassID() const { return unknown; }
//返回对象的类名字符串
virtual const char* objClassStr() const { return "UNKNOWN_CLASS"; }
public:
//对外接口
//仅在第一次生成对象时使用,改变对象数据, MouseST是标志是鼠标当前处于什么状态
virtual void changData_new (QPointF & rp ,MouseST mouseST);
//设置对象成员数据的接口-根据成员对应得属性名称
virtual void setMemberData(const char * mbname, QVariant & data) = 0;
//根据对象成员属性名称返回对象相对应属性的数据值
virtual QVariant getMemberData(const char * mbname) = 0;
//获取图元的外框矩形【针对各种类型图元提供公共的精准外框数据接口--注意为了统一,该接口提供的是场景坐标数据】
virtual QRectF getOutlineBorder() { QRectF rt; return rt; }
//返回对象属性列表
virtual QList <PropertyItemData> getPropertyList() = 0;
//返回对象的扩展属性列表,假如有扩展属性的则需要重写
virtual QList <PropertyItemData> getExtendPropertyList() { QList <PropertyItemData> list; return list; }
//数据存储函数--直接存储xml写入方式--类似于自定义写入方式
virtual void saveData(QDomDocument& doc, QDomElement& dom) = 0;
//图元数据读取【加载XML元素结点】函数
virtual void loadData(QDomElement& dom) = 0;
//克隆函数
virtual ShapeBase_HX * clone() = 0;
//等比改变外框矩形数据--所有图元均可缩放,具体图元具体算法
virtual void zoomChangedData(qreal sx, qreal sy) = 0;
//获取角度
double getAngle() const {return m_angle;}
//设置角度
void setAngle (double angle)
{
setTransformOriginPoint (0,0);
setRotation (-m_angle);
setRotation (angle);
m_angle = angle;
}
//叠加角度
void addAngle (double angle_e)
{
setTransformOriginPoint (0,0);
setRotation (-m_angle);
setRotation (m_angle+angle_e);
m_angle += angle_e;
}
//获取因为视图针对场景的位置的图元保存需要的偏移量
void getViewOffset(QPoint &orgPt, double &_offsetx, double &_offsety);
//设置拓扑对象接口,有需要进行拓扑对象关联的才进行重写
virtual void setTopologyObj(ShapeBase_HX* _topologyObj, QPointF _topologyPt, bool _bIsHead) {};
//移除拓扑关联对象【指定移除,全部移除】 ,_removeFlag=false代表单个关联移除【_topologyObj作为关键值指定】,_removeFlag=true整体关联移除
virtual void removeTopologyObj(bool _removeAll, ShapeBase_HX* _topologyObj) {_removeAll = 0; _topologyObj = 0; } //消除警告
//检查更新拓扑关联--目前主要是拓扑线需要重写该函数
virtual void topologyRelevanceCheck() {};
//获取拓扑关联的对象列表
virtual QList <ShapeBase_HX*> getTopologyList() { QList <ShapeBase_HX*> pTopoList; return pTopoList; }
//该接口是使用在可以被拓扑关联的对象中,用来检测本对象的拓扑点位置的接口,参数_checkPt是传入的场景坐标拓扑检测点【通过放大范围,去检测相交位置】,
//rangeVal是_checkPt的放大检测范围,retPt是检测到的返回拓扑点
virtual bool checkTopologyPoint(QPointF _checkPt, int rangeVal, QPointF &retPt) { return false; }
//该接口是为了某些子图元做特殊动作时调用,该接口用在复合图元中,如gif动画图元,在用户做删除动作时,其实在内存中没有删除,
//仅仅是将图元移除场景,但是针对动画显示的控件没有被关闭,因此需要调用该特殊动作处理函数来关闭动画显示组件。
virtual void specificActions(QString _actionFlag) {}
public:
//计算直线的外围平行矩形区域
static QPolygonF caculatePolygon(QPointF & sp, QPointF & ep);
//对应用系统的窗口尺寸进行记录与初始化 -- 虽然该处存在默认值-但是还是应该在程序启动图形模块时进行初始化,通常由graph库来完成初始化
static void initRec_winsize (int _width, int _height) {g_width=_width; g_height=_height;}
//计算直线与矩形的交点, rect是本检测矩形,pt是外部点,pt与矩形中心点形成直线,返回则是线与矩形的交互点
static QPointF LineCutRect(QRectF & rect, QPointF & pt);
//计算直线与矩形的交点, rect是本检测矩形,_spt是外部点,_ept落在矩形范围内,返回则是_spt到_ept的 直线与矩形的交互点
static QPointF calculLineCutRect(QRectF & _rect, QPointF & _spt, QPointF & _ept); //
//根据直线两点计算直线的外围矩形--与直线包容平行的矩形
static void calculateRegionRef(qreal Vx, qreal Hx, qreal x1, qreal y1, qreal x2, qreal y2, qreal &ax1, qreal &ay1,
qreal &ax2, qreal &ay2, qreal &ax3, qreal &ay3, qreal &ax4, qreal &ay4);
//计算点是否在矩形范围内--注意矩形与坐标系必须一致方可以进行计算测试
static bool hitRect(const QRectF &_rect, const QPointF _hitPt);
protected:
//对象的缺省大小设置--当在手工绘制图元时,大小小于一定程度调用这个函数来设置图元外观数据以便图元能被查看
virtual void defaultData () {}
protected:
double m_angle; //角度值
public:
//对全局的状态颜色映射的默认初始化,如果有另外的状态颜色映射关系,则自行进行初始化,主要用于拓扑图元、拓扑线图元等的状态颜色码表
static StateColorMap& initTopologyColorMap() {
g_TopologyColorMap[0] = QColor(127,127,127);
g_TopologyColorMap[1] = QColor(0,255,0);
g_TopologyColorMap[2] = QColor(255,0,0);
return g_TopologyColorMap;
}
//投运状态的状态颜色码表初始化
static StateColorMap& initDeviceRunstateColorMap() {
g_deviceRunstateColorMap[0] = QColor(127, 127, 127); //停运
g_deviceRunstateColorMap[1] = QColor(0, 255, 0); //投运
g_deviceRunstateColorMap[2] = QColor(255, 234, 0); //热备 浅橙绿
g_deviceRunstateColorMap[3] = QColor(255, 0, 255); //检修 橙色
return g_TopologyColorMap;
}
static StateColorMap& initStaticMacroColorMap() {
g_StaticMacroColorMap[0] = QColor(127, 127, 127); //设备静态宏设置到设备
g_StaticMacroColorMap[1] = QColor(0, 255, 0); //设备静态宏参数未设置到设备
return g_StaticMacroColorMap;
}
static QBrush getGradientBrush(const QRectF &_rect, const QColor& _fillColor, FillTheWay _fillTheWay);
public:
static int dragHandle; //用来临时保留鼠标按下击中了对象拖放句柄的句柄索引--因为针对不同类型的类对象句柄数不定
//因此不能固定使用枚举量,如折线,矩形,直线几种类图元的拖拽句柄数不一致
//改变对象的拖拽句柄--当等于-1时,表明不在拖拽句柄上。
static int estopHit; //禁止击中对象标志--用在对象第一次拖拽生成对象时候
static QPointF offset; //临时使用的点变量
static int g_width; //显示器的宽度像素--注意,虽然该处存在默认值-但是还是应该在程序启动图形模块时进行初始化
static int g_height; //显示器的高度像素--注意,虽然该处存在默认值-但是还是应该在程序启动图形模块时进行初始化
static QPointF g_tempPoint; //临时保存点
static QRectF g_tmpRect; //临时保存矩形
public:
//全局码表区
static StateColorMap g_TopologyColorMap; //该键值对映射是用在记录拓扑图元的状态颜色的
static StateColorMap g_deviceRunstateColorMap; //该键值码表是标识 投运状态的码表值容器
static StateColorMap g_TopologyLineColorMap; //该键值码表是标识 拓扑线相关的的码表值容器 【对应的图元包含,拓扑直线,折线型拓扑线】
static StateColorMap g_StaticMacroColorMap; //用在静态宏监测时,设备图元的颜色动画呈现值设定
};
#endif // SHAPEBASE_HX_H
基类CPP文件内容如下:
#include "shapebase_hx.h"
#include <QGraphicsScene>
#include "drawmath_hx.h"
#include <cmath>
#include <QRadialGradient>
//静态全局变量
int ShapeBase_HX::dragHandle = -1; //-1代表 DRAG_UNKNOWN,未击中拖拽句柄
int ShapeBase_HX::estopHit = -1; //-1代表禁止击中
int ShapeBase_HX::g_width = 1366; //显示器的默认宽度像素
int ShapeBase_HX::g_height = 768; //显示器的默认高度像素
QPointF ShapeBase_HX::offset = QPointF(0.0,0.0);
QPointF ShapeBase_HX::g_tempPoint = QPointF(-1, -1); //临时保存点--为了去掉GVarsDataset_HX中的对应的点,所以在这里声明了一个
QRectF ShapeBase_HX::g_tmpRect = QRectF(0, 0, 0, 0); //临时保存矩形
StateColorMap ShapeBase_HX::g_TopologyColorMap;
StateColorMap ShapeBase_HX::g_deviceRunstateColorMap;
StateColorMap ShapeBase_HX::g_TopologyLineColorMap;
StateColorMap ShapeBase_HX::g_StaticMacroColorMap;
//ShapeBase_HX基类实现体代码部分
ShapeBase_HX::ShapeBase_HX(QGraphicsItem * parent )
: QGraphicsItem (parent) ,m_angle(0.0)
{
setFlags(ItemIsSelectable | ItemIsMovable);
}
void ShapeBase_HX::changData_new (QPointF & rp , MouseST mouseST)
{
switch (mouseST)
{
case ST_MOUSEPRESS:
{
setPos (rp);
estopHit = 1;//在对象是第一次生成时,设置标志为1 用来保证这时候这个对象不能被击中(shape函数中不能被击中)
}
break;
case ST_MOUSEMOVE:
{
}
break;
case ST_MOUSERELEASE:
{
estopHit = -1;//这里标志对象已经初始生成完毕,清除首次生成标志,对象可以被击中
setSelected(1);
}
break;
case ST_MOUSEUNKNOWN: //不处理
break;
}
}
void ShapeBase_HX::getViewOffset(QPoint &orgPt, double &_offsetx, double &_offsety)
{
//获取显示器的宽度高度像素
int diswidth = g_width;
int disheight = g_height;
_offsetx = orgPt.rx();
_offsety = orgPt.ry();
if (diswidth > scene()->width())
{
_offsetx = 0;
}
if (disheight > scene()->height())
{
_offsety = 0;
}
QPointF tp,sp;
}
//根据直线两点计算直线的外围矩形
QPolygonF ShapeBase_HX::caculatePolygon(QPointF & sp, QPointF & ep)
{
QPolygonF ploygon;
qreal x1, x2, y1, y2;
x1 = sp.rx();
x2 = ep.rx();
y1 = sp.ry();
y2 = ep.ry();
qreal vx = 8.0, hx = 8.0;
qreal rsx1, rsy1, rsx2, rsy2, rsx3, rsy3, rsx4, rsy4;
//根据直线两点计算直线的外围矩形--与直线包容平行的矩形
calculateRegionRef(vx, hx, x1, y1, x2, y2, rsx1, rsy1, rsx2, rsy2, rsx3, rsy3, rsx4, rsy4); //很奇怪,在qtcreator中尽然无法调用算法类中静态函数 ?
ploygon.append(QPointF(rsx1, rsy1));
ploygon.append(QPointF(rsx3, rsy3));
ploygon.append(QPointF(rsx4, rsy4));
ploygon.append(QPointF(rsx2, rsy2));
return ploygon;
}
//pt是矩形外部的一个点
QPointF ShapeBase_HX::LineCutRect(QRectF & rect, QPointF & pt)
{
QPointF point;
QPointF pt1, pt2, pt3, pt4;
QPointF cpt = rect.center(); //中心点
qreal ptrx = pt.rx(); //外部点
qreal ptry = pt.ry();
qreal cptrx = cpt.rx();
qreal cptry = cpt.ry();
if (!(abs(ptrx - cptrx)<0.00001 )) //这里是不等于
{
qreal kfix = qreal(rect.height()) / qreal(rect.width());
qreal k = qreal((ptry - cptry)) / qreal((ptrx - cptrx));
if (-1 * kfix < k && k < kfix)
{
if (cptrx < ptrx)
{
point.setX(rect.right());
point.setY(qreal(rect.right() - cptrx)*k + cptry);
}
else
{
point.setX(rect.left());
point.setY(qreal(rect.left() - cptrx)*k + cptry);
}
}
else
{
if (cptry < ptry)
{
point.setY(rect.bottom());
point.setX(qreal(rect.bottom() - cptry) / k + cptrx);
}
else
{
point.setY(rect.top());
point.setX(qreal(rect.top() - cptry) / k + cptrx);
}
}
}
else //等于
{
point.setX(ptrx);
if (ptry < cptry)
{
point.setY(rect.top());
}
else
{
point.setY(rect.bottom());
}
}
return point;
}
//注意,计算交点,输入的数据要处于统一坐标系,一般统一到场景坐标
QPointF ShapeBase_HX::calculLineCutRect(QRectF& _rect, QPointF& _spt, QPointF& _ept)
{
QPointF point;
QPointF pt1, pt2, pt3, pt4;
QPointF cpt = _ept; //中心点
qreal ptrx = _spt.rx(); //外部点
qreal ptry = _spt.ry();
qreal cptrx = cpt.rx();
qreal cptry = cpt.ry();
if (!(abs(ptrx - cptrx)<0.00001)) //这里是不等于
{
qreal kfix = qreal(_rect.height()) / qreal(_rect.width());
qreal k = qreal((ptry - cptry)) / qreal((ptrx - cptrx));
if (-1 * kfix < k && k < kfix)
{
if (cptrx < ptrx)
{
point.setX(_rect.right());
point.setY(qreal(_rect.right() - cptrx)*k + cptry);
}
else
{
point.setX(_rect.left());
point.setY(qreal(_rect.left() - cptrx)*k + cptry);
}
}
else
{
if (cptry < ptry)
{
point.setY(_rect.bottom());
point.setX(qreal(_rect.bottom() - cptry) / k + cptrx);
}
else
{
point.setY(_rect.top());
point.setX(qreal(_rect.top() - cptry) / k + cptrx);
}
}
}
else //等于
{
point.setX(ptrx);
if (ptry < cptry)
{
point.setY(_rect.top());
}
else
{
point.setY(_rect.bottom());
}
}
return point;
}
//计算直线的指定(x,y)偏移外围区域
void ShapeBase_HX::calculateRegionRef (qreal Vx, qreal Hx, qreal x1, qreal y1, qreal x2, qreal y2,
qreal &ax1, qreal &ay1, qreal &ax2, qreal &ay2, qreal &ax3, qreal &ay3, qreal &ax4, qreal &ay4)
{
//竖直
double xval = abs(x2 - x1);
//if (abs(x2 - x1) == 0) //本句原意
if (!(xval>0 || xval<0))
{
if (y2 > y1)
{
ax1 = x1 - Vx;
ay1 = y1 - Hx;
ax2 = x1 + Vx;
ay2 = y1 - Hx;
ax3 = x1 - Vx;
ay3 = y2 + Hx;
ax4 = x1 + Vx;
ay4 = y2 + Hx;
}
else
{
ax1 = x1 - Vx;
ay1 = y2 - Hx;
ax2 = x1 + Vx;
ay2 = y2 - Hx;
ax3 = x1 - Vx;
ay3 = y1 + Hx;
ax4 = x1 + Vx;
ay4 = y1 + Hx;
}
return;
}
qreal k = qreal(y2 - y1)/qreal(x2 - x1);
qreal angle = atan(k);
//线段长度的一半
//float halfLenth = float(sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)))/2;
//中点
qreal mx = (x2 + x1)/2;
qreal my = (y2 + y1)/2;
qreal alf;
qreal cx1,cy1,cx2,cy2;
alf = (-1)*angle;
cx1 = qreal(cos(alf) * (x1 - mx) - sin(alf) * (y1 - my) + mx);
cy1 = qreal(sin(alf) * (x1 - mx) + cos(alf) * (y1 - my) + my);
cx2 = qreal(cos(alf) * (x2 - mx) - sin(alf) * (y2 - my) + mx);
cy2 = qreal(sin(alf) * (x2 - mx) + cos(alf) * (y2 - my) + my);
qreal bx1, by1,bx2, by2,bx3, by3,bx4, by4;
if (x1>x2)
{
bx1 = cx1 + Hx;
by1 = cy1 + Vx;
bx2 = cx1 + Hx;
by2 = cy1 - Vx;
bx3 = cx2 - Hx;
by3 = cy2 + Vx;
bx4 = cx2 - Hx;
by4 = cy2 - Vx;
}
else
{
bx1 = cx1 - Hx;
by1 = cy1 + Vx;
bx2 = cx1 - Hx;
by2 = cy1 - Vx;
bx3 = cx2 + Hx;
by3 = cy2 + Vx;
bx4 = cx2 + Hx;
by4 = cy2 - Vx;
}
qreal theta = angle;
ax1 = qreal(cos(theta) * (bx1 - mx) - sin(theta) * (by1 - my) + mx);
ay1 = qreal(sin(theta) * (bx1 - mx) + cos(theta) * (by1 - my) + my);
ax2 = qreal(cos(theta) * (bx2 - mx) - sin(theta) * (by2 - my) + mx);
ay2 = qreal(sin(theta) * (bx2 - mx) + cos(theta) * (by2 - my) + my);
ax3 = qreal(cos(theta) * (bx3 - mx) - sin(theta) * (by3 - my) + mx);
ay3 = qreal(sin(theta) * (bx3 - mx) + cos(theta) * (by3 - my) + my);
ax4 = qreal(cos(theta) * (bx4 - mx) - sin(theta) * (by4 - my) + mx);
ay4 = qreal(sin(theta) * (bx4 - mx) + cos(theta) * (by4 - my) + my);
}
bool ShapeBase_HX::hitRect(const QRectF &_rect, const QPointF _hitPt)
{
return _rect.contains(_hitPt);
}
QBrush ShapeBase_HX::getGradientBrush(const QRectF &_rect, const QColor& _fillColor, FillTheWay _fillTheWay) {
QBrush brush;
switch (_fillTheWay) {
case ST_FILLUNKNOWN:
case ST_DONTFILL: {
brush = QBrush(Qt::NoBrush);
}break;
case ST_NORMALFILL: {
brush = QBrush(_fillColor); //正常填充
}break;
case ST_GradientHorizontalFILL: {
QColor fillColor = _fillColor.dark(80);
QLinearGradient fade(0, 0, _rect.width(), 0); //横向渐变
fade.setSpread(QGradient::ReflectSpread);
fade.setColorAt(0, fillColor.light(150));
fade.setColorAt(1, fillColor.dark(180));
brush = QBrush(fade);
}break;
case ST_GradientVerticalFILL: {
QColor fillColor = _fillColor.dark(80);
QLinearGradient fade(0, 0, 0, _rect.height()); //纵向渐变
fade.setSpread(QGradient::ReflectSpread);
fade.setColorAt(0, fillColor.light(150));
fade.setColorAt(1, fillColor.dark(180));
brush = QBrush(fade);
}break;
case ST_GradientLeftDiagonalFILL: {
QColor fillColor = _fillColor.dark(80);
QLinearGradient fade(0, 0, _rect.width(), _rect.height()); //左上对角线渐变 左上角右下角
fade.setSpread(QGradient::ReflectSpread);
fade.setColorAt(0, fillColor.light(150));
fade.setColorAt(1, fillColor.dark(180));
brush = QBrush(fade);
}break;
case ST_GradientRightDiagonalFILL: {
QColor fillColor = _fillColor.dark(80);
QLinearGradient fade(_rect.right(), _rect.top(), 0, _rect.height()); //对角线渐变 右上角左下角
fade.setSpread(QGradient::ReflectSpread);
fade.setColorAt(0, fillColor.light(150));
fade.setColorAt(1, fillColor.dark(180));
brush = QBrush(fade);
}break;
case ST_GradientRadiation2FILL: { //2级梯度渐变
QColor fillColor = _fillColor.dark(80);
QRadialGradient gradient(0, 0, _rect.width());
gradient.setCenter(_rect.center());
gradient.setFocalPoint(_rect.center());
gradient.setColorAt(1, fillColor.light(180));
gradient.setColorAt(0, fillColor.dark(180));
brush = QBrush(gradient);
}break;
case ST_GradientRadiation5FILL: { //5级梯度辐射渐变
QColor fillColor = _fillColor.dark(80);
qreal centerX = _rect.center().rx();
qreal centerY = _rect.center().ry();
qreal rangVal = _rect.height() > _rect.width() ? _rect.height() : _rect.width();
QRadialGradient radialGradient(centerX, centerY, rangVal, centerX, centerY);
radialGradient.setColorAt(0, fillColor.light(250));
radialGradient.setColorAt(0.2, fillColor.light(200));
radialGradient.setColorAt(0.4, fillColor.light(150));
radialGradient.setColorAt(0.6, fillColor.light(100));
radialGradient.setColorAt(1.0, fillColor.dark(100));
brush = QBrush(radialGradient);
}break;
case ST_ConicalGradientFILL: { //角度渐变
QColor fillColor = _fillColor.dark(80);
QConicalGradient conicalGradient(_rect.center(), 0);
//创建了一个QConicalGradient对象实例,参数分别为中心坐标和初始角度
conicalGradient.setColorAt(0, fillColor);
conicalGradient.setColorAt(0.2, fillColor.light(40));
conicalGradient.setColorAt(0.4, fillColor.light(90));
conicalGradient.setColorAt(0.6, fillColor.light(150));
conicalGradient.setColorAt(0.8, fillColor.light(70));
conicalGradient.setColorAt(1.0, fillColor);
brush = QBrush(conicalGradient);
}break;
}
return brush;
}
直线类ShapeLine_HX简述
我们已经实现了基类后,接下来我们来实现直线类ShapeLine_HX, 先贴出头文件,然后我们在来一一讲解成员函数
#ifndef H_ShapeLine_HX_H_
#define H_ShapeLine_HX_H_
#pragma once
#include "shapebase_hx.h"
/*
* @date: 2020/4/17
* @author wxj
* @func 直线图元对象类,直线绘制类
*/
class ShapeLine_HX :
public ShapeBase_HX
{
public:
ShapeLine_HX(void);
virtual ~ShapeLine_HX(void);
virtual const int objClassID() { return xline; }
virtual const char* objClassStr() const { return "ShapeLine_HX"; }
public:
//设置对象得成员数据-根据成员对应得属性名称
virtual void setMemberData (const char * mbname, QVariant & data);
//根据对象成员属性名称返回对象相对应属性的数据值
virtual QVariant getMemberData (const char * mbname);
//返回对象属性列表
virtual QList <PropertyItemData> getPropertyList();
//数据存储函数
virtual void saveData (QDomDocument& doc, QDomElement& dom);
//数据读取函数
virtual void loadData (QDomElement& dom);
//改变外框矩形数据
virtual void zoomChangedData(qreal sx, qreal sy);
//对象克隆函数
virtual ShapeBase_HX * clone();
//获取对象的精准外框矩形数据
virtual QRectF getOutlineBorder();
private:
virtual void changData_new (QPointF & rp ,MouseST mouseST);
//重新调整中心位置,并且将4点坐标值调整正确
virtual void justPosAndData();
protected:
//必须重载的3个虚函数,不重载则会导致不能对象实例化
QRectF boundingRect() const;
QPainterPath shape() const; //击中范围设置
void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void defaultData();
//绘制对象的选中拖拽框
void drawSelectedHandle (QPainter *painter);
// 返回改变对象的拖拽位置点 测试鼠标是击中在对象的哪个拖拽位置点上,并且返回对象要被改变的点位置索引
// 注意:针对直线类型双点对象获取的拖拽句柄不能按 DRAG_HANDLE定义解释,只能按0起点,1终点来解释
int getDragHandle(QPointF &point);
//获取画笔线宽度
int penWidth () ;
//获取线型
int penStyle ();
//设置对象线宽
virtual void setPenWidth (int width) ;
//设置对象线颜色
virtual void setLineColor (QColor & color) {m_lineColor=color;}
//设置对象线型
virtual void setPenStyle (int penstyle) ;
protected:
vector<QPointF> m_points; //直线的起点与终点数据
unsigned char m_penWidth; //线宽
unsigned char m_penStyleNumb; //线型
unsigned int m_flag; //包含直线线宽(0,1,2,3,4,位标识),直线对象的线型(5,6,7位标识线型)
QColor m_lineColor;
};
#endif //H_ShapeLine_HX_H_
ShapeLine_HX 类
1)成员用途简述
vector<QPointF> m_points; 该成员决定我们的直线如何绘制,实际就是起点数据与终点数据。
m_penWidth决定绘制线时线宽度,m_penStyleNumb 是绘制线型,也就是绘制实心线,或者点划线、或者虚线等等,
unsigned int m_flag; 目前没有使用了,忽略即可(早期因为内存小的原因,尽量压缩成员以便于节约空间)。
QColor m_lineColor; 决定来线绘制出来的颜色。
2)成员函数用途简述
a、首先,因为我们的图元是从QGraphicsItem继承而来,因此有3个纯虚函数必须实现,这三个函数分别是:
QRectF boundingRect() const;
QPainterPath shape() const; //击中范围设置
void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget);
boundingRect函数决定图元对象刷新时的矩形区域大小。
shape()函数决定对象的击中范围,举例来说,一条直线,我们可以简单的设置它的击中范围为直线的外框矩形,但是这样会造成,当一条直线穿过屏幕矩形的对角线时,那么整个屏幕都可以击中这条直线,因此这样设置击中范围是不可取的。
paint函数是用来根据自定义图元数据来绘制真实绘制的图元形状的。
b、对象是通过鼠标操作来完成初始绘制,因此我们需要实现鼠标相关的3个函数,分别是
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
mousePressEvent函数中主要完成,鼠标按下标志的记录,直线起点的记录,以及记录鼠标偏移点起点。
mouseMoveEvent函数中主要完成对直线对象的外形数据改变,在新建直线时,鼠标按下时就生成了直线对象,也就是起点终点都是鼠标按下点位置,在mouseMoveEvent中,如果鼠标处于按下状态,此时鼠标移动则改变直线的终点数据,从而改变了直线外观。
mouseReleaseEvent函数主要是完成直线数据改变的确认,根据直线对象当前数据重新调整直线对象的中心位置数据。
c、数据改变接口,数据获取接口,主要包含
void setMemberData (const char * mbname, QVariant & data);
QVariant getMemberData (const char * mbname);
void zoomChangedData(qreal sx, qreal sy);
setMemberData 通过属性名称字符串查找到对应属性,然后设置数据到对应成员。
getMemberData 通过属性名称字符串查找返回对应的成员值。
zoomChangedData改变外框矩形数据,直线在块中缩放时会用到。
//返回对象属性列表,主要应用在属性编辑
virtual QList <PropertyItemData> getPropertyList();
d、数据保存加载相关接口
//数据存储函数
virtual void saveData (QDomDocument& doc, QDomElement& dom);
//数据读取函数
virtual void loadData (QDomElement& dom);
e、较为特殊的内部函数
//changData_new 函数主要用在对象首次创建时的数据改变。
virtual void changData_new (QPointF & rp ,MouseST mouseST);
//重新调整中心位置
virtual void justPosAndData();
//绘制对象的选中拖拽框,在对象处于选中状态时,需要提示鼠标按在什么位置可以对直线对象进行改变,因此绘制出拖拽矩形框
void drawSelectedHandle (QPainter *painter);
// 返回改变对象的拖拽位置点 测试鼠标是击中在对象的哪个拖拽位置点上,并且返回对象要被改变的点位置索引
// 注意:针对直线类型双点对象获取的拖拽句柄不能按 DRAG_HANDLE定义解释,只能按0起点,1终点来解释
int getDragHandle(QPointF &point);
f、基础设置函数
//获取画笔线宽度
int penWidth () ;
//获取线型
int penStyle ();
//设置对象线宽
virtual void setPenWidth (int width) ;
//设置对象线颜色
virtual void setLineColor (QColor & color) {m_lineColor=color;}
//设置对象线型
virtual void setPenStyle (int penstyle) ;
g、为了使用原型模式工厂,因此对象需要实现clone()函数接口
//对象克隆函数
virtual ShapeBase_HX * clone();
下面是ShapeLine_HX 类.cpp
#include "shapeline_hx.h"
#include <QPainter>
#include <QtWidgets>
#include "gvarsdataset_hx.h"
#include "drawmath_hx.h"
#include <vector>
#include <algorithm> //常规算法头文件
using std::sort;
using std::vector;
ShapeLine_HX::ShapeLine_HX(void):m_lineColor(GVarsDataset_HX::defaultLineColor()),m_flag(0)
{
m_points.resize(2);
this->setPenStyle(1);
this->setPenWidth(2);
m_angle = 0.0;
}
ShapeLine_HX::~ShapeLine_HX(void)
{
}
void ShapeLine_HX::changData_new (QPointF & rp, MouseST mouseST)
{
switch (mouseST)
{
case ST_MOUSEPRESS:
{
this->setPos (rp);
QPointF startPt(0, 0);
m_points[0] = startPt; //添加起点与终点到m_points中,在这里生成实际的起点与终点数据
m_points[1] = startPt;
estopHit = 1; //在对象是第一次生成时,设置标志为1 用来保证这时候这个对象不能被击中(shape函数中不能被击中)
}
break;
case ST_MOUSEMOVE:
{
ShapeBase_HX::g_tempPoint = this->mapFromScene(rp);
m_points.at(1) = ShapeBase_HX::g_tempPoint; //move出改变第二点
}
break;
case ST_MOUSERELEASE:
{
estopHit = -1;
ShapeBase_HX::g_tempPoint = this->mapFromScene(rp);
m_points.at(1) = ShapeBase_HX::g_tempPoint; //move出改变第二点
QPointF cp1 = this->mapToScene(m_points.at(0)); //转换到场景坐标--第一点
QPointF cp2 = this->mapToScene(m_points.at(1)); //转换到场景坐标--第二点
QPointF npos;
npos = QPointF((cp1.rx() + cp2.rx()) / 2, (cp1.ry() + cp2.ry()) / 2);
//设置计算后的新中心坐标
this->setPos(npos);
m_points.at(0) = this->mapFromScene(cp1); //根据重新设定的中点数据重新转换获得正确的第一点数据
m_points.at(1) = this->mapFromScene(cp2); //根据重新设定的中点数据重新转换获得正确的第二点数据
this->setSelected(1);
defaultData();
}
break;
case ST_MOUSEUNKNOWN: //不处理
break;
}
update();
}
//返回刷新区域
QRectF ShapeLine_HX::boundingRect() const
{
qreal adjust = 20;
// 这里是根据更改了的点数据重新计算对象的绘制刷新范围
qreal x1, x2, y1, y2;
QPointF sp = m_points[0];
QPointF ep = m_points[1];
x1 = sp.rx();
x2 = ep.rx();
y1 = sp.ry();
y2 = ep.ry();
QRectF rect;
if (y1<y2)
{
rect.setTop(y1 - adjust);
rect.setBottom(y2 + adjust);
}
else
{
rect.setTop(y2 - adjust);
rect.setBottom(y1 + adjust);
}
if (x1<x2)
{
rect.setLeft(x1 - adjust);
rect.setRight(x2 + adjust);
}
else
{
rect.setLeft(x2 - adjust);
rect.setRight(x1 + adjust);
}
return rect;
}
//这里是击中对象的检测范围函数
QPainterPath ShapeLine_HX::shape() const
{
QPainterPath path;
if (ShapeBase_HX::estopHit == 1)
return path;
QPolygonF ploygon;
qreal x1, x2, y1, y2;
QPointF sp = m_points[0];
QPointF ep = m_points[1];
x1 = sp.rx();
x2 = ep.rx();
y1 = sp.ry();
y2 = ep.ry();
qreal vx = 8.0, hx = 8.0;
qreal rsx1, rsy1, rsx2, rsy2, rsx3, rsy3, rsx4, rsy4;
//根据直线两点计算直线的外围矩形--与直线包容平行的矩形
calculateRegionRef(vx, hx, x1, y1, x2, y2, rsx1, rsy1, rsx2, rsy2, rsx3, rsy3, rsx4, rsy4);
ploygon.append(QPointF(rsx1, rsy1));
ploygon.append(QPointF(rsx3, rsy3));
ploygon.append(QPointF(rsx4, rsy4));
ploygon.append(QPointF(rsx2, rsy2));
path.addPolygon(ploygon);
return path;
}
void ShapeLine_HX::drawSelectedHandle (QPainter *painter)
{
qreal x1, x2, y1, y2;
QPointF sp = m_points[0];
QPointF ep = m_points[1];
x1 = sp.rx();
x2 = ep.rx();
y1 = sp.ry();
y2 = ep.ry();
painter->drawRect(x1 - HitShapeOffset, y1 - HitShapeOffset, HitShapeOffsetWidth, HitShapeOffsetWidth);
painter->drawRect(x2 - HitShapeOffset, y2 - HitShapeOffset, HitShapeOffsetWidth, HitShapeOffsetWidth);
}
// 返回改变对象的拖拽位置点
int ShapeLine_HX::getDragHandle(QPointF &point)
{
qreal x1, x2, y1, y2;
QPointF sp = m_points[0];
QPointF ep = m_points[1];
x1 = sp.rx();
x2 = ep.rx();
y1 = sp.ry();
y2 = ep.ry();
QRectF t(x1 - HitShapeOffset, y1 - HitShapeOffset, HitShapeOffsetWidth, HitShapeOffsetWidth);
if (t.contains(point))
return 0;
QRectF t1(x2 - HitShapeOffset, y2 - HitShapeOffset, HitShapeOffsetWidth, HitShapeOffsetWidth);
if (t1.contains(point))
return 1;
return -1;
}
int ShapeLine_HX::penWidth ()
{
return m_penWidth;
}
void ShapeLine_HX::setPenWidth (int width)
{
m_penWidth = width;
};
int ShapeLine_HX::penStyle ()
{
return m_penStyleNumb;
}
void ShapeLine_HX::setPenStyle (int penstyle)
{
m_penStyleNumb = penstyle;
}
void ShapeLine_HX::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mousePressEvent(event); //默认函数会为本图元添加状态QStyle::State_Selected并且调用update更新界面
update();
QPointF tpt = event->pos();
ShapeBase_HX::g_tempPoint = tpt;
ShapeBase_HX::dragHandle = getDragHandle(tpt);
offset = tpt;
if (ShapeBase_HX::dragHandle == -1)
offset = this->pos();
}
void ShapeLine_HX::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (GVarsDataset_HX::isClient()) {
return;
}
if (event->modifiers() & Qt::ShiftModifier)
{
update();
return;
}
if (ShapeBase_HX::dragHandle != -1)
{
QPointF pos = event->pos();
switch (ShapeBase_HX::dragHandle)
{
case 0:
{
m_points[0] = pos;
//将直线的item坐标保存转换到场景坐标,保存起来,待重新设定中心位置后
//在将保存的直线场景坐标在新的中心位置转换为item坐标
//1、将item坐标转换为场景坐标
QPointF cp1 = this->mapToScene(m_points[0]);
QPointF cp2 = this->mapToScene(m_points[1]);
//2、使用转换后获取的场景线坐标计算Item中心位置
QPointF npos;
npos = QPointF((cp1.rx() + cp2.rx()) / 2, (cp1.ry() + cp2.ry()) / 2);
//3、设置计算后的新中心坐标到本Item
this->setPos(npos);
//将保存的直线场景坐标在新的中心位置转换为item坐标
m_points[0] = this->mapFromScene(cp1);
m_points[1] = this->mapFromScene(cp2);
}
break;
case 1:
{
m_points[1] = pos;
//将直线的item坐标保存转换到场景坐标,保存起来,待重新设定中心位置后
//在将保存的直线场景坐标在新的中心位置转换为item坐标
//1、将item坐标转换为场景坐标
QPointF cp1 = this->mapToScene(m_points[0]);
QPointF cp2 = this->mapToScene(m_points[1]);
//2、使用转换后获取的场景线坐标计算Item中心位置
QPointF npos;
npos = QPointF((cp1.rx() + cp2.rx()) / 2, (cp1.ry() + cp2.ry()) / 2);
//3、设置计算后的新中心坐标到本Item
this->setPos(npos);
//将保存的直线场景坐标在新的中心位置转换为item坐标
m_points[0] = this->mapFromScene(cp1);
m_points[1] = this->mapFromScene(cp2);
}
break;
}
update();
}
else
QGraphicsItem::mouseMoveEvent(event);
}
void ShapeLine_HX::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (ShapeBase_HX::dragHandle != -1)
{
offset = event->pos() - offset;
QPointF posPt = event->pos();
switch (ShapeBase_HX::dragHandle)
{
case 0:
{
m_points[0] = posPt;
}
break;
case 1:
{
m_points[1] = posPt;
}
break;
}
//将直线的item坐标保存转换到场景坐标,保存起来,待重新设定中心位置后
//在将保存的直线场景坐标在新的中心位置转换为item坐标
//1、将item坐标转换为场景坐标
QPointF cp1 = this->mapToScene(m_points[0]);
QPointF cp2 = this->mapToScene(m_points[1]);
//2、使用转换后获取的场景线坐标计算Item中心位置
QPointF npos;
npos = QPointF((cp1.rx() + cp2.rx()) / 2, (cp1.ry() + cp2.ry()) / 2);
//3、设置计算后的新中心坐标到本Item
this->setPos(npos);
//将保存的直线场景坐标在新的中心位置转换为item坐标
m_points[0] = this->mapFromScene(cp1);
m_points[1] = this->mapFromScene(cp2);
}
else
{
if (this->pos().x() != offset.x() && this->pos().y() != offset.y())
{
offset = this->pos() - offset; //通知外部变化偏移量
//GVarsDataset_HX::emitnotify(this->scene(), 0, offset, 3);
}
}
ShapeBase_HX::dragHandle = -1;
QGraphicsItem::mouseReleaseEvent(event);
update();
}
void ShapeLine_HX::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
QColor penColor = (option->state & QStyle::State_Selected) ? m_lineColor.dark(150) : m_lineColor;
QPen pen;
int width = penWidth();
int penstyle = penStyle();
pen.setStyle(Qt::PenStyle(penstyle));
if (option->state & QStyle::State_Selected)
{
penColor = m_lineColor.dark(150);
}
else
{
penColor = m_lineColor;
}
if (option->state & QStyle::State_MouseOver)
{
penColor = m_lineColor.light(85);
width += 2;
}
pen.setColor(penColor);
pen.setWidth(width);
painter->setPen(pen);
QPen oldPen = painter->pen();
QBrush oldBrush = painter->brush();
if (option->state & QStyle::State_Selected)
width += 2;
pen.setWidth(width);
QPointF sp = m_points[0];
QPointF ep = m_points[1];
painter->drawLine(sp, ep);
if (option->state & QStyle::State_Selected)
{
drawSelectedHandle(painter);
}
}
void ShapeLine_HX::setMemberData (const char * mbname, QVariant & data)
{
if (strcmp(mbname, pty_penWidth) == 0)
{
setPenWidth(data.toInt());
}
else if (strcmp(mbname, pty_penStyle) == 0)
{
int x = data.toInt();
setPenStyle(x);
switch (x)
{
case 0:
setPenStyle(Qt::SolidLine);
break;
case 1:
setPenStyle(Qt::DashLine);
break;
case 2:
setPenStyle(Qt::DotLine);
break;
case 3:
setPenStyle(Qt::DashDotLine);
break;
case 4:
setPenStyle(Qt::DashDotDotLine);
break;
case 5:
setPenStyle(Qt::CustomDashLine);
break;
default:
setPenStyle(Qt::SolidLine);
break;
}
this->update(this->boundingRect());
}
else if (strcmp(mbname, pty_lineColor) == 0)
{
QColor color = data.value<QColor>();
setLineColor(color);
}
else if (strcmp(mbname, pty_posx) == 0)
{
QPointF npos;
npos = this->pos();
//设置计算后的新中心坐标
qreal x = data.toDouble();
npos.setX(x);
this->setPos(npos);
}
else if (strcmp(mbname, pty_posy) == 0)
{
QPointF npos;
npos = this->pos();
//设置计算后的新中心坐标
qreal y = data.toDouble();
npos.setY(y);
this->setPos(npos);
}
else if (strcmp(mbname, pty_startx) == 0)
{
m_points[0].setX(data.toDouble());
}
else if (strcmp(mbname, pty_endx) == 0)
{
m_points[1].setX(data.toDouble());
}
else if (strcmp(mbname, pty_starty) == 0)
{
m_points[0].setY(data.toDouble());
}
else if (strcmp(mbname, pty_endy) == 0)
{
m_points[1].setY(data.toDouble());
}
else if (strcmp(mbname, pty_endPoint) == 0)
{
QPointF tp = data.value<QPointF>();
QPointF tp1 = mapFromScene(tp);
m_points[1] = tp1;
}
else if (strcmp(mbname, pty_startPoint) == 0)
{
QPointF tp = data.value<QPointF>();
QPointF tp1 = mapFromScene(tp);
m_points[0] = tp1;
}
else if (strcmp(mbname, pty_angle) == 0)
{
double angleVal = data.toDouble();
setAngle(angleVal);
}
else if (strcmp(mbname, pty_layerNum) == 0)
{
int z = data.toInt();
this->setZValue(z);
}
this->update (this->boundingRect());
}
QVariant ShapeLine_HX::getMemberData (const char * mbname)
{
if (strcmp(mbname, pty_penWidth) == 0)
{
return penWidth();
}
else if (strcmp(mbname, pty_penStyle) == 0)
{
return penStyle();
}
else if (strcmp(mbname, pty_lineColor) == 0)
{
return m_lineColor;
}
else if (strcmp(mbname, pty_posx) == 0)
{
QPointF npos;
npos = this->pos();
return npos.x();
}
else if (strcmp(mbname, pty_posy) == 0)
{
QPointF npos;
npos = this->pos();
return npos.y();
}
else if (strcmp(mbname, pty_startx) == 0)
{
return m_points[0].rx();
}
else if (strcmp(mbname, pty_endx) == 0)
{
return m_points[1].rx();
}
else if (strcmp(mbname, pty_starty) == 0)
{
return m_points[0].ry();
}
else if (strcmp(mbname, pty_endy) == 0)
{
return m_points[1].ry();
}
else if (strcmp(mbname, pty_startPoint) == 0)
{
return mapToScene(m_points[0]); //转换为场景坐标返回
}
else if (strcmp(mbname, pty_endPoint) == 0)
{
return mapToScene(m_points[1]); //转换为场景坐标返回
}
else if (strcmp(mbname, pty_angle) == 0)
{
return m_angle;
}
else if (strcmp(mbname, pty_OutlineorderRect) == 0)
{
return getOutlineBorder();
}
return QVariant(QVariant::Invalid);
}
void ShapeLine_HX::saveData (QDomDocument& doc, QDomElement& dom)
{
QDomElement gele = doc.createElement("line");
dom.appendChild(gele);
QString str;
QString points;
QPointF pos = this->pos();
//直接保存对象pos点数据,降低保存以及加载解析难度
gele.setAttribute(pty_posx, pos.rx());
gele.setAttribute(pty_posy, pos.ry());
gele.setAttribute(pty_penWidth, m_penWidth); //线宽
gele.setAttribute(pty_penStyle, m_penStyleNumb); //线型
int count = m_points.size(); //保存折线型拓扑线的点数据
for (int i = 0; i < count; ++i)//需要保存为场景坐标
{
//转换为场景
QPointF scencePt = mapToScene(m_points[i]);
str = QString("%1,%2").arg(scencePt.rx()) //保存一个点的x,y值,该处直接保存的是对象坐标值,加载时无需转换【因为已经保存了对象位置坐标】
.arg(scencePt.ry());
points += str;
points += " ";
}
gele.setAttribute(pty_pointlist, points);
QString tstr = QString("%1").arg(QVariant(m_lineColor).toString()); //保存线的颜色
gele.setAttribute(pty_lineColor, tstr);
}
void ShapeLine_HX::loadData (QDomElement& dom)
{
QPointF pos;
//加载图元对象的位置数据
if (dom.hasAttribute(pty_posx))
{
pos.setX(dom.attribute(pty_posx).toDouble());
}
if (dom.hasAttribute(pty_posy))
{
pos.setY(dom.attribute(pty_posy).toDouble());
}
setPos(pos);
//加载图元对象的线宽线型
if (dom.hasAttribute(pty_penWidth)) {
m_penWidth = dom.attribute(pty_penWidth).toInt();
}
if (dom.hasAttribute(pty_penStyle)) {
m_penStyleNumb = dom.attribute(pty_penStyle).toInt();
}
//加载线图元的点数据
if (dom.hasAttribute(pty_pointlist)) {
QString points = dom.attribute(pty_pointlist);
//这里按;符号来分割,分割出来的每个字符串是一个点的字符串
QStringList strlist = points.split(" ", QString::SkipEmptyParts);
QString tstr;
for (int i = 0; i < strlist.size(); ++i)
{
tstr = strlist.at(i);
QStringList pplist = tstr.split(",", QString::SkipEmptyParts);
QPointF point;
point.setX(pplist.at(0).toDouble());
point.setY(pplist.at(1).toDouble());
m_points[i] = mapFromScene(point); //注意因为对象在初始化的时候已经分配了2点的空间,因此不能使用push_back
}
}
//加载线的颜色
QString lineColorStr = dom.attribute(pty_lineColor);
m_lineColor = QVariant(lineColorStr).value<QColor>();
}
QList <PropertyItemData> ShapeLine_HX::getPropertyList()
{
QList <PropertyItemData> lists;
PropertyItemData posX;
posX.nam = ShpaeGBKStr("中心位置X");
posX.val = pos().x();
posX.type = FloatProperty;
posX.indexName = pty_posx;
lists.append(posX);
PropertyItemData posY;
posY.nam = ShpaeGBKStr("中心位置Y");
posY.val = pos().y();
posY.type = FloatProperty;
posY.indexName = pty_posy;
lists.append(posY);
PropertyItemData lineColorX;
lineColorX.nam = ShpaeGBKStr("笔颜色");
lineColorX.val = m_lineColor;
lineColorX.type = ColorProperty;
lineColorX.indexName = pty_lineColor;
lists.append(lineColorX);
PropertyItemData angleX;
angleX.nam = ShpaeGBKStr("旋转角度");
angleX.val = m_angle;
angleX.type = IntProperty;
angleX.indexName = pty_angle;
lists.append(angleX);
PropertyItemData lineWidth;
lineWidth.nam = ShpaeGBKStr("线宽");
lineWidth.val = penWidth();
lineWidth.type = IntProperty;
lineWidth.indexName = pty_penWidth;
lists.append(lineWidth);
PropertyItemData lineStyle;
lineStyle.nam = ShpaeGBKStr("线型");
lineStyle.val = penStyle();
lineStyle.type = LineTypeProperty;
lineStyle.indexName = pty_penStyle;
lists.append(lineStyle);
PropertyItemData layerNum;
layerNum.nam = ShpaeGBKStr("图层");
layerNum.val = this->zValue();
layerNum.type = IntProperty;
layerNum.indexName = pty_layerNum;
lists.append(layerNum);
return lists;
}
ShapeBase_HX * ShapeLine_HX::clone()
{
ShapeLine_HX * cloneObj = new ShapeLine_HX(); //为了在对象内部不包含创建工厂,因此这里直接使用new
cloneObj->m_lineColor = m_lineColor;
cloneObj->m_points[0] = m_points[0];
cloneObj->m_points[1] = m_points[1];
cloneObj->m_flag = m_flag;
cloneObj->setAngle(m_angle);
cloneObj->setPos(pos());
return cloneObj;
}
void ShapeLine_HX::zoomChangedData(qreal sx, qreal sy)
{
m_points[0].setX(m_points[0].rx()*sx);
m_points[0].setY(m_points[0].ry()*sy);
m_points[1].setX(m_points[1].rx()*sx);
m_points[1].setY(m_points[1].ry()*sy);
}
void ShapeLine_HX::defaultData()
{
QPointF tpt1 = m_points[0];
QPointF tpt2 = m_points[1];
if (DrawMath_HX::Distance(tpt1, tpt2) < 10.000)
{
m_points[0] = QPointF(-50, -20);
m_points[1] = QPointF(50, 20);
justPosAndData();
}
}
QRectF ShapeLine_HX::getOutlineBorder()
{
vector<qreal> yVec;
vector<qreal> xVec;
int count = m_points.size();
for (int i = 0; i <count; ++i) {
xVec.push_back(m_points[i].rx());
yVec.push_back(m_points[i].ry());
}
//按从小到大排序
sort(xVec.begin(), xVec.end(), less<qreal>());
sort(yVec.begin(), yVec.end(), less<qreal>());
QPointF topLeft(xVec[0], yVec[0]); //最小数据组成点
QPointF bottomRight(xVec.back(), yVec.back()); //按最大数据组成点
//转换为场景坐标
topLeft = mapToScene(topLeft);
bottomRight = mapToScene(bottomRight);
QRectF rt;
rt.setTopLeft(topLeft);
rt.setBottomRight(bottomRight);
return rt;
}
//重新调整中心位置,并且将起点与终点坐标值调整正确
void ShapeLine_HX::justPosAndData()
{
//在将保存的直线场景坐标在新的中心位置转换为item坐标
QPointF cp1 = this->mapToScene(m_points[0]);
QPointF cp2 = this->mapToScene(m_points[1]);
QPointF npos;
npos = QPointF((cp1.rx() + cp2.rx()) / 2, (cp1.ry() + cp2.ry()) / 2);
//设置计算后的新中心坐标
this->setPos(npos);
//将保存的直线场景坐标在新的中心位置转换为item坐标
m_points[0] = this->mapFromScene(cp1);
m_points[1] = this->mapFromScene(cp2);
}
矩形类ShapeRect_HX简述
介绍完直线对象,接下来我们来看看矩形图元.下面是ShapeRect_HX,先贴出头文件,然后我们在来一一讲解成员函数
#ifndef H_ShapeRect_HX_H__
#define H_ShapeRect_HX_H__
#pragma once
#include "shapebase_hx.h"
/*
* @date: 2020/3/6
* @date: 2020/5/26 重新修订该保存加载算法
* @author wxj
* @func 矩形图元
注:矩形的外观数据保存方式为,将其左上角坐标转换为场景坐标后保存为x,y属性,并且保存矩形宽度高度值,在加载时将左上角x,y场景值解析出,
解析宽度高度值,然后通过左上角场景值与宽度高度计算出矩形中心位置,然后设置该图元的位置,然后将左上角场景值转换为本地坐标设置到矩形的左上角。
然后设置矩形的宽度高度值,矩形图元的外形数据加载完毕。
*/
class ShapeRect_HX:
public ShapeBase_HX
{
public:
ShapeRect_HX(void);
virtual ~ShapeRect_HX(void);
virtual int objClassID() const { return xrect; }
virtual const char* objClassStr() const { return "ShapeRect_HX"; }
public:
//设置对象得成员数据-根据成员对应得属性名称
virtual void setMemberData (const char * mbname, QVariant & data);
//根据对象成员属性名称返回对象相对应属性的数据值
virtual QVariant getMemberData (const char * mbname);
//返回对象属性列表
virtual QList <PropertyItemData> getPropertyList();
//数据存储函数
virtual void saveData (QDomDocument& doc, QDomElement& dom);
//数据读取函数
virtual void loadData (QDomElement& dom);
//等比改变外框矩形数据
virtual void zoomChangedData(qreal sx, qreal sy);
//对象克隆函数
ShapeBase_HX * clone();
//获取对象的精准外框矩形数据
virtual QRectF getOutlineBorder();
protected:
QRectF boundingRect() const;
QPainterPath shape() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
protected:
virtual void defaultData ();
//绘制对象的选中拖拽框
void drawSelectedHandle (QPainter *painter);
// 返回改变对象的拖拽位置点 测试鼠标是击中在对象的哪个拖拽位置点上,并且返回对象要被改变的点位置索引
int getDragHandle(QPointF &point);
int penWidth () ;
int penStyle ();
bool objFill () { return m_flag & 0x01 ;}
//设置对象线宽
virtual void setPenWidth (int width) ;
//设置对象线颜色
virtual void setLineColor (QColor & color) {m_lineColor=color;}
//设置对象线型
virtual void setPenStyle (int linetype) ;
//设置对象填充
virtual void setObjFill (bool fillflag) { m_flag &= 0xfffffffe; m_flag += fillflag; }
//设置对象填充颜色
virtual void setFillColor (QColor & color) {m_fillColor=color;}
virtual void changData_new (QPointF & rp ,MouseST mouseST);
protected:
//这是用来保存对象的bool型成员填充对象和线宽成员的容器,第0位代表是否填充对象,1,2,3,4,5位数值代表对象的线宽,6,7,8位标识线型
//[注意,对于填充方式因为修改了填充模式,每种填充模式对应了不同的绘制算法,所以原第0位代表是否填充对象目前已经无效了,填充使用m_iFillFlag填充绘制模式标志]
unsigned int m_flag;
QRectF m_rect;
QColor m_fillColor;
QColor m_lineColor;
float m_rx; //圆角半径
float m_ry; //圆角半径
int m_iFillFlag; //填充绘制模式标志
};
#endif //H_ShapeRect_HX_H__
ShapeRect_HX 类
1)成员用途简述
QRectF m_rect; //矩形的外形数据
QColor m_fillColor; //矩形对象的填充颜色
float m_rx; //圆角半径
float m_ry; //圆角半径
int m_iFillFlag; //填充绘制模式标志
2)成员函数用途简述
a、首先,因为我们的图元是从QGraphicsItem继承而来,因此有3个纯虚函数必须实现,这三个函数分别是:
QRectF boundingRect() const;
QPainterPath shape() const; //击中范围设置
void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget);
boundingRect函数决定图元对象刷新时的矩形区域大小。
shape()函数决定对象的击中范围
paint函数是用来根据自定义图元数据来绘制真实绘制的图元形状的。
b、对象是通过鼠标操作来完成初始绘制,因此我们需要实现鼠标相关的3个函数,分别是
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
mousePressEvent函数中主要完成,鼠标按下标志的记录,直线起点的记录,以及记录鼠标偏移点起点。
mouseMoveEvent函数中主要完成对矩形对象的外形数据改变,在新建矩形时,鼠标按下时就生成了矩形对象,也就是矩形TopLeft, RightBottom都是鼠标按下点位置,在mouseMoveEvent中,如果鼠标处于按下状态,此时鼠标移动则改变RightBottom点数据,从而改变了矩形外观。
mouseReleaseEvent函数主要是完成矩形数据改变的确认,根据矩形对象当前数据重新调整矩形对象的中心位置数据。
c、数据改变接口,数据获取接口,主要包含
void setMemberData (const char * mbname, QVariant & data);
QVariant getMemberData (const char * mbname);
void zoomChangedData(qreal sx, qreal sy);
setMemberData 通过属性名称字符串查找到对应属性,然后设置数据到对应成员。
getMemberData 通过属性名称字符串查找返回对应的成员值。
zoomChangedData改变外框矩形数据,直线在块中缩放时会用到。
//返回对象属性列表,主要应用在属性编辑
virtual QList <PropertyItemData> getPropertyList();
d、数据保存加载相关接口
//数据存储函数
virtual void saveData (QDomDocument& doc, QDomElement& dom);
//数据读取函数
virtual void loadData (QDomElement& dom);
e、较为特殊的内部函数
//changData_new 函数主要用在对象首次创建时的数据改变。
virtual void changData_new (QPointF & rp ,MouseST mouseST);
//重新调整中心位置
virtual void justPosAndData();
//绘制对象的选中拖拽框,在对象处于选中状态时,需要提示鼠标按在什么位置可以对矩形对象进行改变,因此绘制出拖拽矩形框,与直线对象的区别,直线对象只有2个拖拽矩形句柄,而矩形对象则存在8个拖拽句柄矩形。
void drawSelectedHandle (QPainter *painter);
// 返回改变对象的拖拽位置点 测试鼠标是击中在对象的哪个拖拽位置点上,并且返回对象要被改变的点位置索引
int getDragHandle(QPointF &point);
f、基础设置函数
int penWidth () ;
int penStyle ();
bool objFill () { return m_flag & 0x01 ;}
//设置对象线宽
virtual void setPenWidth (int width) ;
//设置对象线颜色
virtual void setLineColor (QColor & color) {m_lineColor=color;}
//设置对象线型
virtual void setPenStyle (int linetype) ;
//设置对象填充
virtual void setObjFill (bool fillflag) { m_flag &= 0xfffffffe; m_flag += fillflag; }
//设置对象填充颜色
virtual void setFillColor (QColor & color) {m_fillColor=color;}
g、为了使用原型模式工厂,因此对象需要实现clone()函数接口
//对象克隆函数
virtual ShapeBase_HX * clone();
ShapeRect_HX类cpp内容如下:
#include "shaperect_hx.h"
#include <QPainter>
#include <QtWidgets>
#include "gvarsdataset_hx.h"
#include <math.h>
#include "drawmath_hx.h"
ShapeRect_HX::ShapeRect_HX(void) : m_flag(0), m_rx(0), m_ry(0)
{
setPenWidth(GVarsDataset_HX::defaultPenWidth());
setPenStyle(GVarsDataset_HX::defaultPenStyle());
setObjFill(GVarsDataset_HX::isFill());
QColor color = GVarsDataset_HX::defaultLineColor();
setLineColor (color);
color = GVarsDataset_HX::defaultFillColor();
setFillColor(color);
m_iFillFlag = ST_GradientRadiation5FILL; // ST_NORMALFILL; //默认设置为5级辐射中心渐变
}
ShapeRect_HX::~ShapeRect_HX(void)
{
}
//返回刷新区域
QRectF ShapeRect_HX::boundingRect() const
{
qreal left, top, right, bottom;
left = m_rect.left();
top = m_rect.top();
right = m_rect.right();
bottom = m_rect.bottom();
qreal width = right-left+60;
qreal height = bottom-top+60;
if (left<right)
{
if (top<bottom)
{
return QRectF(left-30, top-30, right-left+60, bottom-top+60);
}
else
{
return QRectF(left-30, bottom-30, right-left+60, top-bottom+60);
}
}
else
{
if (top<bottom)
{
return QRectF(right-30, top-30, left-right+60, bottom-top+60);
}
else
{
return QRectF(right-30, bottom-30, left-right+60, top-bottom+60);
}
}
return QRectF (left-30,top-30, width+60, height+60);
}
//这里是击中对象的检测范围函数
QPainterPath ShapeRect_HX::shape() const
{
QPainterPath path;
if (ShapeBase_HX::estopHit==1)
return path;
/*qreal x1, y1, x2, y2;
x1 = m_rect.left();
y1 = m_rect.top();
x2 = m_rect.right();
y2 = m_rect.bottom();
QRectF rt;
if (x1 < x2) {
if (y1 < y2) {
rt = QRectF(m_rect.left() - SELHandleSize_HALF, m_rect.top() - SELHandleSize_HALF, m_rect.width() + SELHandleSize, m_rect.height() + SELHandleSize);
}
else {
rt = QRectF(m_rect.left() - SELHandleSize_HALF, m_rect.bottom() - SELHandleSize_HALF, m_rect.width() + SELHandleSize, m_rect.height() + SELHandleSize);
}
}
else {
if (y1 < y2) {
rt = QRectF(m_rect.right() - SELHandleSize_HALF, m_rect.top() - SELHandleSize_HALF, m_rect.width() + SELHandleSize, m_rect.height() + SELHandleSize);
}
else {
rt = QRectF(m_rect.right() - SELHandleSize_HALF, m_rect.bottom() - SELHandleSize_HALF, m_rect.width() + SELHandleSize, m_rect.height() + SELHandleSize);
}
}
path.addRect(rt); */
path.addRect(m_rect);
return path;
}
void ShapeRect_HX::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
QColor penColor = (option->state & QStyle::State_Selected) ? m_lineColor.dark(150) : m_lineColor;
QPen pen;
int width = penWidth();
int penstyle = penStyle();
pen.setStyle(Qt::PenStyle (penstyle));
if (QStyle::State_None != (option->state & QStyle::State_Selected))
{
penColor = m_lineColor.dark(150);
QColor fillColor = m_fillColor.dark(80);
painter->setBrush(getGradientBrush(m_rect, fillColor, FillTheWay(m_iFillFlag)));
}
else
{
penColor = m_lineColor;
QColor fillColor = m_fillColor.dark(80);
painter->setBrush(getGradientBrush(m_rect, m_fillColor, FillTheWay(m_iFillFlag)));
}
if (QStyle::State_None != (option->state & QStyle::State_MouseOver))
{
penColor = m_lineColor.light(85);
width += 2;
}
pen.setColor (penColor);
pen.setWidth (width);
painter->setPen(pen);
QPen oldPen = painter->pen();
if (QStyle::State_None != (option->state & QStyle::State_Selected))
width += 2;
pen.setWidth(width);
//qreal x1, y1, x2, y2, w, h;
qreal x1, y1;
x1 = m_rect.left();
y1 = m_rect.top();
//用不着像下面那么麻烦,Qt的这个绘图接口会根据 width()和height()值得正负来判断所在象限
painter->drawRoundRect(x1, y1, m_rect.width(), m_rect.height(), m_rx, m_ry); //强制性的将圆角矩形的圆角定值设置为0
if (QStyle::SC_None != (option->state & QStyle::State_Selected) && (!GVarsDataset_HX::isClient()))
{
drawSelectedHandle(painter);
}
}
void ShapeRect_HX::defaultData ()
{
QPointF topLeft = m_rect.topLeft();
QPointF bottomRight = m_rect.bottomRight();
if (DrawMath_HX::Distance(topLeft, bottomRight) < 10.000)
{
m_rect.setLeft(-30.00);
m_rect.setTop(-30.00);
m_rect.setRight(60.00);
m_rect.setBottom(60.00);
}
}
void ShapeRect_HX::changData_new (QPointF & rp, MouseST mouseST)
{
switch (mouseST)
{
case ST_MOUSEPRESS:
{
this->setPos (rp);
estopHit = 1; //在对象是第一次生成时,设置标志为1 用来保证这时候这个对象不能被击中(shape函数中不能被击中)
}
break;
case ST_MOUSEMOVE:
{
ShapeBase_HX::g_tempPoint = this->mapFromScene(rp);
m_rect.setBottomRight(ShapeBase_HX::g_tempPoint);
//将矩形的item坐标保存转换到场景坐标,保存起来,待重新设定中心位置后
//在将保存的矩形场景坐标在新的中心位置转换为item坐标
QPointF topleft = m_rect.topLeft();
QPointF cp1 = this->mapToScene(topleft); //起点
QPointF cp2 = rp; //终点
int h = m_rect.height();
int w = m_rect.width();
if (h<6.0 && w <6.0)
{
defaultData();
cp2 = this->mapToScene(m_rect.bottomRight());
}
QPointF npos;
npos = QPointF((cp1.rx() + cp2.rx()) / 2, (cp1.ry() + cp2.ry()) / 2);
//设置计算后的新中心坐标
this->setPos(npos);
//将保存的直线场景坐标在新的中心位置转换为item坐标
QPointF p1 = this->mapFromScene(cp1);
QPointF p2 = this->mapFromScene(cp2);
m_rect.setTopLeft(p1);
m_rect.setBottomRight(p2);
}
break;
case ST_MOUSERELEASE:
{
estopHit = -1;//这里标志对象已经初始生成完毕,清除首次生成标志,对象可以被击中
defaultData();
this->setSelected(true);
}
break;
}
update();
}
void ShapeRect_HX::drawSelectedHandle (QPainter *painter)
{
qreal left,top,right,bottom,heightV2,widthV2;
left = m_rect.left();
top = m_rect.top();
right = m_rect.right();
bottom = m_rect.bottom();
QPointF centerPt = m_rect.center();
qreal centerX = centerPt.rx();
qreal centerY = centerPt.ry();
if (left < right) {
if (top < bottom) {
painter->drawRect(left - SELHandleSize_LeftTop, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
painter->drawRect(right - SELHandleSize_RightBotom, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //2 righttop
painter->drawRect(right - SELHandleSize_RightBotom, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize);//4 rightbottom
painter->drawRect(left - SELHandleSize_LeftTop, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //6 leftbottom
}
else {
painter->drawRect(left - SELHandleSize_LeftTop, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
painter->drawRect(right - SELHandleSize_RightBotom, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //2 righttop
painter->drawRect(right - SELHandleSize_RightBotom, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //4 rightbottom
painter->drawRect(left - SELHandleSize_LeftTop, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //6 leftbottom
}
}
else {
if (top < bottom) {
painter->drawRect(left - SELHandleSize_RightBotom, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
painter->drawRect(right - SELHandleSize_LeftTop, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //2 righttop
painter->drawRect(right - SELHandleSize_LeftTop, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize);//4 rightbottom
painter->drawRect(left - SELHandleSize_RightBotom, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //6 leftbottom
}
else {
painter->drawRect(left - SELHandleSize_RightBotom, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
painter->drawRect(right - SELHandleSize_LeftTop, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //2 righttop
painter->drawRect(right - SELHandleSize_LeftTop, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize);//4 rightbottom
painter->drawRect(left - SELHandleSize_RightBotom, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //6 leftbottom
}
}
if (left < right) {
if (top < bottom) {
painter->drawRect(centerX - SELHandleSize_HALF, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //1 top < bottom
painter->drawRect(right - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right > left
painter->drawRect(centerX - SELHandleSize_HALF, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //5 bottom> top
painter->drawRect(left - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left < right
}
else
{
painter->drawRect(centerX - SELHandleSize_HALF, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //1 top > bottom
painter->drawRect(right - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right > left
painter->drawRect(centerX - SELHandleSize_HALF, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //5 bottom< top
painter->drawRect(left - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left < right
}
}
else {
if (top < bottom) {
painter->drawRect(centerX - SELHandleSize_HALF, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //1 top < bottom
painter->drawRect(right - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right < left
painter->drawRect(centerX - SELHandleSize_HALF, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //5 bottom > top
painter->drawRect(left - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left > right
}
else
{
painter->drawRect(centerX - SELHandleSize_HALF, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //1 top > bottom
painter->drawRect(right - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right < left
painter->drawRect(centerX - SELHandleSize_HALF, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //5 bottom < top
painter->drawRect(left - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left > right
}
}
}
// 返回改变对象的拖拽位置点
int ShapeRect_HX::getDragHandle(QPointF &point)
{
if(GVarsDataset_HX::isClient())
return DRAG_UNKNOWN;
/*qreal left,top,right,bottom,heightV2,widthV2;
left = m_rect.left();
top = m_rect.top();
right = m_rect.right();
bottom = m_rect.bottom();
heightV2 = m_rect.height()/2;
widthV2 = m_rect.width()/2;
QRectF t = QRectF (left- SELHandleSize_HALF, top- SELHandleSize_HALF, SELHandleSize, SELHandleSize); //0
if (t.contains (point))
return DRAG_LEFT_TOP;
t = QRectF (left- SELHandleSize_HALF, bottom- SELHandleSize_HALF, SELHandleSize, SELHandleSize); //6
if (t.contains (point))
return DRAG_LEFT_BOTTOM;
t = QRectF (right- SELHandleSize_HALF, top- SELHandleSize_HALF, SELHandleSize, SELHandleSize); //2
if (t.contains (point))
return DRAG_RIGHT_TOP;
t = QRectF (right- SELHandleSize_HALF, bottom- SELHandleSize_HALF, SELHandleSize, SELHandleSize); //4
if (t.contains (point))
return DRAG_RIGHT_BOTTOM;
t = QRectF (left- SELHandleSize_HALF, top+heightV2- SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7
if (t.contains (point))
return DRAG_LEFT;
t = QRectF (right- SELHandleSize_HALF, top+heightV2- SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3
if (t.contains (point))
return DRAG_RIGHT;
t = QRectF (left+widthV2- SELHandleSize_HALF, top- SELHandleSize_HALF, SELHandleSize, SELHandleSize); //1
if (t.contains (point))
return DRAG_TOP;
t = QRectF (left+widthV2- SELHandleSize_HALF, bottom- SELHandleSize_HALF, SELHandleSize, SELHandleSize);//5
if (t.contains (point))
return DRAG_BOTTOM; */
QRectF t;
qreal left, top, right, bottom, heightV2, widthV2;
left = m_rect.left();
top = m_rect.top();
right = m_rect.right();
bottom = m_rect.bottom();
QPointF centerPt = m_rect.center();
qreal centerX = centerPt.rx();
qreal centerY = centerPt.ry();
//返回4个顶角的句柄
if (left < right) {
if (top < bottom) {
t = QRectF(left - SELHandleSize_LeftTop, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
if (t.contains(point))
return DRAG_LEFT_TOP;
t = QRectF(right - SELHandleSize_RightBotom, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //2 righttop
if (t.contains(point))
return DRAG_RIGHT_TOP;
t = QRectF(right - SELHandleSize_RightBotom, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //4 rightbottom
if (t.contains(point))
return DRAG_RIGHT_BOTTOM;
t = QRectF(left - SELHandleSize_LeftTop, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //6 leftbottom
if (t.contains(point))
return DRAG_LEFT_BOTTOM;
}
else {
t = QRectF(left - SELHandleSize_LeftTop, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
if (t.contains(point))
return DRAG_LEFT_TOP;
t = QRectF(right - SELHandleSize_RightBotom, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //2 righttop
if (t.contains(point))
return DRAG_RIGHT_TOP;
t = QRectF(right - SELHandleSize_RightBotom, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //4 rightbottom
if (t.contains(point))
return DRAG_RIGHT_BOTTOM;
t = QRectF(left - SELHandleSize_LeftTop, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //6 leftbottom
if (t.contains(point))
return DRAG_LEFT_BOTTOM;
}
}
else {
if (top < bottom) {
t = QRectF(left - SELHandleSize_RightBotom, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
if (t.contains(point))
return DRAG_LEFT_TOP;
t = QRectF(right - SELHandleSize_LeftTop, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //2 righttop
if (t.contains(point))
return DRAG_RIGHT_TOP;
t = QRectF(right - SELHandleSize_LeftTop, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //4 rightbottom
if (t.contains(point))
return DRAG_RIGHT_BOTTOM;
t = QRectF(left - SELHandleSize_RightBotom, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //6 leftbottom
if (t.contains(point))
return DRAG_LEFT_BOTTOM;
}
else {
t = QRectF(left - SELHandleSize_RightBotom, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //0 lefttop left<right, < bottom
if (t.contains(point))
return DRAG_LEFT_TOP;
t = QRectF(right - SELHandleSize_LeftTop, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //2 righttop
if (t.contains(point))
return DRAG_RIGHT_TOP;
t = QRectF(right - SELHandleSize_LeftTop, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //4 rightbottom
if (t.contains(point))
return DRAG_RIGHT_BOTTOM;
t = QRectF(left - SELHandleSize_RightBotom, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //6 leftbottom
if (t.contains(point))
return DRAG_LEFT_BOTTOM;
}
}
//返回中间位置的句柄
if (left < right) {
if (top < bottom) {
t = QRectF(centerX - SELHandleSize_HALF, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //1 top < bottom
if (t.contains(point))
return DRAG_TOP;
t = QRectF(right - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right > left
if (t.contains(point))
return DRAG_RIGHT;
t = QRectF(centerX - SELHandleSize_HALF, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //5 bottom> top
if (t.contains(point))
return DRAG_BOTTOM;
t = QRectF(left - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left < right
if (t.contains(point))
return DRAG_LEFT;
}
else
{
t = QRectF(centerX - SELHandleSize_HALF, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //1 top > bottom
if (t.contains(point))
return DRAG_TOP;
t = QRectF(right - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right > left
if (t.contains(point))
return DRAG_RIGHT;
t = QRectF(centerX - SELHandleSize_HALF, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //5 bottom< top
if (t.contains(point))
return DRAG_BOTTOM;
t = QRectF(left - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left < right
if (t.contains(point))
return DRAG_LEFT;
}
}
else {
if (top < bottom) {
t = QRectF(centerX - SELHandleSize_HALF, top - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //1 top < bottom
if (t.contains(point))
return DRAG_TOP;
t = QRectF(right - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right < left
if (t.contains(point))
return DRAG_RIGHT;
t = QRectF(centerX - SELHandleSize_HALF, bottom - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //5 bottom > top
if (t.contains(point))
return DRAG_BOTTOM;
t = QRectF(left - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left > right
if (t.contains(point))
return DRAG_LEFT;
}
else
{
t = QRectF(centerX - SELHandleSize_HALF, top - SELHandleSize_RightBotom, SELHandleSize, SELHandleSize); //1 top > bottom
if (t.contains(point))
return DRAG_TOP;
t = QRectF(right - SELHandleSize_LeftTop, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //3 right < left
if (t.contains(point))
return DRAG_RIGHT;
t = QRectF(centerX - SELHandleSize_HALF, bottom - SELHandleSize_LeftTop, SELHandleSize, SELHandleSize); //5 bottom < top
if (t.contains(point))
return DRAG_BOTTOM;
t = QRectF(left - SELHandleSize_RightBotom, centerY - SELHandleSize_HALF, SELHandleSize, SELHandleSize); //7 left > right
if (t.contains(point))
return DRAG_LEFT;
}
}
//if (m_rect.contains(point))
// return DRAG_SELECT;
return DRAG_UNKNOWN;
}
int ShapeRect_HX::penWidth ()
{
unsigned int res = m_flag >> 1;
res &= 0x1f;
return res;
}
void ShapeRect_HX::setPenWidth (int width)
{
unsigned int widthX = width % 0x1f;
m_flag &= 0xffffffc1;
m_flag += (widthX<<1);
};
int ShapeRect_HX::penStyle ()
{
unsigned int penstyleX = m_flag>>6;
return (penstyleX & 0x07);
}
void ShapeRect_HX::setPenStyle (int linetype)
{
unsigned int penstyleX = linetype % 0x07;
m_flag &= 0xfffffe3f;
m_flag += (penstyleX<<6);
}
void ShapeRect_HX::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
/*QPointF tpt = event->pos ();
ShapeBase_HX::dragHandle = getDragHandle (tpt);
offset = tpt;
if(ShapeBase_HX::dragHandle==-1)
offset=this->pos();
QGraphicsItem::mousePressEvent(event); //QGraphicsItem的鼠标事件会自动调用update() */
QPointF tpt = event->pos();
ShapeBase_HX::g_tempPoint = mapToScene(tpt); //场景坐标记录
ShapeBase_HX::dragHandle = getDragHandle(tpt); //测试击中句柄--本地坐标系
QGraphicsItem::mousePressEvent(event);
}
void ShapeRect_HX::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (GVarsDataset_HX::isClient()) {
return;
}
if (event->modifiers() & Qt::ShiftModifier)
{
update();
return;
}
if ( ShapeBase_HX::dragHandle != -1)
{
QPointF tpt = mapToParent(event->pos()); //场景坐标记录
QPointF toffset = tpt - ShapeBase_HX::g_tempPoint; //计算出需要改变的偏差值
ShapeBase_HX::g_tempPoint = tpt; //保存场景坐标系点值
switch (ShapeBase_HX::dragHandle)
{
case DRAG_LEFT_TOP:
{
m_rect.setTopLeft (event->pos ()); //event->pos()是图形项本身的逻辑坐标
}
break;
case DRAG_TOP:
{
m_rect.setTop (event->pos ().ry());
}
break;
case DRAG_RIGHT_TOP:
{
m_rect.setTopRight (event->pos ());
}
break;
case DRAG_RIGHT:
{
m_rect.setRight (event->pos ().rx());
}
break;
case DRAG_RIGHT_BOTTOM:
{
m_rect.setBottomRight (event->pos ());
}
break;
case DRAG_BOTTOM:
{
m_rect.setBottom (event->pos ().ry());
}
break;
case DRAG_LEFT_BOTTOM:
{
m_rect.setBottomLeft (event->pos ());
}
break;
case DRAG_LEFT:
{
m_rect.setLeft (event->pos ().rx());
}
break;
/*case DRAG_SELECT: { 这里是对象移动
QPointF tTopLeft = m_rect.topLeft() + toffset;
QPointF tRightBotom = m_rect.bottomRight() + toffset;
m_rect.setTopLeft(tTopLeft);
m_rect.setBottomRight(tRightBotom);
}break;*/
}
//将矩形的item坐标保存转换到场景坐标,保存起来,待重新设定中心位置后
//在将保存的矩形场景坐标在新的中心位置转换为item坐标
QPointF cp1 = this->mapToScene(m_rect.topLeft());
QPointF cp2 = this->mapToScene(m_rect.bottomRight());
//设置计算后的新中心坐标
QPointF npos;
npos = QPointF((cp1.rx() + cp2.rx()) / 2, (cp1.ry() + cp2.ry()) / 2);
this->setPos(npos); //将图形项自身的原点设置到场景坐标npos的位置
//拖动右下角句柄时setPos()之后图形项的左上角相对与场景坐标还是不变的,但是相对于自身的坐标原点,左上角坐标的值发生了变化
QPointF p1 = this->mapFromScene(cp1); //用相同的场景坐标得到不同的图形项坐标
QPointF p2 = this->mapFromScene(cp2);
m_rect.setTopLeft(p1);
m_rect.setBottomRight(p2);
update();
}
else {
/*QPointF tpt = mapToParent(event->pos()); //场景坐标记录
QPointF toffset = tpt - ShapeBase_HX::g_tempPoint; //计算出需要改变的偏差值
ShapeBase_HX::g_tempPoint = tpt; //保存场景坐标系点值
QPointF tTopLeft = m_rect.topLeft() + toffset;
QPointF tRightBotom = m_rect.bottomRight() + toffset;
m_rect.setTopLeft(tTopLeft);
m_rect.setBottomRight(tRightBotom); */
QGraphicsItem::mouseMoveEvent(event); //QGraphicsItem的鼠标事件会自动调用update()
}
}
void ShapeRect_HX::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if ( ShapeBase_HX::dragHandle != DRAG_UNKNOWN)
{
offset = event->pos () - offset;
if ( ShapeBase_HX::dragHandle == DRAG_LEFT_TOP
|| ShapeBase_HX::dragHandle == DRAG_TOP
|| ShapeBase_HX::dragHandle == DRAG_RIGHT_TOP
|| ShapeBase_HX::dragHandle == DRAG_RIGHT
|| ShapeBase_HX::dragHandle == DRAG_RIGHT_BOTTOM
|| ShapeBase_HX::dragHandle == DRAG_BOTTOM
|| ShapeBase_HX::dragHandle == DRAG_LEFT_BOTTOM
|| ShapeBase_HX::dragHandle == DRAG_LEFT
)
{
GVarsDataset_HX::emitnotify(this->scene(), ShapeBase_HX::dragHandle, offset, 4);
}
}
else
{
if(this->pos().x()!=offset.x() && this->pos().y()!=offset.y())
{
offset = this->pos() - offset;
GVarsDataset_HX::emitnotify(this->scene(),0,offset,3);
}
}
QGraphicsItem::mouseReleaseEvent(event); //QGraphicsItem的鼠标事件会自动调用update()
ShapeBase_HX::dragHandle = DRAG_UNKNOWN;
}
void ShapeRect_HX::setMemberData (const char * mbname, QVariant & data)
{
if (strcmp(mbname, pty_penWidth)==0 )
{
setPenWidth (data.toInt());
}
else if(strcmp(mbname, pty_penStyle)==0 )
{
int x = data.toInt();
setPenStyle (x);
switch (x)
{
case 0:
setPenStyle (Qt::SolidLine);
break;
case 1:
setPenStyle (Qt::DashLine);
break;
case 2:
setPenStyle (Qt::DotLine);
break;
case 3:
setPenStyle (Qt::DashDotLine);
break;
case 4:
setPenStyle (Qt::DashDotDotLine);
break;
case 5:
setPenStyle (Qt::CustomDashLine);
break;
default:
setPenStyle (Qt::SolidLine);
break;
}
QRectF rt = this->boundingRect();
this->update (rt);
}
else if(strcmp(mbname, pty_lineColor)==0)
{
QColor color = data.value<QColor>();
setLineColor (color);
}
else if(strcmp(mbname, pty_height)==0 )
{
m_rect.setHeight (data.toDouble());
}
else if(strcmp(mbname, pty_width)==0 )
{
m_rect.setWidth (data.toDouble());
}
else if(strcmp(mbname, pty_posx)==0 )
{
QPointF npos ;
npos = this->pos();
//设置计算后的新中心坐标
qreal x = data.toDouble();
npos.setX (x);
this->setPos (npos);
}
else if(strcmp(mbname, pty_posy)==0)
{
QPointF npos ;
npos = this->pos();
//设置计算后的新中心坐标
qreal y = data.toDouble();
npos.setY (y);
this->setPos (npos);
}
else if(strcmp(mbname, pty_angle)==0 )
{
setAngle (data.toDouble());
m_angle = data.toDouble();
}
else if (strcmp(mbname, pty_fill)==0 )
{
int d = data.toInt();
setObjFill (d);
}
else if (strcmp(mbname, pty_fillMode) == 0)
{
m_iFillFlag = data.toInt();
update();
}
else if(strcmp(mbname, pty_fillColor)==0 )
{
QColor color = data.value<QColor>();
setFillColor(color);
}
else if (strcmp(mbname, pty_rect)==0 )
{
m_rect = data.value<QRectF>();
}
else if (strcmp(mbname, pty_rectrx) == 0)
{
m_rx = data.toFloat();
}
else if (strcmp(mbname, pty_rectry) == 0)
{
m_ry = data.toFloat();
}
else if (strcmp(mbname, pty_layerNum) == 0)
{
int z = data.toInt();
this->setZValue(z);
update();
}
this->update (this->boundingRect());
}
QVariant ShapeRect_HX::getMemberData (const char * mbname)
{
if (strcmp(mbname, pty_penWidth)==0 )
{
return penWidth();
}
else if(strcmp(mbname, pty_penStyle)==0 )
{
return penStyle();
}
else if(strcmp(mbname, pty_lineColor)==0 )
{
return m_lineColor;
}
else if(strcmp(mbname, pty_height)==0 )
{
return m_rect.width();
}
else if(strcmp(mbname, pty_width)==0 )
{
return m_rect.height();
}
else if(strcmp(mbname, pty_posx)==0 )
{
QPointF npos = this->pos();
return npos.rx();
}
else if(strcmp(mbname, pty_posy)==0 )
{
QPointF npos ;
npos = this->pos();
return npos.ry();
}
else if(strcmp(mbname, pty_rect)==0 )
{
QPolygonF rt = mapToScene(m_rect); //转换为场景坐标后返回wxj2020-4/15
return rt.boundingRect();
}
else if(strcmp(mbname, pty_fillColor)==0 )
{
return m_fillColor;
}
else if (strcmp(mbname, pty_fill)==0 )
{
return objFill();
}
else if(strcmp(mbname, pty_angle)==0 )
{
return m_angle;
}
else if (strcmp(mbname, pty_rectrx) == 0)
{
return m_rx;
}
else if (strcmp(mbname, pty_rectry) == 0)
{
return m_ry;
}
else if (strcmp(mbname, pty_OutlineorderRect) == 0)
{
return getOutlineBorder();
}
return QVariant(QVariant::Invalid);
}
void ShapeRect_HX::saveData (QDomDocument& doc, QDomElement& dom)
{
QDomElement ele = doc.createElement("rect");
dom.appendChild(ele);
qreal x1, y1, x2, y2, width, height;
x1 = m_rect.left();
y1 = m_rect.top();
x2 = m_rect.right();
y2 = m_rect.bottom();
width = abs(m_rect.width());
height = abs(m_rect.height());
QPointF lefttop; //svg中的左上角坐标
if (x1<x2)
{
if (y1<y2) //四象限
{
lefttop = mapToScene(m_rect.topLeft()); //获取场景的坐标
}
else //一象限
{
lefttop = mapToScene(m_rect.bottomLeft());
}
}
else
{
if (y1<y2) //三象限
{
lefttop = mapToScene(m_rect.topRight());
}
else //二象限
{
lefttop = mapToScene(m_rect.bottomRight());
}
}
//保存
//直接按视口坐标保存
ele.setAttribute("x", lefttop.rx()); //位于场景的矩形左上角位置
ele.setAttribute("y", lefttop.ry());
ele.setAttribute("width", width);
ele.setAttribute("height", height);
ele.setAttribute("rx", m_rx); //rx
ele.setAttribute("ry", m_ry);
ele.setAttribute(pty_fillMode, m_iFillFlag); //保存填充模式数据
//保存矩形style
QString tstr;
if (objFill())
{
tstr = QString("fill:%1;stroke:%2;stroke-width:%3")
.arg(QVariant(m_fillColor).toString()).arg(QVariant(m_lineColor).toString()).arg(penWidth());
}
else
{
tstr = QString("fill:none;stroke:%1;stroke-width:%2")
.arg(QVariant(m_lineColor).toString()).arg(penWidth());
}
ele.setAttribute("style", tstr);
#if 0
m_angle = 60;
#endif
//保存角度
if (m_angle!=0)
{
tstr = QString("rotate(%1 %2 %3)").arg(m_angle).arg(lefttop.rx()+ width/2).arg(lefttop.ry()+ height/2);
ele.setAttribute("transform", tstr);
}
else
{
//角度值为0,则不保存角度属性
}
}
void ShapeRect_HX::loadData (QDomElement& dom)
{
m_rx = dom.attribute("rx").toFloat();
m_ry = dom.attribute("ry").toFloat();
qreal x = dom.attribute("x").toFloat();
qreal y = dom.attribute("y").toFloat(); xy为左上角的记录
qreal width = dom.attribute("width").toFloat();
qreal height = dom.attribute("height").toFloat();
qreal centerX = x + width / 2;
qreal centerY = x + height / 2;
QPointF centerPt(centerX, centerY);
this->setPos(centerPt);
QPointF topLeft(x, y);
topLeft = mapFromScene(topLeft);
m_rect.setTopLeft(topLeft);
m_rect.setHeight(height);
m_rect.setWidth(width);
m_iFillFlag = dom.attribute(pty_fillMode).toInt();
QString style = dom.attribute("style");
QStringList styleList = style.split(";");
if (styleList.count()==3) {
QString fillRgbStr = styleList.at(0); //填充色
QString fillstr = fillRgbStr.right(fillRgbStr.length() - 5);
if (fillstr.compare("none") == 0) {
setObjFill(false);
QColor tColor;
setFillColor(tColor);
}
else {
setObjFill(true);
QColor tColor = QVariant(fillstr).value<QColor>();
setFillColor(tColor);
}
QString lineRgbStr = styleList.at(1); //边框色
QString lineColorstr = lineRgbStr.right(lineRgbStr.length() - 7);
if (lineColorstr.compare("none") == 0) {
QColor tColor;
setFillColor(tColor);
}
else {
QColor tColor = QVariant(lineColorstr).value<QColor>();
setLineColor(tColor);
}
QString lineWidthStr = styleList.at(2); //线宽度
int pos5 = lineWidthStr.indexOf(":");
int lineWidth = lineWidthStr.mid(pos5 + 1, lineWidthStr.size() - 1 - pos5).toInt();
setPenWidth(lineWidth);
}
if (dom.hasAttribute("transform"))
{
QString transform = dom.attribute("transform");
if (transform.contains("rotate"))
{
QString s = transform.section("rotate", 1, 1);
s = s.section("(", 1, 1);
s = s.section(")", 0, 0);
int angle = s.section(" ", 0, 0).toInt();
setAngle(angle);
}
}
}
QList <PropertyItemData> ShapeRect_HX::getPropertyList()
{
QList <PropertyItemData> lists;
PropertyItemData Width;
Width.nam = ShpaeGBKStr("宽度");
Width.val = abs(m_rect.width());
Width.type = FloatProperty;
Width.indexName = pty_width;
lists.append(Width);
PropertyItemData Height;
Height.nam = ShpaeGBKStr("高度");
Height.val = abs(m_rect.height());
Height.type = FloatProperty;
Height.indexName = pty_height;
lists.append(Height);
PropertyItemData posX;
posX.nam = ShpaeGBKStr("中心位置X");
posX.val = pos().x();
posX.type = FloatProperty;
posX.indexName = pty_posx;
lists.append(posX);
PropertyItemData posY;
posY.nam = ShpaeGBKStr("中心位置Y");
posY.val = pos().y();
posY.type = FloatProperty;
posY.indexName = pty_posy;
lists.append(posY);
PropertyItemData lineColorX;
lineColorX.nam = ShpaeGBKStr("笔颜色");
lineColorX.val = m_lineColor;
lineColorX.type = ColorProperty;
lineColorX.indexName = pty_lineColor;
lists.append(lineColorX);
/*PropertyItemData fill;
fill.nam = ShpaeGBKStr("是否填充");
fill.val = objFill();
fill.type = BoolProperty;
fill.indexName = pty_fill;
lists.append(fill);*/
PropertyItemData fillMode;
fillMode.nam = ShpaeGBKStr("填充模式");
fillMode.val = m_iFillFlag;
fillMode.type = StateProperty;
fillMode.indexName = pty_fillMode;
lists.append(fillMode);
PropertyItemData fillColorX;
fillColorX.nam = ShpaeGBKStr("填充颜色");
fillColorX.val = m_fillColor;
fillColorX.type = ColorProperty;
fillColorX.indexName = pty_fillColor;
lists.append(fillColorX);
PropertyItemData angleX;
angleX.nam = ShpaeGBKStr("旋转角度");
angleX.val = m_angle;
angleX.type = IntProperty;
angleX.indexName = pty_angle;
lists.append(angleX);
PropertyItemData lineWidth;
lineWidth.nam = ShpaeGBKStr("线宽");
lineWidth.val = penWidth();
lineWidth.type = IntProperty;
lineWidth.indexName = pty_penWidth;
lists.append(lineWidth);
PropertyItemData lineStyle;
lineStyle.nam = ShpaeGBKStr("线型");
lineStyle.val = penStyle();
lineStyle.type = LineTypeProperty;
lineStyle.indexName = pty_penStyle;
lists.append(lineStyle);
PropertyItemData RX;
RX.nam = ShpaeGBKStr("圆角X");
RX.val = m_rx;
RX.type = IntProperty;
RX.indexName = pty_rectrx;
RX.atonceFlg = true;
lists.append(RX);
PropertyItemData RY;
RY.nam = ShpaeGBKStr("圆角Y");
RY.val = m_ry;
RY.type = IntProperty;
RY.indexName = pty_rectry;
RX.atonceFlg = true;
lists.append(RY);
PropertyItemData layerNum;
layerNum.nam = ShpaeGBKStr("图层");
layerNum.val = this->zValue();
layerNum.type = IntProperty;
layerNum.indexName = pty_layerNum;
lists.append(layerNum);
return lists;
}
ShapeBase_HX * ShapeRect_HX::clone()
{
ShapeRect_HX * cloneObj = new ShapeRect_HX(); //为了在对象内部不包含创建工厂,因此这里直接使用new
cloneObj->m_lineColor = m_lineColor;
cloneObj->m_rect = m_rect;
cloneObj->m_fillColor = m_fillColor;
cloneObj->m_flag = m_flag;
cloneObj->m_rx = m_rx;
cloneObj->m_ry = m_ry;
cloneObj->setPos(pos());
cloneObj->m_iFillFlag = m_iFillFlag;
return cloneObj;
}
void ShapeRect_HX::zoomChangedData(qreal sx, qreal sy)
{
/*qreal w = m_rect.width();
qreal h = m_rect.height();
qreal ww;
qreal hh;
int limit = m_angle / 90;
switch (limit)
{
case 0:
{
ww = w*sx;
hh = h*sy;
}break;
case 1:
{
ww = w*sy;
hh = h*sx;
}break;
case 2:
{
ww = w*sx;
hh = h*sy;
}break;
case 3:
{
ww = w*sy;
hh = h*sx;
}break;
}
qreal left = -ww / 2;
qreal right = ww / 2;
qreal top = -hh / 2;
qreal btm = hh / 2;
m_rect.setLeft(left);
m_rect.setTop(top);
m_rect.setRight(right);
m_rect.setBottom(btm); */
QPointF topLeft = m_rect.topLeft();
QPointF buttomRight = m_rect.bottomRight();
topLeft.setX(topLeft.rx()*sx);
topLeft.setY(topLeft.ry()*sy);
buttomRight.setX(buttomRight.rx()*sx);
buttomRight.setY(buttomRight.ry()*sy);
m_rect.setTopLeft(topLeft);
m_rect.setBottomRight(buttomRight);
}
QRectF ShapeRect_HX::getOutlineBorder()
{
QPointF topLeft = m_rect.topLeft();
QPointF bottomRight = m_rect.bottomRight();
topLeft = mapToScene(topLeft);
bottomRight = mapToScene(bottomRight);
QRectF rt;
rt.setTopLeft(topLeft);
rt.setBottomRight(bottomRight);
return rt;
}
上面描述来图元基类,直线图元类,矩形图元类,接下来我们来看看我们的图元工厂,下面是头文件
#ifndef _H_FACTORY_PROTYPE_HX_H_
#define _H_FACTORY_PROTYPE_HX_H_
#include <map>
#include <string>
#include <iostream>
using std::map;
using std::string;
/*
原型模式的工厂方法类,可创建任意类型的对象,只要该对象支持原型clone方法,注意该工厂仅仅支持对象的无参构造,不支持有参构造类对象
@author: wxj
@date 2020/3/12
*/
//原型工厂模式 --obj_interface系列的工厂
template <typename AbstractObject_t>
class ProtyepFactory {
public:
// 获取工厂单例,工厂的实例是唯一的
static ProtyepFactory &Instance()
{
static ProtyepFactory instance; //懒汉式单例
return instance;
}
//通过原型克隆一个对象
AbstractObject_t* createObject(string _objclass) {
// 从map找到已经注册过的原型对象,并返回通过原型克隆的新对象
if (m_regProtypeMap.find(_objclass) != m_regProtypeMap.end())
{
return m_regProtypeMap[_objclass]->clone();
}
return nullptr;
}
//原型注册
void RegisterObject(AbstractObject_t *registrar, std::string name)
{
m_regProtypeMap[name] = registrar;
}
private:
// 禁止外部构造和析构构
ProtyepFactory() {}
~ProtyepFactory() {}
private:
map<string, AbstractObject_t*> m_regProtypeMap;
};
#endif //_H_FACTORY_PROTYPE_HX_H_
#ifndef _HX_SHAPEFACTORY_HX_H__
#define _HX_SHAPEFACTORY_HX_H__
#include "shape_global.h"
#include <iostream>
#include <string>
#include <map>
using std::map;
using std::string;
using std::cout;
using std::endl;
/*
* @date: 2020/3/20
* @author wxj
* @func 图元对象创建工厂类【模板方式的原型工厂设计模式实现】,完成所有的图元根据字符串方式的类型来创建对应的指定类型图元,
*/
class ShapeBase_HX;
//模板类的前向声明
template <class AbstractObject_t> class ProtyepFactory;
//注册图元到工厂模板对象中
class SHAPESHARED_EXPORT ShapeFactory {
public:
static void ShapesRegisterInit();
static ShapeBase_HX* createShape(const string& _shapeType);
};
#endif //_HX_SHAPEFACTORY_HX_H__
/
ShapeFactory 的CPP 内容
#include "factory_protype_hx.h"
#include "shapefactory_hx.h"
#include "shapebase_hx.h"
#include "shapegroup_hx.h"
#include "shapeline_hx.h"
#include "shaperect_hx.h"
#include "shapeellipse_hx.h"
#include "shapetext_hx.h"
#include "shapepolyline_hx.h"
#include "shapepolygon_hx.h"
#include "shapearc_hx.h"
#include "shapetopology_hx.h"
#include "shapetopologybrokenline_hx.h"
#include "shapetopologyline_hx.h"
#include "shapemultitext_hx.h"
#include "shapeimage_hx.h"
#include "shapetwowaychoice_hx.h"
#include "shapegif_hx.h"
#include "shapebezier_hx.h"
#include "shapearrowline.h"
#include "shapebrokenarrawline_hx.h"
//类型定义
typedef ProtyepFactory<ShapeBase_HX> ShapeFactory_HX;
void ShapeFactory::ShapesRegisterInit() {
ShapeFactory_HX::Instance().RegisterObject(new ShapeGroup_HX(), "ShapeGroup_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeLine_HX(), "ShapeLine_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeRect_HX(), "ShapeRect_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeEllipse_HX(), "ShapeEllipse_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeText_HX(), "ShapeText_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapePolyline_HX(), "ShapePolyline_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapePolygon_HX(), "ShapePolygon_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapePolygon_HX(), "ShapePolygon_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeArc_HX(), "ShapeArc_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeTopology_HX(), "ShapeTopology_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeTopologyBrokenLine_HX(), "ShapeTopologyBrokenLine_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeTopologyLine_HX(), "ShapeTopologyLine_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeMultiText_HX(), "ShapeMultiText_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeImage_HX(), "ShapeImage_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeTwowaychoice_HX(), "ShapeTwowaychoice_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeGif_HX(), "ShapeGif_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeBezier_HX(), "ShapeBezier_HX");
ShapeFactory_HX::Instance().RegisterObject(new ShapeArrowLine(), "ShapeArrowLine");
ShapeFactory_HX::Instance().RegisterObject(new ShapeBrokenArrawLine_HX(), "ShapeBrokenArrawLine_HX");
}
ShapeBase_HX* ShapeFactory::createShape(const string& _shapeType)
{
return ShapeFactory_HX::Instance().createObject(_shapeType);
}
接下是场景类
#ifndef FHGRAPHSCENE_H
#define FHGRAPHSCENE_H
#include “graphface_global.h”
#include
#include
#include <set>
using std::set;
#include "../shape/shapebase_def_hx.h"
#include "graphbasedef_hx.h"
using namespace wxj;
class PropertyItemData;
/**
*@class class GraphScene_HX : public QGraphicsScene
*@brief 用来加载以及绘制各种图元的场景类,包含于绘图视图窗口中。本类完成从按下鼠标开始到鼠标弹起生成指定类型的图元对象,
从按下鼠标选择到图元对象,移动鼠标,到鼠标弹起后完成对象的移动。从按下鼠标选择到图元对象的缩放句柄,移动鼠标,
到鼠标弹起后完成对象的缩放。通过修改对象的属性来改变对应对象的外观或者位置等。
*date 20191014
*author wxj
*external
*/
//回调函数,在view上显示单行文本输入框,并且设置其焦点
typedef void (*TextEditShow_CallBack)(void* ptrOwner, QPointF& pos);
//回调函数,获取单行文本输入框里面的文本内容
typedef QString (*TextEditGet_CallBack)(void* ptrOwner);
//回调函数,在view上显示多行文本输入框,并且设置其焦点,在显示该编辑框是还需要对应设置窗口大小以及设置其默认文本内容(从多行文本对象中获取)
typedef void(*MultiTextEditShow_CallBack)(void* ptrOwner, QPointF& pos, QRectF& _winRect, QString& _text);
//回调函数,获取多行文本输入框里面的文本内容
typedef QString(*MultiTextEditGet_CallBack)(void* ptrOwner);
//场景类与视图类的回调策略结构定义
typedef struct _IGraphviewCallBack
{
TextEditShow_CallBack TextEditShow; //显示单行文本编辑框对象
TextEditGet_CallBack inputTextGet; //获取单行文本输入框对象中的文本
MultiTextEditShow_CallBack MultiTextEditShow; //显示多行行文本编辑框对象
MultiTextEditGet_CallBack inputMultiTextGet; //获取多行文本输入框对象中的文本
void * ptrOwener; //回调函数的实现者
}ICallBack_Graphview;
QT_BEGIN_NAMESPACE
class QDragEnterEvent;
class QDropEvent;
class QMouseEvent;
class QMimeData;
QT_END_NAMESPACE
class ShapeBase_HX;
class ICallBack_GraphOperator;
class GraphScene_HX : public QGraphicsScene
{
Q_OBJECT
public:
GraphScene_HX(QObject *parent = NULL, bool isEdit=1); //默认窗口类型是编辑
~GraphScene_HX();
public:
void shapeNewType (const int &shapeType); //按指定类型绘制图元
void setICallBack_Graphview (ICallBack_Graphview &icallback); //设置回调策略对象
void setOperator(OperatorST _operType); //设置操作状态
void saveDraw(); //保存
void loadDraw(); //通过内部的弹出文件选择框来完成加载
void loadDrawFile(const QString& _filePath); //通过文件全路径字符串来完成文件加载
void setICallBack_GraphOperator(ICallBack_GraphOperator *pGraphOper); //设置图形操作回调接口
void setIsEditMode(bool _mode) { m_bIsEditFlag = _mode; } //设置本窗口是客户端还是编辑模式
void modifyMemberData(const char * mbname, QVariant & data); //修改图形窗口的属性接口
QSize getGraphSceneSize();
protected:
virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent); //鼠标双击事件
void setModifyFlag (bool ismodify) {m_bModified=ismodify;} //设置图形内容是否修改标志
private:
void pasteObjs(); //粘贴对象
void delObjs(); //删除对象
void combinationObjs(); //组合对象
void freeGroupBlock(); //拆分块对象
void freeGroupChild(ShapeBase_HX * groupObj); //拆分出块对象的孩子--实际块拆分动作--只拆分一层
QGraphicsItem * getTopParent(QGraphicsItem * item); //通过该函数获取图元对象的最顶层父节点
ShapeBase_HX * createShape(std::string classStr); //根据指定的图元类字符串创建对象的图元对象
void mouseReleaseEvent_right(QGraphicsSceneMouseEvent *mouseEvent, OperatorST optAct); //鼠标右键弹起
void mouseReleaseEvent_left(QGraphicsSceneMouseEvent *mouseEvent, OperatorST optAct); //鼠标左键弹起
bool checkItemContain(QGraphicsItem *item, QList <QGraphicsItem * >&itemList); //检查指定图元条目是否在对应的图元队列中
void setSelectedFocus(QList <QGraphicsItem * >&itemList, bool selFlag); //清除或者设定指定对象列表的所选对象的焦点
void removeDuplicate(QList <QGraphicsItem * >&itemList); //消除列表中的重复【如选中块后又选中块内的对象】
//返回对象属性列表
virtual QList <PropertyItemData> getPropertyList();
//virtual void loadGroup(QDomElement& dom); //加载组图元
private slots:
void handleItemChange(QGraphicsScene * scene,int pointIndex,QPointF offset,int operState);
void handleItemHoverEnter(QGraphicsScene * scene,ShapeBase_HX * obj);
void handleItemHoverLeave(QGraphicsScene * scene,ShapeBase_HX * obj);
signals:
//void createItemFinished();
private:
bool m_bIsEditFlag; //编辑还是运行---处于编辑还是客户端标志
OperatorST m_operatorST; //操作动作编号
MouseST m_mouseST; //鼠标的状态
QPointF m_currentPos; //鼠标当前的位置
QPointF m_bouncePos; //鼠标弹起位置
SelectedST m_mSelectedST; //当前鼠标选中对象的状态
bool m_bRightBtnPaste; //鼠标右键点击粘贴标志
int m_iNewShapeType; //这里代表的是对象的类型-对象新生成的类型
ShapeBase_HX* m_pCurrShapeItem; //当前创建或者选中的单个图元对象
ICallBack_Graphview m_IViewCallBack; //场景对象与视图对象的回调接口结构对象
bool m_bModified; //内容被修改标志
GraphBaseCfg m_nodeConfig; //本场景自身配置属性结点
ICallBack_GraphOperator* m_pGraphOperator; //图形操作接口
//--------------------------------------------------------------------------------------------------------------------
//加载时拓扑线对象的容器
set<ShapeBase_HX*> m_topologyLineSet; //保存拓扑线的集合容器
ShapeBase_HX* m_multiTextObj; //被双击的多行文本对象的指针,主要用来实现多行文本内容的输入需要,输入实现流程其具体看类说明
};
#endif // FHGRAPHSCENE_H
因代码太长,就不贴出全部CPP内容了。仅仅贴出鼠标绘制相关函数
void GraphScene_HX::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//记录鼠标按下状态,并且记录鼠标按下的位置点
m_mouseST = ST_MOUSEPRESS;
m_currentPos = mouseEvent->scenePos();
if (!m_bIsEditFlag)
{
//如果是客户端则直接返回选中的状态
QGraphicsScene::mousePressEvent(mouseEvent); //同样需要通过反射代理将击中操作返回到外部
return;
}
//根据操作动作标志来区别处理
switch (m_operatorST)
{
case ST_DRAWING: //客户端模式不会存在对象生成的操作
{
((QGraphicsView*)parent())->setDragMode(QGraphicsView::NoDrag);
//如果不是多点模式对象生成
if (!GVarsDataset_HX::multipointFlag()) //这里可以由GVarsDataset_HX::multipointFlag来判断是否是首次按下鼠标
{
m_pCurrShapeItem = createShape(ShapeClassName[m_iNewShapeType]);
if (m_pCurrShapeItem)
{
m_pCurrShapeItem->changData_new(m_currentPos, m_mouseST); //多态调用
addItem(m_pCurrShapeItem);
setModifyFlag(true); //设置图形被修改标志
}
}
else //这里是需要进行多点操作的对象处理
{
if(xtext==m_iNewShapeType) //文本对象
{
if(GVarsDataset_HX::multistepFlag() == SHOW_EDIT) //文本对象首先不能生成对象-这里显示文本框用来输入文本对象的文本内容
{
QPointF tp = mouseEvent->scenePos();
//记录文本对象需要的位置信息
GVarsDataset_HX::setTextPos (tp);
// 这里通过回调策略对象中显示文本输入框函数显示输入文本框对象
if (m_IViewCallBack.TextEditShow)
{
QPointF tpt = QPointF(tp.rx(), tp.ry());
m_IViewCallBack.TextEditShow (m_IViewCallBack.ptrOwener, tpt);
}
//记录文本对象生成到最后结束的步骤
GVarsDataset_HX::setMultistepFlag (NewTextFinish);
}
else if(GVarsDataset_HX::multistepFlag() == NewTextFinish) //获取编辑框里面的文字 ,这里生成文本对象
{
QString textStr;
if (m_IViewCallBack.inputTextGet)
{
textStr = m_IViewCallBack.inputTextGet (m_IViewCallBack.ptrOwener);
}
GVarsDataset_HX::setMultistepFlag (-1);
//这里实际创建文本对象
m_pCurrShapeItem = createShape(ShapeClassName[xtext]);
if (m_pCurrShapeItem)
{
QVariant tTextVal(textStr.toStdString().c_str());
m_pCurrShapeItem->setMemberData(pty_text, tTextVal);
QVariant tFontVal(GVarsDataset_HX::defaultFont());
m_pCurrShapeItem->setMemberData(pty_font, tFontVal);
QFontMetrics fm(GVarsDataset_HX::defaultFont());
int w = fm.lineSpacing();
QPointF tpos = GVarsDataset_HX::textPosition();
//tpos.setX(tpos.x() - w); //2020.3.26
QVariant tTextPosition(tpos);
m_pCurrShapeItem->setMemberData(pty_position, tTextPosition); //这里设置文本对象的位置
addItem(m_pCurrShapeItem);
setModifyFlag(true); //设置图形被修改标志
//文本对象生成完毕,重新设置为橡皮筋模式
((QGraphicsView*)parent())->setDragMode(QGraphicsView::RubberBandDrag);
//通知主窗口进行窗口代理类型切换到图形编辑代理
//通过图形操作接口将选中对象的操作反馈到外部【将对象的属性显示到属性框上去】
if (m_pGraphOperator)
{
m_pGraphOperator->mouseClickObj(m_pCurrShapeItem, MOUSE_LEFT);
}
}
m_operatorST = ST_SELECT;
m_pCurrShapeItem = 0;
m_mouseST = ST_MOUSEUNKNOWN;
m_mSelectedST = ST_SELECTED_ONE;
GVarsDataset_HX::setMultipointFlag (0);
}
}
else if (xpolyline == m_iNewShapeType) //折线对象
{
if (GVarsDataset_HX::multistepFlag() == -1) //这里可以由GVarsDataset_HX::multistepFlag来判断是否是首次按下鼠标
{
m_pCurrShapeItem = createShape(ShapeClassName[m_iNewShapeType]); //首次则创建图元 xpolyline
if (m_pCurrShapeItem)
{
m_pCurrShapeItem->changData_new(m_currentPos, m_mouseST); //刚创建对象时的第一次chang,设置2个点,并且multistepFlag=0
addItem(m_pCurrShapeItem);
setModifyFlag(true); //设置图形被修改标志
}
}
if (GVarsDataset_HX::multistepFlag() == 1) //标志为1代表对象生成的第一次按下弹起结束,这里 是代表折线对象需要新增点
{
if (m_pCurrShapeItem)
{
if (mouseEvent->button() == Qt::LeftButton) {
QPointF tPos = mouseEvent->scenePos();
m_pCurrShapeItem->changData_new(tPos, ST_MOUSEPRESS); //多点生成对象方式 这里添加一个点
}
}
}
}
else if (xarc == m_iNewShapeType) {
if (GVarsDataset_HX::multistepFlag() == -1) //这里可以由GVarsDataset_HX::multistepFlag来判断是否是首次按下鼠标
{
m_pCurrShapeItem = createShape(ShapeClassName[m_iNewShapeType]); //首次则创建图元 xarc
if (m_pCurrShapeItem)
{
m_pCurrShapeItem->changData_new(m_currentPos, m_mouseST); //刚创建对象时的第一次chang,设置2个点,并且multistepFlag=0
addItem(m_pCurrShapeItem);
setModifyFlag(true); //设置图形被修改标志
}
}
else {
if (m_pCurrShapeItem) //注意,曲线绘制的多步状态的步进变化在曲线图元changData_new中已经依次完成,
{
m_pCurrShapeItem->changData_new(m_currentPos, m_mouseST); //进行曲线的进一步绘制
}
}
}
}
}break;
case ST_FROMTEMPLATE:
case ST_LOADDRAW:
{
m_operatorST = ST_SELECT;
}break;
default:
{}
}
QGraphicsScene::mousePressEvent(mouseEvent);
}
void GraphScene_HX::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
m_mouseST = ST_MOUSEMOVE;
if (m_bIsEditFlag) {
switch (m_operatorST)
{
case ST_DRAWING:
{
((QGraphicsView*)parent())->setDragMode(QGraphicsView::NoDrag);
if (m_pCurrShapeItem)
{
QPointF tPos = mouseEvent->scenePos();
m_pCurrShapeItem->changData_new(tPos, m_mouseST);
}
}
break;
case ST_FROMTEMPLATE:
m_operatorST = ST_SELECT;
break;
case ST_MAPROAM: //漫游状态
{
return;
}break;
default:
{}
}
}
//如果是客户端则直接选中的状态
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
void GraphScene_HX::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
m_mouseST = ST_MOUSERELEASE;
m_bouncePos = mouseEvent->scenePos();
if (!m_bIsEditFlag) {
//如果是客户端则直接选中的状态
QGraphicsScene::mouseReleaseEvent(mouseEvent);
return;
}
switch (m_operatorST){
case ST_DRAWING:
{
((QGraphicsView*)parent())->setDragMode(QGraphicsView::NoDrag);
if (!GVarsDataset_HX::multipointFlag ()) //不是多点生成对象--按正常流程拖拽鼠标弹起,结束对象的生成-并且退出生成对象过程,进入选中对象模式
{
if (m_pCurrShapeItem)
{
QPointF tPos = mouseEvent->scenePos();
m_pCurrShapeItem->changData_new(tPos, m_mouseST);
m_operatorST = ST_SELECT;
m_mouseST = ST_MOUSEUNKNOWN;
m_mSelectedST = ST_SELECTED_ONE;
//通过图形操作接口将选中对象的操作反馈到外部【将对象的属性显示到属性框上去】
//通知图形外框窗口--最后通知到达主窗口,以便于主窗口进行代理窗口类型切换--在代理中实现函数发送信号通知主窗口切换代理
if (m_pGraphOperator) {
m_pGraphOperator->mouseClickObj(m_pCurrShapeItem, MOUSE_LEFT);
m_pCurrShapeItem->setSelected(true);
}
m_bModified = true;
}
((QGraphicsView*)parent())->setDragMode(QGraphicsView::RubberBandDrag);
}
else
{
if (mouseEvent->button() == Qt::RightButton) //这里使用右键结束多点对象的生成
{
((QGraphicsView*)parent())->setDragMode(QGraphicsView::NoDrag);
GVarsDataset_HX::setMultistepFlag(0); //清除多点有效标志
if (m_pCurrShapeItem)
{
m_operatorST = ST_SELECT;
m_mouseST = ST_MOUSEUNKNOWN;
m_mSelectedST = ST_SELECTED_ONE;
ShapeBase_HX::estopHit = 0;
QPointF tPos = mouseEvent->scenePos();
m_pCurrShapeItem->changData_new(tPos, ST_MOUSERELEASE);
//通过图形操作接口将选中对象的操作反馈到外部【将对象的属性显示到属性框上去】
//通知图形外框窗口--最后通知到达主窗口,以便于主窗口进行代理窗口类型切换--在代理中实现函数发送信号通知主窗口切换代理
if (m_pGraphOperator)
{
m_pGraphOperator->mouseClickObj(m_pCurrShapeItem, MOUSE_LEFT);
m_pCurrShapeItem->setSelected(true);
}
m_bModified = true;
}
((QGraphicsView*)parent())->setDragMode(QGraphicsView::RubberBandDrag);
}
else { //鼠标左键弹起
if (GVarsDataset_HX::multistepFlag() == 0) //标志为1代表对象生成的第一次按下弹起结束,这里 是代表需多点步骤生成对象要新增点
{
if (m_pCurrShapeItem)
{
GVarsDataset_HX::setMultistepFlag(1); //第一次按下弹起状态结束转换为 多点对象鼠标左键再次按下代表进入新增点模式
}
}
else if (GVarsDataset_HX::multistepFlag() == -1 && xarc == m_iNewShapeType) { //注意,曲线绘制的多步状态在曲线图元中已经完成,当标志为由-1到-1表示生成结束
if (m_pCurrShapeItem)
{
m_pCurrShapeItem->setSelected(true);
m_operatorST = ST_SELECT;
m_mouseST = ST_MOUSEUNKNOWN;
m_mSelectedST = ST_SELECTED_ONE;
//通过图形操作接口将选中对象的操作反馈到外部【将对象的属性显示到属性框上去】
//通知图形外框窗口--最后通知到达主窗口,以便于主窗口进行代理窗口类型切换--在代理中实现函数发送信号通知主窗口切换代理
if (m_pGraphOperator) {
m_pGraphOperator->mouseClickObj(m_pCurrShapeItem, MOUSE_LEFT);
m_pCurrShapeItem->setSelected(true);
}
}
}
}
}
}break;
case ST_SELECT:
case ST_COPY:
{
if (mouseEvent->button() == Qt::LeftButton) //鼠标左键
{
mouseReleaseEvent_left(mouseEvent, m_operatorST);
}
else if (mouseEvent->button() == Qt::RightButton) //鼠标右键处理
{
mouseReleaseEvent_right(mouseEvent, m_operatorST);
}
}break;
case ST_FROMTEMPLATE:
{
m_operatorST = ST_SELECT;
}break;
case ST_MAPROAM: //漫游状态
{
return;
}break;
default:
{
}
}
if (m_multiTextObj !=nullptr) { //如果多行文本被双击了,每次鼠标弹起时根据鼠标点位置检查是否还是处于多行文本对象的范围
//如果不再范围内则从视图上显示的多行文本编辑框中获取text文本,设置到这个对象中,然后将该对象指针清空,且隐藏文本输入框
QString textStr;
if (m_IViewCallBack.inputMultiTextGet)
{
if (m_pCurrShapeItem != m_multiTextObj) {
//从多行文本框中获取输入的文本,并且设置到对应图元中,通知隐藏多行文本输入框
textStr = m_IViewCallBack.inputMultiTextGet(m_IViewCallBack.ptrOwener);
QVariant textVal(textStr);
m_multiTextObj->setMemberData(pty_text, textVal);
m_multiTextObj = nullptr;
}
}
}
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
void GraphScene_HX::mouseReleaseEvent_left(QGraphicsSceneMouseEvent *mouseEvent, OperatorST optAct)
{
Q_UNUSED(mouseEvent);
//鼠标左键弹起处理
switch (optAct)
{
case ST_SELECT:
{
QList <QGraphicsItem *> list = this->selectedItems();
g_operList = list;
removeDuplicate(g_operList); //选中多个对象就进行重复消除,针对选中块后又局部选中块内对象
int count = g_operList.count();
if (count > 1) //多选
{
m_operatorST = ST_SELECT; //框选选中多个item
m_mSelectedST = ST_SELECTED_MULTI;
//当选中多个对象时,在外部主窗口应该将copy菜单使能
if (m_pGraphOperator)
{
m_pGraphOperator->mouseHitMulRelease(g_operList, m_operatorST, MOUSE_LEFT);
}
}
else if (count == 1) //单选
{
m_operatorST = ST_SELECT; //框选选中单个item
m_mSelectedST = ST_SELECTED_ONE;
if (m_pGraphOperator)
{
m_pCurrShapeItem = dynamic_cast<ShapeBase_HX *>(g_operList.at(0));
//拓扑自动关联检查操作开始
string classStr = m_pCurrShapeItem->objClassStr();
if (classStr.compare("ShapeTopology_HX") == 0)
{ //拓扑图元
//检查是不是拓扑图元,是拓扑图元则获取其关联的拓扑线对象保存到容器中,然后通过拓扑线容器
QList<ShapeBase_HX*> TopoList = m_pCurrShapeItem->getTopologyList();
int count = TopoList.size();
for (int i = 0; i < count; ++i) {
TopoList.at(i)->topologyRelevanceCheck();
}
}
else if (classStr.compare("ShapeTopologyBrokenLine_HX") == 0) { //拓扑直线
m_pCurrShapeItem->topologyRelevanceCheck();
}
else if (classStr.compare("ShapeTopologyLine_HX") == 0) { //拓扑直线
m_pCurrShapeItem->topologyRelevanceCheck();
}
//拓扑自动关联检查操作结束
//当选中对象时,在外部主窗口应该将copy菜单使能
//通过图形操作接口将选中对象的操作反馈到外部【将对象的属性显示到属性框上去】
m_pGraphOperator->mouseClickObj(m_pCurrShapeItem, MOUSE_LEFT);
}
}
else if (count == 0) //经过选择列表返回空来判断
{
//这里没有选中对象,其实应该填充窗口的属性到属性框
m_operatorST = ST_SELECT;
m_pCurrShapeItem = NULL;
m_mSelectedST = ST_SELECTED_NULL;
if (m_pGraphOperator)
{
//当没有选中对象时,在外部主窗口应该将copy菜单屏蔽,并且显示图形窗口的属性
//m_pGraphOperator->mouseClickObj(0, MOUSE_LEFT);
QList <PropertyItemData> list = this->getPropertyList();
QList<QGraphicsView *> viewlist = this->views();
if (viewlist.count()>0) {
QGraphicsView * tView = viewlist.at(0);
QWidget * pWidget = tView->parentWidget();
GraphFace_HX* pGraphFace_HX = (GraphFace_HX*)pWidget;
m_pGraphOperator->mouseClickGraph(pGraphFace_HX, list, MOUSE_LEFT);
}
}
}
}break;
case ST_COPY:
{
QGraphicsItem * item = this->itemAt(m_bouncePos, QTransform());
if (!item)
m_operatorST = ST_SELECT;
}break;
}
}
void GraphScene_HX::mouseReleaseEvent_right(QGraphicsSceneMouseEvent *mouseEvent, OperatorST optAct)
{
Q_UNUSED(mouseEvent);
//鼠标右键弹起处理
switch (optAct)
{
case ST_SELECT:
{
int objCount = 0;
QGraphicsItem * item = this->itemAt(m_bouncePos, QTransform());
objCount = g_operList.count();
if (!item)
{ //本次右键没有选中对象,则取消上次选中的对象
g_operList.clear();
}
else
{
//对本次选中的对象进行检查,是否是属于块对象中的,如果是块对象中的对象则应该将选中对象变换为该对象的所属块对象
item = getTopParent(item);
//本次右键选中一个对象的处理
if (objCount>1) //上次选中多个对象
{//ok
bool retContain = checkItemContain(item, g_operList);
if (retContain) //本次右键击中的对象是属于上次选中的对象集合中--ok
{
removeDuplicate(g_operList);
m_pGraphOperator->mouseSlectedState(ST_SELECTED_MULTI);
m_pGraphOperator->mouseHitMulRelease(g_operList, m_operatorST, MOUSE_RIGHT);
}
else //本次右键击中的对象不属于上次选中的对象
{
setSelectedFocus(g_operList,0); //清除上次选中对象的焦点
g_operList.clear(); //清除上次选中的对象
g_operList.append(item); //将本次选中的对象加入选中列表
item->setSelected(1); //将本次右键选中的对象设置焦点
m_pCurrShapeItem = dynamic_cast<ShapeBase_HX *>(item);
m_pGraphOperator->mouseSlectedState(ST_SELECTED_ONE);
m_pGraphOperator->mouseClickObjRelease_right(m_pCurrShapeItem);
}
}
else if (objCount == 1) //上次选中单个对象--本次击中一个对象
{//ok
QGraphicsItem * preItem = g_operList.at(0);
if (preItem != item)
{
if (0 != m_pCurrShapeItem) {
m_pCurrShapeItem->setSelected(0);//将上次选中的图元取消焦点
m_pCurrShapeItem = dynamic_cast<ShapeBase_HX *>(item);
m_pCurrShapeItem->setSelected(1);//将上本次选中的图元设置选中焦点
g_operList.clear();
g_operList.append(item);
}
}
m_pGraphOperator->mouseSlectedState(ST_SELECTED_ONE);
m_pGraphOperator->mouseClickObjRelease_right(m_pCurrShapeItem);
}
else
{//ok
//左键上次没有选中对象,但是本次右键选中了对象,则直接按本次选中对象进行处理
m_pCurrShapeItem = dynamic_cast<ShapeBase_HX *>(item);
if(m_pCurrShapeItem != nullptr) {
m_pCurrShapeItem->setSelected(1);
m_pGraphOperator->mouseSlectedState(ST_SELECTED_ONE);
m_pGraphOperator->mouseClickObjRelease_right(m_pCurrShapeItem);
g_operList.append(item);
}
}
}
}break;
case ST_COPY: //能进入该标志代表着已经点击了CPOY菜单,此时如果是在场景上操作应该弹出粘贴菜单
{
int objCount = 0;
objCount = g_operList.count();
QGraphicsItem * item = this->itemAt(m_bouncePos, QTransform());
if (!item) //本次没有选中对象
{
if (objCount>1) //上次选中了多个对象,但本次右键没有选中对象,将上次选中的多个对象设置焦点,正常弹出粘贴菜单
{
m_pGraphOperator->mouseSlectedState(ST_SELECTED_MULTI);
setSelectedFocus(g_operList, 1);
m_pGraphOperator->mouseHitMulRelease(g_operList, m_operatorST, MOUSE_RIGHT);
}
else if (objCount == 1) //上次选中了单个对象,但本次没有选中对象,将上次选中的多个对象设置焦点,正常弹出粘贴菜单
{
m_pGraphOperator->mouseSlectedState(ST_SELECTED_ONE);
setSelectedFocus(g_operList, 1);
m_pGraphOperator->mouseHitMulRelease(g_operList, m_operatorST, MOUSE_RIGHT);
}
else
{
//上次没有选中对象,本次没有选中对象,因此没有操作
}
}
else //本次选中对象
{
//对本次选中的对象进行检查,是否是属于块对象中的,如果是块对象中的对象则应该将选中对象变换为该对象的所属块对象
item = getTopParent(item);
if (objCount>1)
{ //上次选中了多个对象,本次右键选中一个对象,判断选中对象是否属于上次多选的对象列表
bool retContain = checkItemContain(item, g_operList);
if (retContain) //本次击中的对象是属于上次选中的对象集合中--ok
{
m_pGraphOperator->mouseSlectedState(ST_SELECTED_MULTI);
m_pGraphOperator->mouseHitMulRelease(g_operList, m_operatorST, MOUSE_RIGHT);
}
else //因为上次选中多个对象,并且已经点击了拷贝,本次右键击中对象不属于上次选中对象列表,所以拷贝状态失效,重新进入选中状态
{
setSelectedFocus(g_operList,0);
g_operList.clear();
m_operatorST = ST_SELECT;
g_operList.append(item);
clearFocus();
item->setSelected(1);
m_pCurrShapeItem = dynamic_cast<ShapeBase_HX *>(item);
m_pGraphOperator->mouseSlectedState(ST_SELECTED_ONE);
m_pGraphOperator->mouseHitMulRelease(g_operList, m_operatorST, MOUSE_RIGHT);
}
}
else if (objCount == 1)
{
QGraphicsItem * preItem = g_operList.at(0);
if (preItem != item)
{
//因为上次选中一个对象,并且已经点击了拷贝,但本次右键点击了另外一个对象,所以拷贝状态失效,重新进入选中状态
m_operatorST = ST_SELECT;
m_pCurrShapeItem->setSelected(0); //将上次选中的图元取消焦点
m_pCurrShapeItem = dynamic_cast<ShapeBase_HX *>(item);
m_pCurrShapeItem->setSelected(1); //将上本次选中的图元设置选中焦点
g_operList.clear();
g_operList.append(item);
}
m_pGraphOperator->mouseSlectedState(ST_SELECTED_ONE);
m_pGraphOperator->mouseHitMulRelease(g_operList, m_operatorST, MOUSE_RIGHT);
}
else
{
//上次没有选中对象,本次没有击中对象则什么也不做
}
}
}break;
}
}
GraphScene_HX提供的生成图元对象接口,供外部调用,通知场景实际生成图元
void GraphScene_HX::shapeNewType (const int &shapeType)
{
if (m_pCurrShapeItem)
{
m_pCurrShapeItem->setSelected(0); //取消选中的图元
m_pCurrShapeItem = NULL; //制空
}
m_iNewShapeType = shapeType;
m_operatorST = ST_DRAWING;
//这里是先将是否多点标志清零,防止点击了文本对象或者多点对象,但未进行生成操作,而直接点非单点对象
//防止这时无法生成单点对象
GVarsDataset_HX::setMultipointFlag(0);
if (xtext==m_iNewShapeType) //创建文本对象
{
GVarsDataset_HX::setMultipointFlag (1);
GVarsDataset_HX::setMultistepFlag (0); //ST_DRAWING && GVarsDataset_HX::islotsPointObj=0时代表的是文本对象的生成
}
else if( xpolyline == m_iNewShapeType || xarc == m_iNewShapeType) //创建自由多边形,折线 ,创建弧线
{
GVarsDataset_HX::setMultipointFlag (1);
GVarsDataset_HX::setMultistepFlag(-1); //代表的是需要多步骤生成标志
}
}
因本项目代码量过多,所以也就不在写了,反正下载分很少,具体参看代码吧。
本样例下载地址:
https://download.csdn.net/download/wangxuejun1972/19974245