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

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

yuyutoo 2024-10-19 11:09 10 浏览 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构建成组图元;
  • 加载资源文件;
  • 使用菜单条和工具条。

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

相关推荐

电脑 CMD 命令大全:简单粗暴收藏版

电脑CMD命令大全包括了许多常用的命令,这些命令可以帮助用户进行各种系统管理和操作任务。以下是一些常用的CMD命令及其功能:1、系统信息和管理...

电脑维修高手必备!8个神奇DOS命令,自己动手不求人

我相信搞电脑维修或者维护的基本都会些DOS的命令。就算Windows操作系统是可视化的界面,但很多维护检查是离不开DOS命令的。掌握好这些命令,你不仅能快速诊断问题,还能解决90%的常见电脑故障。下...

一个互联网产品总监的设计技巧总结 - 技术篇

古语:工欲善其事必先利其器。往往在利其器后我们才能事半功倍。从这个角度出发成为一个合格的产品经理你需要的是“利其器”,这样你才能产品的设计过程中如鱼得水,得心应手。有些产品经理刚入职,什么都感觉自己欠...

超详解析Flutter渲染引擎|业务想创新,不了解底层原理怎么行?

作者|万红波(远湖)出品|阿里巴巴新零售淘系技术部前言Flutter作为一个跨平台的应用框架,诞生之后,就被高度关注。它通过自绘UI,解决了之前RN和weex方案难以解决的多端一致性...

瑞芯微RK3568|SDK开发之环境安装及编译操作

1.SDK简介一个通用LinuxSDK工程目录包含有buildroot、app、kernel、device、docs、external等目录。其中一些特性芯片如RK3308/RV1108/R...

且看L-MEM ECC如何守护i.MXRT1170从核CM4

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M4内核的L-MEMECC功能。本篇是《简析i.MXRT1170Cortex-M7F...

ECC给i.MXRT1170 FlexRAM带来了哪些变化?

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M7内核的FlexRAMECC功能。ECC是“ErrorCorrectingCode”...

PHP防火墙代码,防火墙,网站防火墙,WAF防火墙,PHP防火墙大全

PHP防火墙代码,防火墙,网站防火墙,WAF防火墙,PHP防火墙大全资源宝整理分享:https://www.htple.net...

从零开始移植最新版本(2023.10)主线Uboot到Orange Pi 3(全志H6)

本文将从零开始通过一步一步操作来实现将主线U-Boot最新代码移植到OrangePi3(全志H6)开发板上并正常运行起来。本文从通用移植思路的角度,展现是思考的过程,通过这种方式希望能让读者一通百...

可视化编程工具Blockly——定制工具箱

1概述本文重点讲解如何定制Blocklytoolbox上,主要包含如下几点目标:如何为toolbox不同类别添加背景色如何改变选中的类别的外观如何为toolbox类别添加定制化的css如何改变类别...

用户界面干货盘点(用户界面的基本操作方法)

DevExpressDevExpressWPF的DXSplashScreen控件在应用加载的时候显示一个启动界面。添加DXSplashScreen后,会默认生成一个XAML文件,当然,你也可...

Vue3+Bootstrap5整合:企业级后台管理系统实战

简洁而不简单,优雅而不失强大在当今快速发展的企业数字化进程中,高效、美观的后台管理系统已成为企业运营的核心支撑。作为前端开发者,我们如何选择技术栈,才能既保证开发效率,又能打造出专业级的用户体验?答案...

什么?这三款i.MXRT型号也开放了IAP API?

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT1050/1020/1015系列ROM中的FlexSPI驱动API使用。今天痞子衡去4S店给爱车做保养了,...

OneCode基础组件介绍——表格组件(Grid)

在企业级应用开发中,表格组件是数据展示与交互的核心载体。OneCode平台自研的Grid表格组件,以模型驱动设计...

开源无线LoRa传感器(光照温湿度甲醛Tvoc)

本开源项目基于ShineBlinkC2M低代码单片机实现,无需复杂单片机C语言开发。即使新手也可很容易用FlexLua零门槛开发各种功能丰富稳定可靠的IoT硬件,更多学习教程可参考Flex...

取消回复欢迎 发表评论: