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

实战PyQt5: 084-图形视图框架的关键特性

yuyutoo 2024-10-19 11:09 4 浏览 0 评论

缩放与旋转

QGraphicsView通过QGraphicsView.setMatrix()支持同QPainter相同的仿射变换,通过对一个视图应用变换,可以很容易地支持普通的导航功能(如缩放与旋转)。下面的例子演示了如何在QGraphicsView的子类中实现缩放和旋转。

class MyView(QGraphicsView):
        def zoomIn(self):
                   self.scale(1.2, 1.2)
         def zoomOut(self):
                   self.scale(1/1.2, 1/1.2)
         def rotateLeft(self):
                   self.rotate(-10)
         def rotateRight(self)
                   self.rotate(10)

这些槽函数可以在启用了autoRepeat的情况下连接到QToolButtons。当对视图进行变换时,QGraphicsView可使视图的中心对齐。

打印

图形视图通过其渲染函数QGraphicsScene.render()和QGraphicsView.render()提供单行打印。这些函数提供相同的API:通过将QPainter传递给任一渲染函数,可以使场景或视图将其全部或部分内容渲染到任何绘画设备中。下面示例说明如何使用QPrinter将整个场景打印成整页。

scene = QGraphicsScene()
printer = QPrinter()
scene.addRect(QRect(0,0,100,200), QPen(Qt.black), QBrush(Qt.green)
 
if QPrintDialog(printer).exec() == QDialog.Accepted:
         painter = QPainter(printer)
         painter.setRenderHint(QPainter.Antialiasing)
         scene.render(painter)
         del painter

场景和视图渲染功能之间的区别在于,一个在场景坐标中运行,另一个在视图坐标中运行。QGraphicsScene.render()通常是打印未变换的场景的整个片段(例如,绘制几何数据或打印文本文档)的首选。另一方面,QGraphicsView.render()适合截图,它的默认行为是使用提供的painter渲染视口的确切内容。

scene = QGraphsScene()
scene.addRect(QRectF(0,0,100, 200), QPen(Qt.black), QBrush(Qt.green))
pixmap = QPixmap()
painter = QPainter(pixmap)
painter.setRenderHint(QPainter.Antialiasing)
scene.render(painter)
painter.end()
 
pixmap.save('scene.png')

当源区域和目标区域的大小不匹配时,将拉伸源内容以适合目标区域。通过将Qt.AspectRatioMode传递给正在使用的渲染函数,可以选择在拉伸内容时保持或忽略场景的宽高比。

拖放

由于QGraphicsView间接继承了QWidget,因此它也提供了与QWidget提供的拖放功能相同的功能。另外,为方便起见,图形视图框架为场景以及每个图元提供拖放支持。当视图接收到拖放事件时,它将拖放事件转换为QGraphicsSceneDragDropEvent,然后将其转发到场景。场景将接管此事件的调度,并将其发送到接受放置的鼠标光标下的第一个图元。

要从一个图元开始拖动,请创建QDrag对象,并传递到开始拖动的部件。可以同时通过多个视图观察图元,但是只有一个视图可以开始拖动。在大多数情况下,拖动是由于按下或移动鼠标而开始的,因此在mousePressEvent()或mouseMoveEvent()中,可以从事件中得到原始的窗口部件对象。例如:

def mousePressEvent(self, event):
         data = QMimeData()
         drag = QDrag(event.widget())
         drag.setMimeData(data)
         drag.exec()

为了在场景中拦截拖放事件,必须重新实现QGraphicsScene.dragEnterEvent()和在QGraphicsItem的子类里任何与特定场景需要的事件处理器。items也可以通过调用QGraphicsItem.setAcceptDrops()获得拖放支持,为了处理将进行的拖放事件,需要重新实现QGraphicsItem.dragEnterEvent(), QGraphicsItem.dragMoveEvent(), QGraphicsItem.dragLeaveEvent() 和QGraphicsItem.dropEvent()。

光标和工具信息提示

像QWidget一样,QGraphicsItem也支持光标(QgraphicsItem.setCursor)与工具信息提示(QGraphicsItem.setToolTip())。当光标进入到图元的区域,光标与工具信息提示被QGraphicsView激活(通过调用QGraphicsItem.contains()检测)。也可以通过调用QGraphicsView.setCursor()直接在视图上设置一个缺省光标。

动画

图形视图支持多个级别的动画。因此可以使用动画框架轻松地组装动画。图元只需要继承来自QGraphicsObject和QPropertyAnimation与之关联任何可以实施动画的QObject属性即可。

另一个选择是创建一个继承自QObject和QGraphicsItem的自定义项。该项目可以设置自己的计时器,并通过QObject.timerEvent()中的增量步骤控制动画。

第三个选项是通过调用QGraphicsScene.advance()来实施场景动画,QGraphicsScene.advance()会依次调用QGraphicsItem.advance()。

OpenGL渲染

要启用OpenGL渲染,只需调用QGraphicsView.setViewport()即可将新的QOpenGLWidget设置为QGraphicsView的视口。如果要让OpenGL提供抗锯齿功能,则需要使用QSurfaceFormat.setSamples()来设置所需的样本数。 例如:

view = QGraphics(scene)
gl = QOpenGLWidget()
format = QSurfaceFormat()
format.setSamples(4)
gl.setFormat(format)
view.setViewport(gl)

图元组(Item Group)

通过使一个图元成为另一个图元的子项,可以实现图元组的最基本特征:这些图元将一起移动,并且所有转换都从父图元传递到子图元。

另外,QGraphicsItemGroup是一个特殊项,它将子事件处理与一些接口相结合,用于在组中添加项或从组中删除图元。将图元添加到QGraphicsItemGroup中将保留该图元的原始位置和变换,而通常情况下新的父图元会导致子级图元相对于其新的父级图元而重新定位。方便起见,可以通过调用QGraphicsScene.createItemGroup()在整个场景中创建QGraphicsItemGroup。

部件和布局

Qt通过QGraphicsWidget引入了对几何形状和布局感知项的支持。这个特殊的基本图元类似于QWidget,但是与QWidget不同,它继承自QGraphicsItem不是继承自QPaintDevice继承。这允许我们可以编写带有事件,信号和插槽,大小提示和策略的完整窗口部件,还可以通过QGraphicsLinearLayout和QGraphicsGridLayout管理布局中的窗口部件。

QGraphicsWidget

基于QGraphicsItem,QGraphicsWidget提供了两全其美的功能:QWidget的其他功能,例如样式,字体,调色板,布局方向及其几何形状,以及QGraphicsItem的分辨率独立性和转换支持。由于图形视图使用实数坐标而不是整数,因此QGraphicsWidget的几何形状函数也可以在QRectF和QPointF上运行。这也适用于框架矩形,边距和间距。例如使用QGraphicsWidget,通常指定(0.5、0.5、0.5、0.5)的内容边距。可以创建子部件和“顶层”窗口。在某些情况下,可以将图形视图用于高级MDI应用程序。

QGraphicsWidget支持一些QWidget的属性(包括窗口标志和属性,但不是全部)。例如,可以通过将Qt.Window窗口标志传递给QGraphicsWidget的构造函数来创建和装饰窗口,但是图形视图当前不支持macOS上常见的Qt.Sheet和Qt.Drawer标志。

QGraphicsLayout

QGraphicsLayout是专门为QGraphicsWidget设计的布局框架的一部分。其API与QLayout非常相似。可以在QGraphicsLinearLayout和QGraphicsGridLayout内部管理部件和子布局。还可以通过自己子类化QGraphicsLayout轻松地编写自己的布局,或者通过编写QGraphicsLayoutItem的适配器子类将自己的QGraphicsItem项目添加到布局中。

支持部件嵌入

图形视图对将任何部件嵌入场景提供了无缝支持。可以嵌入简单的部件(例如QLineEdit或QPushButton),复杂的小部件(例如QTabWidget)甚至完整的主窗口。要将部件嵌入场景,只需调用QGraphicsScene.addWidget(),或创建QGraphicsProxyWidget的实例即可手动嵌入部件。

通过QGraphicsProxyWidget,图形视图能够集成客户端部件功能,包括其光标,工具提示,鼠标,平板电脑和键盘事件,子部件,动画,弹出窗口(例如QComboBox或QCompleter),以及小部件的输入焦点和激活。QGraphicsProxyWidget甚至集成了嵌入式窗口部件的选项卡顺序,以方便便在嵌入式窗口部件中移入和移出。甚至可以将新的QGraphicsView嵌入到场景中以提供复杂的嵌套场景。

转换嵌入式窗口部件时,“图形视图”会确保独立转换窗口部件的分辨率,从而在放大时使字体和样式保持清晰。(注意,分辨率独立性的影响取决于样式。)

样例代码

在测试代码中,我们使用QGraphicsItemGroup 构造了一个螺旋线,并将其设置到场景中,并使用菜单条和工具条,演示旋转和缩放这个螺旋线。完整代码如下:

import sys,math
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPen, QBrush, QFont, QTransform, QPainter, QIcon
from PyQt5.QtWidgets import (QApplication, QMainWindow, QGraphicsScene, QGraphicsView,
                             QMenuBar, QMenuBar, QAction, QToolBar, QDialog,
                             QGraphicsLineItem, QGraphicsItemGroup)
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter
import resource_rc
 
class DemoGvFeature(QMainWindow):
    def __init__(self, parent=None):
        super(DemoGvFeature, self).__init__(parent)   
        
        # 设置窗口标题
        self.setWindowTitle('实战PyQt5: Graphics View 框架关键特征演示')      
        # 设置窗口大小
        self.resize(600, 400)
      
        self.initUi()
        
    def initUi(self):
        #菜单条
        menuBar = self.menuBar()
        menuFile = menuBar.addMenu('文件')
        menuTrans = menuBar.addMenu('变换')
        
        #文件
        aPrint = QAction('打印', self)
        aPrint.triggered.connect(self.onPrint)
        aExit = QAction('退出', self)
        aExit.triggered.connect(self.close)
        
        menuFile.addAction(aPrint)
        menuFile.addAction(aExit)
        
        #变换
        aZoomIn = QAction('放大', self)
        aZoomIn.setIcon(QIcon(':/res/zoom_in.png'))
        aZoomIn.triggered.connect(self.onZoomIn)
        aZoomOut = QAction('缩小', self)
        aZoomOut.setIcon(QIcon(':res/zoom_out.png'))
        aZoomOut.triggered.connect(self.onZoomOut)
        aRotateLeft = QAction('向左旋转', self)
        aRotateLeft.setIcon(QIcon(':/res/rotate_ccw.png'))
        aRotateLeft.triggered.connect(self.onRotateLeft)
        aRotateRight = QAction('向右旋转', self)
        aRotateRight.setIcon(QIcon(':/res/rotate_cw.png'))
        aRotateRight.triggered.connect(self.onRotateRight)
        
        menuTrans.addAction(aZoomIn)
        menuTrans.addAction(aZoomOut)
        menuTrans.addAction(aRotateLeft)
        menuTrans.addAction(aRotateRight)
        
        toolBar = self.addToolBar('')
        toolBar.addAction(aZoomIn)
        toolBar.addAction(aZoomOut)
        toolBar.addAction(aRotateLeft)
        toolBar.addAction(aRotateRight)
        
        #场景部分
        self.scene = QGraphicsScene()
        
        self.setScene()
        
        self.view = QGraphicsView()
        self.view.setScene(self.scene)
        
        self.setCentralWidget(self.view)
        
    def setScene(self):
        #self.scene.addText('Graphics View\nKey Feature', QFont(self.font().family(), 16))
        
        grp = QGraphicsItemGroup()
        #绘制螺旋线
        colors=[Qt.red, Qt.darkMagenta, Qt.blue, Qt.green, Qt.yellow, Qt.darkCyan]
        n = 5  #边数
        #初始值
        x0 = 0
        y0 = 0
        ratio = 0.35
        deg = (360 / n - 1) * math.pi / 180
        for i in range (360):
            line = QGraphicsLineItem()
            line.setPen(QPen(colors[i%n]))
            x1 = x0 + i * ratio * math.cos(-i * deg)
            y1 = y0 + i * ratio * math.sin(-i * deg)
            line.setLine(x0, y0, x1, y1)
            grp.addToGroup(line)
            x0 = x1
            y0 = y1 
            
        self.scene.addItem(grp)       
    
    def onPrint(self):
        printer = QPrinter()
        if QPrintDialog(printer).exec() == QDialog.Accepted:
            painter = QPainter(printer)
            painter.setRenderHints(QPainter.Antialiasing)
            self.scene.render(painter)
            del painter
            
    def onZoomIn(self):
        self.view.scale(1.2, 1.2)
        
    def onZoomOut(self):
        self.view.scale(1/1.2, 1/1.2)
        
    def onRotateLeft(self):
        self.view.rotate(-10)
        
    def onRotateRight(self):
        self.view.rotate(10)
    
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DemoGvFeature()
    window.show()
    sys.exit(app.exec()) 

运行结果如下图:

本文知识点

  • Graphics View框架关键特性;
  • 使用QGraphicsItemGroup构建成组图元;
  • 加载资源文件;
  • 使用菜单条和工具条。

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

相关推荐

史上最全的浏览器兼容性问题和解决方案

微信ID:WEB_wysj(点击关注)◎◎◎◎◎◎◎◎◎一┳═┻︻▄(页底留言开放,欢迎来吐槽)●●●...

平面设计基础知识_平面设计基础知识实验收获与总结
平面设计基础知识_平面设计基础知识实验收获与总结

CSS构造颜色,背景与图像1.使用span更好的控制文本中局部区域的文本:文本;2.使用display属性提供区块转变:display:inline(是内联的...

2025-02-21 16:01 yuyutoo

写作排版简单三步就行-工具篇_作文排版模板

和我们工作中日常word排版内部交流不同,这篇教程介绍的写作排版主要是用于“微信公众号、头条号”网络展示。写作展现的是我的思考,排版是让写作在网格上更好地展现。在写作上花费时间是有累积复利优势的,在排...

写一个2048的游戏_2048小游戏功能实现

1.创建HTML文件1.打开一个文本编辑器,例如Notepad++、SublimeText、VisualStudioCode等。2.将以下HTML代码复制并粘贴到文本编辑器中:html...

今天你穿“短袖”了吗?青岛最高23℃!接下来几天气温更刺激……

  最近的天气暖和得让很多小伙伴们喊“热”!!!  昨天的气温到底升得有多高呢?你家有没有榜上有名?...

CSS不规则卡片,纯CSS制作优惠券样式,CSS实现锯齿样式

之前也有写过CSS优惠券样式《CSS3径向渐变实现优惠券波浪造型》,这次再来温习一遍,并且将更为详细的讲解,从布局到具体样式说明,最后定义CSS变量,自定义主题颜色。布局...

柠檬科技肖勃飞:大数据风控助力信用社会建设

...

你的自我界限够强大吗?_你的自我界限够强大吗英文

我的结果:A、该设立新的界限...

行内元素与块级元素,以及区别_行内元素和块级元素有什么区别?

行内元素与块级元素首先,CSS规范规定,每个元素都有display属性,确定该元素的类型,每个元素都有默认的display值,分别为块级(block)、行内(inline)。块级元素:(以下列举比较常...

让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华
让“成都速度”跑得潇潇洒洒,地上地下共享轨交繁华

去年的两会期间,习近平总书记在参加人大会议四川代表团审议时,对治蜀兴川提出了明确要求,指明了前行方向,并带来了“祝四川人民的生活越来越安逸”的美好祝福。又是一年...

2025-02-21 16:00 yuyutoo

今年国家综合性消防救援队伍计划招录消防员15000名

记者24日从应急管理部获悉,国家综合性消防救援队伍2023年消防员招录工作已正式启动。今年共计划招录消防员15000名,其中高校应届毕业生5000名、退役士兵5000名、社会青年5000名。本次招录的...

一起盘点最新 Chrome v133 的5大主流特性 ?

1.CSS的高级attr()方法CSSattr()函数是CSSLevel5中用于检索DOM元素的属性值并将其用于CSS属性值,类似于var()函数替换自定义属性值的方式。...

竞走团体世锦赛5月太仓举行 世界冠军杨家玉担任形象大使

style="text-align:center;"data-mce-style="text-align:...

学物理能做什么?_学物理能做什么 卢昌海

作者:曹则贤中国科学院物理研究所原标题:《物理学:ASourceofPowerforMan》在2006年中央电视台《对话》栏目的某期节目中,主持人问过我一个的问题:“学物理的人,如果日后不...

你不知道的关于这只眯眼兔的6个小秘密
你不知道的关于这只眯眼兔的6个小秘密

在你们忙着给熊本君做表情包的时候,要知道,最先在网络上引起轰动的可是这只脸上只有两条缝的兔子——兔斯基。今年,它更是迎来了自己的10岁生日。①关于德艺双馨“老艺...

2025-02-21 16:00 yuyutoo

取消回复欢迎 发表评论: