实战PyQt5:090-处理一个QPaintDevice错误

本文介绍在使用QGraphicsScene的render()函数时遇到的QPaintDevice释放错误,并提供两种解决方案,包括显式释放对象和使用painter的begin()与end()方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要报错的代码

在使用QGraphicsScene的render()函数将场景作为一个图像输出时,代码如下:

image=QImage(320,240, QImage.Format_ARGB32)
painter=QPainter(image)
scene.render(painter)     # scene为一QGraphicsScene对象
image.save(filename)  #filename为绝对路径名

执行上述代码, 报以下错误:

QPaintDevice: Cannot destroy paint device that is being painted

其意思是:

QPaintDevie:不能释放正在绘图的绘图设备

然后就导致程序崩溃了。

经研究发现,出现这个问题的原因和python对象的释放机制有关,在python中使用垃圾回收机制来释放,它对不再使用的对象的释放顺序造成了上述问题的出现。可以使用下面两种方法来解决这个问题:

解决办法一

使用del函数,显式地按顺序来释放对象,正确的代码如下:

image=QImage(320,240, QImage.Format_ARGB32)
painter=QPainter(image)
scene.render(painter)
image.save(filename)  #filename为绝对路径名
del painter
del image

这样可以正确释放,程序也稳定运行。

解决办法二

在创建painter对象的时候,不传入参数,在绘制图像前,使用 begin(image)传图图像设置,绘制结束后。调用end()结束绘制,再保存图像。其代码如下

image=QImage(320,240, QImage.Format_ARGB32)
painter=QPainter()
painter.begin(image)
scene.render(painter)
painter.end()
image.save(filename)

演示代码

演示代码演示了出错和两种正确的方式。完整代码如下:

import sys, os, math
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPen, QBrush, QPainter,QImage,QPainterPath
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
                             QGraphicsScene, QGraphicsView, QGraphicsPathItem)
 
class DemoErrorPaintDevice(QWidget):
    def __init__(self, parent=None):
        super(DemoErrorPaintDevice, self).__init__(parent)   
        
        # 设置窗口标题
        self.setWindowTitle('实战PyQt5: QPaintDevice 绘图后释放错误演示')      
        # 设置窗口大小
        self.resize(480, 320)
      
        self.initUi()
        
    def initUi(self):
        mainLayout = QVBoxLayout()
        
        #按钮操作部分
        hLayout = QHBoxLayout()
        btnError = QPushButton('会出错,应用要崩溃',self)
        btnError.clicked.connect(self.onError)
        btnMethod1 = QPushButton('正确姿势一', self)
        btnMethod1.clicked.connect(self.onMethod1)
        btnMethod2 = QPushButton('正确姿势二', self)
        btnMethod2.clicked.connect(self.onMethod2)
        hLayout.addWidget(btnError)
        hLayout.addWidget(btnMethod1)
        hLayout.addWidget(btnMethod2)
        
        #绘图部分
        self.scene = QGraphicsScene()
        #添加一个路径(三叶草)
        path = QPainterPath()
        r = 120
        for i in range (3) :
            path.cubicTo(math.cos(2*math.pi*i/3.0)*r, 
                         math.sin(2*math.pi*i/3.0)*r, 
                         math.cos(2*math.pi*(i+0.9)/3.0)*r, 
                         math.sin(2*math.pi*(i+0.9)/3.0)*r, 0, 0)
        pathItem = QGraphicsPathItem()
        pathItem.setPath(path)
        pathItem.setPen(Qt.darkGreen)
        pathItem.setBrush(Qt.darkGreen)   
        self.scene.addItem(pathItem)
        
        view = QGraphicsView()
        view.setScene(self.scene)
        
        mainLayout.addLayout(hLayout)
        mainLayout.addWidget(view)
        self.setLayout(mainLayout)
        
    def onError(self):
        image = QImage(320, 240, QImage.Format_ARGB32)
        painter = QPainter(image)
        self.scene.render(painter)
        image.save(os.path.dirname(__file__) + '/test1.png')
        print('错误方法')
           
    def onMethod1(self):
        image = QImage(320, 240, QImage.Format_ARGB32)
        painter = QPainter(image)
        self.scene.render(painter)
        image.save(os.path.dirname(__file__) + '/test2.png')
        del painter
        del image
        print('正确方法一')
    
    def onMethod2(self):
        image = QImage(320, 240, QImage.Format_ARGB32)
        painter = QPainter()
        painter.begin(image)
        self.scene.render(painter)
        painter.end()
        image.save(os.path.dirname(__file__) + '/test3.png')
        print('正确方法二')
    
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DemoErrorPaintDevice()
    window.show()
    sys.exit(app.exec())

运行结果如下图:

实战PyQt5:090-处理一个QPaintDevice错误

QPaintDevice绘图完成后释放报错演示

 

前一篇: 实战PyQt5: 089-撤销重做样例演示

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值