Qt 图形视图 /图形视图框架坐标系统的设计理念和使用方法

文章目录

  • 概述
  • Qt 坐标系统
  • 图形视图的渲染过程
  • Item图形项坐标系
  • Scene场景坐标系
  • View视图坐标系
  • map坐标映射
    • 场景坐标转项坐标
    • 视图坐标转图形项坐标
    • 图形项之间的坐标转换
  • 其他

概述

The Graphics View Coordinate System 图形视图坐标系统是Qt图形视图框架的重要组成部分,按照帮助文档中的划分,其与 The Graphics View Architecture 图形视图架构是在一个层级上的,主要包含图形项坐标系、场景坐标系、视图坐标系、坐标映射等内容。本文将结合帮助文档、Qt编程书籍、Qt示例程序等研习图形视图框架坐标系统的设计理念和使用方法。
在这里插入图片描述
补充,20230501,
编写HelpDoc的人,大概率是极为熟悉该模块的开发者,他们编写文档时的立意是全局化的,很有可能在写第一个章节时,潜移默化的渗透少许第二、甚至第三四五章节的知识点,尽管他们可能已经在尽力避免类似事情的发生,但那很难做到。所以有很多帮助内容,初读时很有跳跃感,比较吃力。此时先读个大概,了解全局,再回头细读,可能是一种可行的方法。

转载请标明原文链接,
https://blog.csdn.net/quguanxin/category_12597847.html

Qt 坐标系统

图形视图框架的坐标系统基于Qt大框架的坐标系统。逻辑坐标系的使用,使绘制代码独立于绘画设备的分辨率和坐标系,开发者可以在逻辑坐标系中编写绘制代码,QPainter 会自动将其映射到实际的设备坐标系。
在Qt框架下,QPainter类实际掌控着Qt坐标系统,并且它与 QPaintDevice 和 QPaintEngine 类共同组成了Qt的绘制系统,而且Qt绘制系统竟然还有一个名字,Arthur 亚瑟,这可能是一个项目代号,具体不得而知。QPainter 用以执行绘制操作,QPaintDevice 是一个二维绘制空间,QPainter就是在该空间进行绘制操作,QPaintEngine为painter在不同设备上的绘制操作提供了interface接口。
QPaintDevice类是所有支持绘制的类的基类,它的绘画能力被 QWidget, QImage, QPixmap, QPicture, and QOpenGLPaintDevice 继承。一个Qt绘制设备的默认坐标系原点在Device左上角,X轴向右增长,Y轴向下增长。在基于像素的设备上,以像素为默认长度单位,在打印设备上,默认单位是一个点(1/72英寸)。QPainter的逻辑坐标到QPaintDevice的物理坐标的映射,由QPainter的变换矩阵、视口和窗口处理。
在这里插入图片描述
图形视图坐标系统以Qt坐标系统为基础,不支持y轴向上增长的反向坐标系(inverted Y-axis coordinate system)。在传统的笛卡尔坐标系中,坐标原点 (0,0) 位于左下角,y轴向上增长,也就是说 y 值越大,坐标点在垂直方向上越高。而在Qt中采用Y轴向下增长,其原因可能是,在绘制图像时,通常图像的第一行像素位于最上方,向下递增,这符合存储图像数据的方式,如此更加自然。

图形视图的渲染过程

在这里插入图片描述
前边提到过,图形视图使用Qt的坐标系统,上述图片段落大意是将图形视图框架的渲染过程代入了 Qt 绘制系统。Qt图形视图框架下,在渲染时,图形视图的场景坐标对应于QPainter的逻辑坐标,视图坐标等同于设备坐标。有关逻辑坐标和设备坐标之间关系的更多内容,在Qt绘制系统的相关文章中讲述。在 ‘绘制’ 这个层次的概念上,Qt图形视图框架并没有脱离QPainter机制,而是在QPainter的基础上进行了扩展和封装,如,自动处理场景的渲染和重绘。

在图形视图中有三个有效effective的坐标系统:图形项坐标系、场景坐标系和视图坐标系。为了简化实现,Qt 图形视图提供了便利函数,允许您在三个坐标系统之间进行映射。

Item图形项坐标系

Item居于live自己的本地坐标系中,可以理解为Item在自己的本地坐标系中运作。图形项坐标系通常以项的中心点 (0,0) 为中心(注意,有的不是哦,后文有提及),这个点也是所有坐标变换的中心。图形项坐标系下的几何图元(Geometric primitives) 通常被称为,点、Item线、Item矩形。
当你创建一个自定义项时,你只需要关注Item本地坐标系即可,场景和视图会帮你执行所有变换。如在 Diagram Scene Example 示例程序中,创建基本流程图形状的代码,是在Item本地坐标系中进行绘制的,根本不用关心场景和视图坐标系的任何。

DiagramItem::DiagramItem(DiagramType diagramType, QMenu *contextMenu, QGraphicsItem *parent) : QGraphicsPolygonItem(parent) {...switch (myDiagramType) {...case Conditional:  //流程图形状,判定框myPolygon << QPointF(-100, 0) << QPointF(0, 100)<< QPointF(100, 0) << QPointF(0, -100)<< QPointF(-100, 0);break;case Step:        //流程图形状,过程框myPolygon << QPointF(-100, -100) << QPointF(100, -100)<< QPointF(100, 100) << QPointF(-100, 100)<< QPointF(-100, -100);break;...}setPolygon(myPolygon);...
}

Qt 图形视图框架在尽力的使用Item本地坐标系坐标交互位置信息,以方便开发者使用。例如,当你 (潜台词,在Item对象中) 接收到鼠标按下或拖拽进入事件时,事件(QGraphicsSceneMouseEvent)对象携带的位置会以Item坐标给出(到图形项对象)。QGraphicsItem::contains() 虚函数用于判断某个点是否在物品内,如果在则返回 true,否则返回 false,该函数的参数为Item坐标系下的坐标。类似地,Item的边界矩形和形状也是以Item坐标表示的。下文以 mousePressEvent 鼠标事件为例,观察该事件在视图、场景和图形项中的运行规律,

//在图形项QGraphicsItem类中
virtual void QGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event);
//在场景QGraphicsScene类中
virtual void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event);
//是视图QGraphicsView类中
virtual void QGraphicsView::mousePressEvent(QMouseEvent *event);

如上函数同为mousePressEvent原型,QGraphicsView 使用QWidget传统的QMouseEvent 事件类型,而 QGraphicsItem 和 QGraphicsScene 使用 QGraphicsSceneMouseEvent 事件类型,后者可传递两种位置信息,如下,
在这里插入图片描述
相关示例代码,放在了后续 ‘View视图坐标系’ 章节中。通过调试信息可确认,在Item对象中接收到的mouseEvent事件,通过 mouseEvent->pos 函数传递出来的是Item坐标系下的坐标。

父坐标 和 子坐标, (同一个视觉位置点的父坐标和子坐标)

在这里插入图片描述

不得不穿插对英语句子的理解,只是个人理解,原谅我英语没学好,
At item’s position is the coordinate of the item’s center point in its parent’s coordinate system。
结合上图中QGraphicsItem的构造函数,对上文的一个合理的释义,可能是,“(this这个) Item 的位置” 是指 (this) Item 的中心点在其父级(父Item) 坐标系中的坐标,有时也简称为父坐标。从某种意义上来说,若一个Item没有任何父(Item),那么场景将被当做是它的父(Item),顶层Item的位置以场景坐标来表示。整体上,这一小段Doc,目的是传递一个父坐标的概念,即,Item的(pos函数)位置是该Item原点在其父Item坐标系中的坐标。
在这里插入图片描述
理解上边这段话,着实花费了我不少时间。如上English,Child是子图形项的意思,parent是 this Item‘s parant 父项的意思,复数形式的coordinates可能通常代表坐标系、单数coordinate可能通常代表坐标点。与前文那句英文类似,这段话里似乎也藏了许多潜台词,故此晦涩。本段开始就告诉我们,子坐标系是相对于(be relative to)父坐标系的,真不太好理解。接着文中提到,如果子级Item未经过变换,则(潜台词,在窗口中,用户视觉上是同一个点,如下图的红点)子坐标与父级坐标之间的差距,与(the two)Items(前面提到的Child和Parent)在父坐标中的距离(也即父Item和子Item中心点之间的距离)相同。只能结合下文具体的例子继续理解,
在这里插入图片描述
如果一个未经过变换的子Item恰好位于其父项的中心点上,那么两个项的坐标系统将是相同的。然而,如上图中,红色矩形代表的子级Item,其在父级Item坐标系下的位置是 (10, 0),而不是 (0,0) 原点。此时,视觉红点,其在子级Item坐标系下的坐标为(0, 10),同时它在父级坐标系下的坐标是(10, 10)。如此,同样一个红点的子坐标(0,10)和父坐标(10,10)之间的差异便是(10,0),这个(10,0)也正好是子项和父项之间的距离(两个矩形中心点的距离),这个距离是10个单位长度。而文中 the difference between a child coordinate and a parent coordinate,并没有特定的指代某点,应该是指子Item上的任意的一个点,即,从视图上看到的任意一个点,其子项坐标与父项坐标的差值是一样的,都是子项和父项原点之间的距离。

由于项位置和项变换是与父项相对的,子项的坐标系并不不会受到父项坐标系变换的影响,尽管父项的变动隐式的转换了子项。还在上面的例子中,即使父项被旋转和缩放,子项坐标系下的(0,10)点依然将对应父项坐标系下的(10,10)点。然而,相对于场景,子项将跟随父项的变换和位置。如果父项被缩放2倍,那么,子项在场景中的坐标将变成(20,0),子项坐标系下的(10,0),在父项坐标系下对应的位置依然是(20,0),在场景坐标系中对应的点是(40,0)。

行文至此,我们再倒回头来聊聊前面没理解的 “子坐标系是相对于父坐标系的”,其含义是,无论父级图形项如何缩放、旋转等变动,子项在父项做标系下的位置不变,子项坐标系下的点,其对应的父项坐标系下的坐标也还是原来的值,没有变化,这就是所谓的相对性,即(子项任意可视点的)子项坐标系下的坐标与对应的父项坐标系下的坐标都还是原来的坐标。与上述相对性对立的,是子项相对于场景/绘制设备的绝对位置和变换则受父项变换的影响。可以不太精确的定义两个名词,相对坐标系和绝对坐标系。这种父子层级结构让坐标变换更加清晰可控,子项目可以独立于父项的变换来定义自身坐标,但最终的绝对变换位置仍由父项的属性确定。

除了少数例外( 如QGraphicsItem::pos() )之外,QGraphicsItem的函数都在项坐标系中运行,不受该项或其任何父项目的变换的影响。例如,一个项的边界矩形(即boundingRect)始终给出项坐标系下的坐标。

Scene场景坐标系

场景表示所有Item图形项的基础坐标系统。场景坐标系统描述的是所有顶层Item的位置,也是所有从View视图到Scene场景的 QGraphicsSceneMouseEvent 场景型事件的基石。场景中的每个项除了具有Item本地位置和本地边界矩形之外,还有一个场景位置和场景边界矩形 (QGraphicsItem::scenePos(), QGraphicsItem::sceneBoundingRect())。如下所示,
在这里插入图片描述

在这里插入图片描述
场景位置描述了项在场景坐标系中的位置,而项的场景边界矩形则构成了QGraphicsScene 确定场景中哪些区域已更改的基础。场景中的更改通过 QGraphicsScene::changed() 信号传播,参数是一个场景矩形列表。通过上图中对 boundingRect() 和 sceneBoundingRect() 的对比,可以直观的看出两者的区别,本地边界矩形被用以QGraphicsView的重绘过程,而场景边界矩形是混合了本地边界矩形和坐标系变换之后的场景坐标系下的矩形,其主要用以探测出项在场景中的位置变动。

QRectF Arrow::boundingRect() const {qreal extra = (pen().width() + 20) / 2.0;return QRectF(line().p1(), QSizeF(line().p2().x() - line().p1().x(), line().p2().y() - line().p1().y())).normalized().adjusted(-extra, -extra, extra, extra);
}

View视图坐标系

