QGraphicsObject 需要显式更新才能重绘

Posted

技术标签:

【中文标题】QGraphicsObject 需要显式更新才能重绘【英文标题】:QGraphicsObject needs explicit update to be redrawn 【发布时间】:2020-03-17 21:04:09 【问题描述】:

我有一个QGraphicsPixmapItem,它的内容会随着鼠标点击而改变。当所有交互事件都在派生自QGraphicsPixmapItem的类中处理时,一切都很好:

import numpy as np
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QGraphicsPixmapItem, QGraphicsScene, QGraphicsView, QMainWindow

class MyQGraphicsPixmapItem(QGraphicsPixmapItem):

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.setNewImage()

  def setNewImage(self):
    im = np.random.randint(255, size=(256, 256), dtype=np.uint8)
    pix = QPixmap.fromImage(QImage(im, im.shape[1], im.shape[0], QImage.Format_Grayscale8))
    self.setPixmap(pix)

  def mousePressEvent(self, event):
    self.setNewImage()

app = QApplication([])
window = QMainWindow()
window.setGeometry(100, 100, 400, 400)
view = QGraphicsView()
scene = QGraphicsScene()
gpix = MyQGraphicsPixmapItem()
scene.addItem(gpix)
view.setScene(scene)
window.setCentralWidget(view)
window.show()
app.exec()

但是,我不想在类中处理事件,而是希望能够在其他地方处理它们,通常作为回调。在this answer 中,提供了两个选项:使用QGraphicsObject,或使用场景事件过滤器,强烈建议使用,因为前者是重量级的。但是,AFAIU,场景事件过滤器使一个图形项的事件被另一个图形项处理;我想要更一般的东西,QWidget 可以处理该事件。所以我认为就我而言,我需要使用QGraphicsObject

我对上面的脚本做了一个简单的修改,将QGraphicsPixmapItem 包装成QGraphicsObject。但是,让我吃惊的是,点击后图片并没有刷新。

import numpy as np
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QGraphicsObject, QGraphicsPixmapItem, QGraphicsScene, QGraphicsView, QMainWindow

class MyQGraphicsObject(QGraphicsObject):

  def __init__(self, gpix):
    super().__init__(gpix)
    self.gpix = gpix
    self.setNewImage()

  def setNewImage(self):
    im = np.random.randint(255, size=(256, 256), dtype=np.uint8)
    pix = QPixmap.fromImage(QImage(im, im.shape[1], im.shape[0], QImage.Format_Grayscale8))
    self.gpix.setPixmap(pix)
    # self.update()  # needs to be uncommented to refresh

  def mousePressEvent(self, event):
    self.setNewImage()

  def boundingRect(self):
    return self.gpix.boundingRect()

  def paint(self, painter, option, widget):
    return self.gpix.paint(painter, option, widget)

app = QApplication([])
window = QMainWindow()
window.setGeometry(100, 100, 400, 400)
view = QGraphicsView()
scene = QGraphicsScene()
opix = MyQGraphicsObject(QGraphicsPixmapItem())
scene.addItem(opix)
view.setScene(scene)
window.setCentralWidget(view)
window.show()
app.exec()

只有当我手动添加self.update() 时,图像才会刷新。当然这解决了问题,但我有一种不应该需要的感觉,我错误地使用了QGraphicsObject。是这样吗,还是真的需要手册update

【问题讨论】:

【参考方案1】:

调用update()方法是正确的。


TL;博士;

什么情况下需要调用更新?

每当开发人员需要调用paint() 方法(或小部件上的paintEvent)时

为什么在 QGraphicsPixmapItem 的情况下不需要调用它,而在 QGraphicsObject 中却需要?

在QGraphicsPixmap中update()方法被调用,你可以验证the source code是否被修改:

void QGraphicsPixmapItem::setPixmap(const QPixmap &pixmap)

    Q_D(QGraphicsPixmapItem);
    prepareGeometryChange();
    d->pixmap = pixmap;
    d->hasShape = false;
    update();

也就是说,在QGraphicsItem中你不再需要调用它,因为setPixmap方法已经做了,不像QGraphicsObject没有它,所以需要调用update()来强制调用paint方法。

【讨论】:

以上是关于QGraphicsObject 需要显式更新才能重绘的主要内容,如果未能解决你的问题,请参考以下文章

如何重命名 GitHub 上的存储库?

显式绑定重定向与自动生成的绑定重定向冲突

我怎样才能“显式”地快速实现一个协议?如果不可能,为啥?

关于Windows更新窗口内容的问题(作为一个实验,效果很明显)

电脑重装了系统还能变回原来的系统吗?怎么弄才能变回原来的系统.

什么时候需要显式调用超类构造函数?