计算机系统应用教程网站

网站首页 > 技术文章 正文

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

btikc 2024-10-27 08:39:18 技术文章 7 ℃ 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构建成组图元;
  • 加载资源文件;
  • 使用菜单条和工具条。

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

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表