视图坐标系是窗口部件的坐标系。视图坐标系下的每个单元都对应一个像素。这个坐标系统的特殊之处在于,它是相对于 widget部件 或 viewport视口的,不受被观察场景的影响。QGraphicsView 的viewport 视口的左上角坐标永远是(0,0),右下角坐标永远是(视口宽度,视口高度)。

所有鼠标事件和拖放事件最初都是以视图坐标接收的,你需要将这些坐标映射到场景中,以便与Item项交互。也可以这么理解,用户的键鼠事件等最早(originally)是由视图对象捕获的,而场景对象可能是作为事件过滤器被安装到关联的视图对象上的,为此我们进行了如下验证。我们扩展 Diagram Scene Example 示例程序,添加从QGraphicsView派生的GraphicsView类,并分别在DiagramItem、DiagramScene、DiagramView 中新实现或修改 mousePressEvent 虚函数,

//派生图形项中对鼠标按压事件的处理
void DiagramItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {qDebug() << "\r\n--#-- DiagramItem::mousePressEvent --#--";qDebug() << "DiagramItem::pos:" << event->pos();qDebug() << "DiagramItem::scenePos:" << event->scenePos();//mustreturn QGraphicsPolygonItem::mousePressEvent(event);
}
//派生场景中对鼠标按压事件的处理
void DiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {qDebug() << "\r\n--#-- DiagramScene::mousePressEvent --#--";qDebug() << "DiagramScene::pos:" << mouseEvent->pos();qDebug() << "DiagramScene::scenePos:" << mouseEvent->scenePos();...
}
//派生视图中对鼠标按压事件的处理
void DiagramView::mousePressEvent(QMouseEvent *event) {qDebug() << " \r\n--#-- DiagramView::mousePressEvent --#--";qDebug() << "DiagramView::localPos:" << event->localPos();qDebug() << "DiagramView::windowPos:" << event->windowPos();qDebug() << "DiagramView::screenPos:" << event->screenPos();qDebug() << "DiagramView::pos:" << event->pos();             //==localPosqDebug() << "DiagramView::globalPos:" << event->globalPos(); //==screenPos//mustreturn QGraphicsView::mousePressEvent(event);
}/* //创建一个ShpeItem点击其靠近右下角的某个位置
--#-- DiagramView::mousePressEvent --#--
DiagramView::localPos: QPointF(355,265)
DiagramView::windowPos: QPointF(564,332)
DiagramView::screenPos: QPointF(664,432)
DiagramView::pos: QPoint(355,265)       //==localPos   //以视图左上角为00
DiagramView::globalPos: QPoint(664,432) //==screenPos--#-- DiagramScene::mousePressEvent --#--
DiagramScene::pos: QPointF(0,0)             //只要点击位置在Item内,则始终为00
DiagramScene::scenePos: QPointF(2573,2562)--#-- DiagramItem::mousePressEvent --#--
DiagramItem::pos: QPointF(75,78)           //以Item中心位置为00原点
DiagramItem::scenePos: QPointF(2573,2562)  //与在场景对象中的捕获坐标值是一致的
*/

通过上运行结果,我们验证了Qt鼠标事件在Qt图形视图框架下的传递顺序,基本可推定,视图对象会在最初捕获用户事件,然后可能的转换过程方案有:转为视图对象直接将 QMouseEvent 转为 QGraphicsSceneMouseEvent 事件;场景对象是视图对象的事件过滤器,图形项对象是场景的事件过滤器,由场景和图形项对象各自分别转换,但由于图形项没有从QObject继承,即无法安装过滤器,这种方案的可能性不大。针对上述问题,后续将研究相关源码,并在其他文章中继续讨论。

当使用未经过任何变换(如缩放、旋转等)的视图observing观察场景时,场景上的unit单位长度对应屏幕上的一个像素。也即在没有应用任何变换的情况下,场景坐标与视图坐标是相互对应的。
例如,如果在场景坐标系中有两个点A(10,10) 和 B(20,10),那么它们在未经变换的视图中的像素距离就是10个像素。这种对应的关系使得最开始在场景上布局和定位图形项时很方便,因为你可以直接以像素为单位来思考。若应用了任何变换,比如缩放或旋转,场景坐标与视图坐标之间就不再是简单的1-1对应关系了,这时就需要使用Qt提供的坐标映射函数来在不同坐标系之间转换。

map坐标映射

通常,在处理场景中的图形项时,需要在场景、图形项、视图之间进行必要的坐标或任意矩形的映射。

例如,当你在 QGraphicsView 的视口中单击鼠标时,可以通过调用 QGraphicsView::mapToScene() 将坐标映射到场景,然后调用QGraphicsScene::itemAt() 来查询光标下的图形项对象。如果你想知道一个图形项在视图视口中的位置,可以在图形项对象上调用QGraphicsItem::mapToScene(),然后在视图上调用QGraphicsView::mapFromScene()。如果你想找出视口中椭圆内的所有图形项,可以将椭圆对象的 QPainterPath 传递给 mapToScene() 映射到场景,然后将映射后的路径传递给QGraphicsScene::items()。

在图形项和场景之间,通过调用QGraphicsItem::mapToScene() and QGraphicsItem::mapFromScene() 函数来相互映射坐标和形状。在图形项与其父项之间通过调用QGraphicsItem::mapToParent() and QGraphicsItem::mapFromParent()来进行映射,或者通过调用 QGraphicsItem::mapToItem() 和 QGraphicsItem::mapFromItem() 在(同一个场景下)不同图形项之间进行映射。所有的映射函数都支持点、矩形、多边形和路径,以 mapToScene 为例,在QGraphicsItem类(图左) 和 QGraphicsView类(图右),有以下6种重载,
在这里插入图片描述
下文将对部分场景下的坐标转换过程,通过示例程序进行说明。

场景坐标转项坐标

在整个Qt图形视图坐标系统中,场景坐标处于至关重要的承上启下的位置。且通过上文可知,场景类并没有直接提供坐标映射的接口,这些映射接口仅存在于QGraphicsItem类 和 QGraphicsView类中。那么在场景对象的处理函数中,如何进行坐标的相关运算呢?
在这里插入图片描述
除了上述 itemAt 函数之外,QGraphicsScene 类还提供了 6 个items(…) 的重载函数、selectedItems() 、views() 等函数,获取目标图形项实例对象。一旦通过场景坐标获取到了图形项实例和视图实例,便可以使用QGraphicsItem类 和 QGraphicsView类中的坐标映射接口。

在 Diagram Scene Example 示例程序,场景类的实现中,就使用了items函数的一个版本,

void DiagramScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) {if (line != 0 && myMode == InsertLine) {QList<QGraphicsItem *> startItems = items(line->line().p1());if (startItems.count() && startItems.first() == line)startItems.removeFirst();QList<QGraphicsItem *> endItems = items(line->line().p2());if (endItems.count() && endItems.first() == line)endItems.removeFirst();...
}

在Diagram Scene Example 示例程序,场景类的实现中,添加如下itemAt函数的使用测试,

void DiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {
...#if 1QTransform deviceTransform; //in paramQGraphicsItem *tempitem = this->itemAt(mouseEvent->scenePos(), deviceTransform);QPointF ItemLocalPt = tempitem->mapFromScene(mouseEvent->scenePos());qDebug() << "DiagramScene::ItemLocalPt:" << ItemLocalPt;#endif
...
}

如上,通过调用场景类的 itemAt 函数,得到 QGraphicsItem 实例对象指针后,便可使用 QGraphicsItem 的映射函数啦。

视图坐标转图形项坐标

如上,视图中提供(be available)了与图形项类相同的映射函数,用于在视图和场景之间进行映射。要从视图映射到图形项,你首先需要将视图坐标映射到场景坐标系,然后再从场景映射到图形项坐标系。我们扩展 Diagram Scene Example 示例程序,

//从QGraphicsView派生DiagramView,替代原view实例 /实现如下非功能的测试函数
void DiagramView::mousePressEvent(QMouseEvent *event) {//QPoint#pos()==QPointF#localPos()qDebug() << "DiagramView# localPos:" << event->pos() << "windowPos:" << event->windowPos() << "screenPos:" << event->screenPos();
#if 1//在视图中得到鼠标位置的Item本地坐标QPointF itemLocalPos;//视图坐标转场景坐标 //event->pos()==event->localPos()QPointF scenePtFromView = mapToScene(event->pos());//获取鼠标事件发生的位置对应的QGraphicsItem实例QGraphicsItem *item = this->scene()->itemAt(scenePtFromView, QTransform());if (item)itemLocalPos = item->mapFromScene(scenePtFromView);qDebug() << "DiagramView# localpos:" << itemLocalPos << "scenePos:" << scenePtFromView;
#endif//mustreturn QGraphicsView::mousePressEvent(event);
}//在DiagramItem追加如下测试函数
void DiagramItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {qDebug() << "DiagramItem# localpos:" << event->pos() << "scenePos:" << event->scenePos();//mustreturn QGraphicsPolygonItem::mousePressEvent(event);
}/* 运行修改后的示例程序,添加一个ShpeItem,点击其中的位置,运行结果如,
DiagramView# localPos: QPoint(187,171) windowPos: QPointF(396,238) screenPos: QPointF(496,338)
DiagramView# localpos: QPointF(5,47) scenePos: QPointF(2405,2468)
DiagramItem# localpos: QPointF(5,47) scenePos: QPointF(2405,2468)
*/

如上代码,将View视图实例QMouseEvent事件中的本地坐标(以视图Widget左上角为原点),也即视图坐标系下的坐标,通过 QGraphicsView::mapToScene 函数转换为场景坐标系下的坐标,进而找到该场景坐标下的Item实例,之后通过调用Item的mapFromScene函数,将场景坐标映射到Item本地坐标系下。通过对比DiagramView对象和DiagramItem对象内的打印信息,可验证转换效果。

在 Drill Down Example 这个示例程序中,

void View::mouseReleaseEvent(QMouseEvent *event) {if (QGraphicsItem *item = itemAt(event->pos())) {if (ImageItem *image = qgraphicsitem_cast<ImageItem *>(item))showInformation(image);}QGraphicsView::mouseReleaseEvent(event);
}

如上,我们细看 QGraphicsView 类,可知,QGraphicsView 自身与 QGraphicsScene 类一样,同样提供了 itemAt(…) 和 items(…) 函数。但是本质上还是要将视图坐标映射到场景坐标系,然后再从场景映射到图形项坐标系。透过以下 QGraphicsView::items 函数的源码,可以证明这一点,

//
QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const {Q_D(const QGraphicsView);if (!d->scene)  return 0;const QList<QGraphicsItem *> itemsAtPos = items(pos);return itemsAtPos.isEmpty() ? 0 : itemsAtPos.first();
}
//
QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const {Q_D(const QGraphicsView);if (!d->scene)return QList<QGraphicsItem *>();// ### Unify these two, and use the items(QPointF) version in// QGraphicsScene instead. The scene items function could use the viewport// transform to map the point to a rect/polygon.if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) {// Use the rect versionQTransform xinv = viewportTransform().inverted();return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)), Qt::IntersectsItemShape, Qt::DescendingOrder, viewportTransform());}// Use the polygon versionreturn d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1), Qt::IntersectsItemShape, Qt::DescendingOrder, viewportTransform());
}

图形项之间的坐标转换

以 Diagram Scene Example Arrow 类的 updatePosition 函数为例,说明Item图形项之间的坐标映射过程。

void Arrow::updatePosition() {//将图形项myStartItem/myEndItem的原点转换为另一个Arrow图形项的坐标系下QLineF line(mapFromItem(myStartItem, 0, 0), mapFromItem(myEndItem, 0, 0));//绘制myStartItem/myEndItem中心点之间的线段setLine(line);
}

如上,为了绘制两个ShapeItem之间的连接线,将myStartItem项的中心原点映射为线段的起点,将myEndItem项的中心原点映射为线段的终点,构建 QLineF 对象并更新 QGraphicsLineItem 类内部维护的线段成员变量。特别需要注意的是,图形项QGraphicsLineItem的本地坐标系和场景坐标系是重合的,因此,this->mapFromItem(myStartItem, 0, 0) 与 myStartItem->mapToScene(QPointF(0,0)) 是相等的,而 this->mapToScene(QPointF(0,0)) 和 this->scenePos() 等返回的都是(0, 0)场景坐标原点。

其他

前面讲到 itemAt 或 items 函数时,它们都有一个QTransform 对象作为输入参数,在 QGraphicsItem 的 mapFromScene、mapToScene等函数实现、QGraphicsItemPrivate的诸多实现中都用到了 QTransform 成员变量或函数参数。该类在 Qt 大框架的模块划分上属于 Qt GUI 模块,隶属 Qt 绘制系统。
要更彻底掌握 Qt 绘制系统坐标系统 和 Qt 图形视图框架坐标系统的设计思路和使用方法,或者仅是想稍加深入的阅读上述框架的源代码,QTransform 是绕不开的知识点。它用于指定二维坐标系统变换,提供了平移(translate)、缩放(scale)、剪切(shear)、旋转(rotate) 和投影(project) 等基本变换操作,常被用于渲染图形时对坐标系统进行变换。QTransform是一个真正的3x3矩阵,允许进行透视投影变换,与QMatrix相比,它功能更加强大,在Qt中QTransform被推荐用作变换类。限于文章篇幅,此文不再对此展开。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/291365.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自定义类型:【联合体和枚举】

一.联合体 1.联合体类型的声明 联合体像结构体一样&#xff0c;也是有一个或者多个成员组成&#xff0c;当然也可以不同的类型。但不同的是&#xff0c;比编译器只为最大的成员分配足够的内存空间&#xff0c;所有成员共用同一块内存空间。所以联合体也叫做&#xff1a;共用体…

[Linux初阶]which-find-grep-wc-管道符命令

目录 一.which 二.find a.-name b.-size 三.grep 四.wc 五.管道符(|) 五.总结 一.which 语法格式: which [命令] Linux中的一个个命令,本体上就是一个个的二进制可执行程序(相当于windows中的.exe文件). 在Linux中,一切皆文件. which命令:用于查看指定命令的可执行…

centos 7 安装磐维(PanWeiDB)数据库(单机)

前置环境准备 文件系统环境要求 文件系统环境所要求的扇区必须为512bytes&#xff0c;查看方法如下&#xff1a; [rootdevops-core-highapp3-b-32 ~]#df -h /apps/ [rootdevops-core-highapp3-b-32 ~]#ll /dev/mapper/vg--docker-lvapp [rootdevops-core-highapp3-b-32 ~]#f…

C++命名空间详解

背景 大型程序往往会使用多个独立开发的库&#xff0c;这些库又会定义大量的全局名字&#xff0c;如类、函数和模板等。当应用程序用到多个供应商提供的库时&#xff0c;不可避免地会发生某些名字相互冲突的情况。 多个库将名字放置在全局命名空间中将引发命名空间污染。 传…

dnf手游攻略:如何利用PVP竞技赛季赚取泰拉与金币?

在DNF手游中&#xff0c;PVP竞技赛季是玩家赚取泰拉和金币的重要途径之一。本攻略将介绍如何通过参与PVP竞技赛季来获取丰厚的游戏收益&#xff0c;帮助玩家在游戏中获得更多的经济收益和游戏资源。 一、认识PVP竞技赛季 PVP竞技赛季是DNF手游中的一个重要活动&#xff0c;每个…

FFmpeg将绿幕视频处理成透明视频播放

怎么在网页端插入透明视频呢&#xff0c;之前在做Web3D项目时&#xff0c;使用threejs可以使绿幕视频透明显示在三维场景中&#xff0c;但是在网页端怎么让绿幕视频透明显示呢&#xff1f; 如图上图&#xff0c;视频背景遮挡住后面网页内容 想要如下图效果 之前有使用过ffmpeg…

选择华为HCIE培训机构有哪些注意事项

选择软件培训机构注意四点事项1、口碑&#xff1a;学员和社会人士对该机构的评价怎样&#xff1f; 口碑对于一个机构是十分重要的&#xff0c;这也是考量一个机构好不好的重要标准&#xff0c;包括社会评价和学员的评价和感言。誉天作为华为首批授权培训中心&#xff0c;一直致…

Vue中使用Vuex(超详细)基本使用方法

在vue中使用vuex&#xff0c;不同的vue版本要对应使用不同的vuex&#xff0c;在这里不做详情介绍&#xff0c;想具体了解的&#xff0c;请自行度娘或者必应一下。 在使用vuex之前&#xff0c;我们创建一个新的项目&#xff0c;这里我们使用的是vue的脚手架创建一个vue项目。 …

基于java高校社团招新系统设计与实现

摘要 &#xff1a;大学学生社团的不断壮大发展&#xff0c;让对社团的招新管理越来越重要&#xff0c;如何高效的管理社团&#xff0c;促进社团有效的运行和发展变得尤为关键。学生社团在学生的成长发展过程中有着一定的积极作用&#xff0c;要发挥好社团的优势&#xff0c;管…

机器学习基础——模型评估与选择(部分)

目录 一、前言&#xff1a;误差与拟合 &#xff08;一&#xff09;经验误差 &#xff08;二&#xff09;过拟合、欠拟合 二、评估方法 &#xff08;一&#xff09;评估总体的思路 &#xff08;二&#xff09;如何划分训练集和测试集 1.留出法 2.k折交叉验证 3.自助法 …

Matlab之求直角坐标系下两直线的交点坐标

目的&#xff1a;在直角坐标系下&#xff0c;求两个直线的交点坐标 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;直线A上的点坐标&#xff1b; AngleA&#xff1a;直线A的倾斜角&#xff0c;单位度&#xff1b; PointB&#xff1a;直线B上的点坐标&#xf…

Game Audio Programming

音频编程时游戏开发中最容易忽略&#xff0c;学习资源又是很少的环节。接下来&#xff0c;你将和我探索人耳的工作机制。 what is sound? 我们可以解释电视机是如何通过眼睛传递视觉信息的&#xff0c;但却往往无法对听觉信息做出类似的解释。 对声音的科学研究被称为声学&…

Day48:WEB攻防-PHP应用文件上传中间件CVE解析第三方编辑器已知CMS漏洞

目录 PHP/ASP-中间件-上传相关-IIS&Apache&Nginx(解析漏洞) IIS Apache Nginx PHP-编辑器-上传相关-第三方处理引用 PHP-CMS源码-上传相关-已知识别到利用 知识点&#xff1a; 1、PHP-中间件-文件上传-CVE&配置解析 2、PHP-编辑器-文件上传-第三方引用安全 3…

Vue——案例01(查询用户)

一、案例实现页面 二、案例实现效果 1. 查询效果 2. 年龄升序 3. 年龄降序 4. 原顺序 三、案例实现思路 1. 定义界面所需标签样式 <div id"app"><h2>查询用户:</h2><input type"text" placeholder"请输入名字"/><b…

01---java面试八股文——mybatis-------10题

1、什么是MyBatis Mybatis是一个半ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了JDBC&#xff0c;开发时只需要关注SQL语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql&#xff0c…

superset config.py路徑

superset config.py路徑 1.路徑 cd /opt/module/miniconda3/envs/superset/lib/python3.9/site-packages/superset2.找到config.py路徑

LeetCode146:LRU缓存

leetCode&#xff1a;146. LRU 缓存 题目描述 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中&#x…

Java项目:80 springboot师生健康信息管理系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统的角色&#xff1a;管理员、宿管、学生 管理员管理宿管员&#xff0c;管理学生&#xff0c;修改密码&#xff0c;维护个人信息。 宿管员…

Elasticsearch 和 Kibana 8.13:简化 kNN 和改进查询并行化

作者&#xff1a;Gilad Gal, Tyler Perkins, Srikanth Manvi, Aris Papadopoulos, Trevor Blackford 在 8.13 版本中&#xff0c;Elastic 引入了向量搜索的重大增强&#xff0c;并将 Cohere 嵌入集成到其统一 inference API 中。这些更新简化了将大型语言模型&#xff08;LLM&a…

深度学习pytorch——卷积神经网络(持续更新)

计算机如何解析图片&#xff1f; 在计算机的眼中&#xff0c;一张灰度图片&#xff0c;就是许多个数字组成的二维矩阵&#xff0c;每个数字就是此点的像素值&#xff08;图-1&#xff09;。在存储时&#xff0c;像素值通常位于[0, 255]区间&#xff0c;在深度学习中&#xff0…