百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

实战PyQt5: 083-图形视图框架中的坐标系

toyiye 2024-08-21 01:42 3 浏览 0 评论

图形视图基于笛卡尔坐标系,图元在场景中的位置和几何形状由x坐标和y坐标两个数字表示。使用未做变换的视图观察场景时,场景中的一个单位等于屏幕上的一个像素。

注意:“图形视图”使用Qt的坐标系,因此不支持反转的Y轴坐标系(向上增长)。

图形视图中有三个有效的坐标系在起作用:图元坐标,场景坐标和视图坐标。为了简化实现,“图形视图”提供了方便的函数,允许在三个坐标系之间进行映射。渲染时,图形视图的场景坐标对应于QPainter的逻辑坐标,视图坐标与设备坐标相同。

图元(Item)坐标

图元位于它们自己的坐标系中。它的坐标都以点(0,0)为中心点,这也是所有变换的中心点。在图元坐标系中的几何图元,经常被称为图元点,图元线,图元矩形。

当创建一个自定义的图元时,必须考虑图元的坐标。QGraphicsScene与QGraphicsView可以执行所有转换,这使得实现自定义的图元变得容易。例如,假如收到鼠标按下或是拖动输入事件,事件的位置以图元坐标的形式给出。当某个点的位置在指定的图元内时,QGraphicsItem.contain()返回True,否则返回False。这个点参数使用图元坐标,相似地,图元的边界矩形与形状也使用图元坐标。

图元位置指的是图元的中心点在其父亲的坐标系中的坐标,有时称其为父坐标。从这个意义上说,场景被视为所有无父亲图元的‘父图元’,因此顶层的图元位置就是在在场景坐标中的位置。

子坐标是相对于父坐标的,假如子图元未经变换,则子坐标与父坐标之间的差值等于在父坐标系下,父图元与子图元之间的距离。例如,假如一个未经变换的子图元位置与其父图元的中心重合,那么这两个图元的坐标系统完全相同。如果子图元的位置是(10,0),那么子图元坐标系中的(0,10)点,对应于父坐标系中的(10,10)点。

因为图元的位置与变换是相对于父图元的,子图元的坐标不会被父亲的变换影响,尽管父图元的变换隐含地对子图元做了变换。在上面的例子中,即使父图元旋转,缩放,子图元的(0,10)点依然对应于父图元的(10,10)点。然而,相对于场景来讲,子图元会遵循父图元的变换。假如父图元被缩放(2X,2X),子图元 的位置在场景中的坐标是(20,0),它的(10,0)点则与场景中的(40,0)对应 。

除QGraphicsItem.pos()函数以外,QGraphicsItem的函数在图元坐标中操作,例如一个图元的边界矩形总是以图元坐标的形式给出。

场景(Scene)坐标

场景代表所有图元的基本坐标系,场景坐标系统描述了每个最顶级图元的位置,也是从视图向场景传递所有场景事件的基础。场景中的每个图元有场景位置与边界矩形(QGraphicsItem::scenePos(),QGraphicsItem::sceneBoundingRect()), 另外,它有自己本地图元位置与边界矩形。场景位置描述了图元在场景坐标下的位置,它的场景边界矩形则用于QGraphicsScene决定场景中哪块区域发生了变化。场景中的变化通过QGraphicsScene.changed()信号来通知,它的参数是场景矩形列表。

视图(View)坐标

视图坐标是部件的坐标,视图坐标中每个单位对应一个像素。这种坐标的特殊之处在于它是相对于部件或是视口的,不会被所观察的场景所影响。QGraphicsView的视口的左上角总是(0,0),右下角总是(视口宽,视口高)。所有鼠标事件和拖放事件最初都是作为视图坐标接收的,因此需要将这些坐标映射到场景坐标以便与图元进行交互。

坐标映射

通常在处理场景中的图元时,要在场景与图元之间,图元与图元之间,视图与场景之间进行坐标映射。举例来讲,当在QGraphicsView的视口中点击鼠标时,应该通过调用QGraphicsView.mapToScence()与QGraphicsScene.itemAt()来获知光标下是场景中的哪个图元。假如想获知一个图元位于视口中的什么位置,应该先在图元上调用QGraphicsItem.mapToScene(), 然后调用QGraphicsView.mapFromScene()。最后,假如想知道在一个椭圆中有哪些图元,则可以将QPainterPath传递到mapToScene(),然后再把映射后的路径传递给QGraphicsScene.items()。

演示代码

使用多种颜色绘制螺旋线,操作下方的滑动条,可旋转这个彩色的螺旋线。完整代码如下:

import sys,math
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtGui import QPen, QBrush, QFont, QTransform
from PyQt5.QtWidgets import (QApplication, QWidget, QGraphicsScene, QGraphicsView,
                             QVBoxLayout, QSlider, QGraphicsLineItem)
 
class DemoGvCoordinate(QWidget):
    def __init__(self, parent=None):
        super(DemoGvCoordinate, self).__init__(parent)   
        
        # 设置窗口标题
        self.setWindowTitle('实战PyQt5: Graphics View 框架坐标系统演示')      
        # 设置窗口大小
        self.resize(480, 480)
      
        self.initUi()
        
    def initUi(self):
        #场景部分
        scene = QGraphicsScene()
                
        scene.setBackgroundBrush(QBrush(Qt.black))
        
        #绘制螺旋线
        colors=[Qt.red, Qt.darkMagenta, Qt.blue, Qt.green, Qt.yellow, Qt.darkCyan]
        x0 = 0
        y0 = 0
        ratio = 0.35
        deg = 59 * math.pi / 180
        for i in range (360):
            line = QGraphicsLineItem()
            line.setPen(QPen(colors[i%6]))
            x1 = x0 + i * ratio * math.cos(i * deg)
            y1 = y0 + i * ratio * math.sin(i * deg)
            line.setLine(x0, y0, x1, y1)
            scene.addItem(line)
            x0 = x1
            y0 = y1
              
        self.view = QGraphicsView()
        self.view.setScene(scene)
        
        #滑动条
        slider = QSlider(Qt.Horizontal, self)
        slider.setRange(-360, 360)
        slider.setPageStep(10)
        slider.setValue(0)
        slider.valueChanged.connect(self.onRotateValueChanged)
        
        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addWidget(slider)
        
        self.setLayout(layout)
    
    def onRotateValueChanged(self, value):
        #是个累积效应,先对变化矩阵进行复位操作
        self.view.setTransform(QTransform())
        self.view.rotate(value)
    
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DemoGvCoordinate()
    window.show()
    sys.exit(app.exec()) 

运行结果如下图:

本文知识点

  • 图形视图基于笛卡尔坐标系;
  • 场景坐标,视图坐标和图元坐标;
  • 场景坐标,视图坐标,图元坐标之间的映射关系;
  • 螺旋线的绘制方法。

喜欢本文内容就关注, 收藏,点赞,评论和转发。

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码