如何在不在 QGraphicsScene 中更改 QGraphicsItems 的情况下将场景中的 QGraphicsItems 绘制到 QImage 上?
How to paint QGraphicsItems from scene onto QImage without changing them in the QGraphicsScene?
我正在将 QGraphicsItem
在 QGraphicsScene
中的位置转换为光栅化蒙版。我正在使用这些蒙版进一步处理视频(采用蒙版内的平均强度)。为实现这一点,我将场景中的每个项目一个一个地绘制在 QImage
上,它的大小刚好足以包裹该项目。一切正常,但场景中的项目消失了。那是因为当我在 QImage
上绘制时,我正在从项目上取下笔。完成后,我将原来的笔放回原位,但场景中并没有重新出现这些项目。
我怎样才能“刷新”场景以使项目重新出现,或者防止项目完全消失?
我真的找不到任何人 运行 解决这个问题。所以也许我只是在做一些根本错误的事情。欢迎提出任何建议。
这是我的代码:
class MyThread(QtCore.QThread):
def __init__(self, scene):
super().__init__()
self.scene = scene
def run(self):
for item in self.scene.items():
# Render the ROI item to create a rasterized mask.
qimage = self.qimage_from_shape_item(item)
# do some stuff
@staticmethod
def qimage_from_shape_item(item: QtWidgets.QAbstractGraphicsShapeItem) -> QtGui.QImage:
# Get items pen and brush to set back later.
pen = item.pen()
brush = item.brush()
# Remove pen, set brush to white.
item.setPen(QtGui.QPen(QtCore.Qt.NoPen))
item.setBrush(QtCore.Qt.white)
# Determine the bounding box in pixel coordinates.
top = int(item.scenePos().y() + item.boundingRect().top())
left = int(item.scenePos().x() + item.boundingRect().left())
bottom = int(item.scenePos().y() + item.boundingRect().bottom()) + 1
right = int(item.scenePos().x() + item.boundingRect().right()) + 1
size = QtCore.QSize(right - left, bottom - top)
# Initialize qimage, use 8-bit grayscale.
qimage = QtGui.QImage(size, QtGui.QImage.Format_Grayscale8)
qimage.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(qimage)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# Offset the painter to paint item in its correct pixel location.
painter.translate(item.scenePos().x() - left, item.scenePos().y() - top)
# Paint the item.
item.paint(painter, QtWidgets.QStyleOptionGraphicsItem())
# Set the pen and brush back.
item.setPen(pen)
item.setBrush(brush)
# Set the pixel coordinate offset of the item to the QImage.
qimage.setOffset(QtCore.QPoint(left, top))
return qimage
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(widget)
view = QtWidgets.QGraphicsView()
layout.addWidget(view)
scene = QtWidgets.QGraphicsScene()
view.setScene(scene)
thread = MyThread(scene)
view.setFixedSize(400, 300)
scene.setSceneRect(0, 0, 400, 300)
rect_item = QtWidgets.QGraphicsRectItem()
p = QtCore.QPointF(123.4, 56.78)
rect_item.setPos(p)
r = QtCore.QRectF(0., 0., 161.8, 100.)
rect_item.setRect(r)
scene.addItem(rect_item)
button = QtWidgets.QPushButton("Get masks")
layout.addWidget(button)
button.clicked.connect(thread.start)
widget.show()
sys.exit(app.exec_())
问题是您“只能在主线程上创建和使用 GUI 小部件”,请参阅 this SO answer here 中的更多信息。
我的解决方法是将GUI交互部分,即qimage_from_shape_item(),从线程中取出,在主循环中处理。我想我直接使用这些项目仍然不是很好,虽然没有明显的闪烁效果或临时设置 NoPen 的任何东西。
另一种方法可能是使用 QGraphicsScene::render;但是,我不知道如何在不与场景中的其他项目交互的情况下逐一渲染项目。
我正在将 QGraphicsItem
在 QGraphicsScene
中的位置转换为光栅化蒙版。我正在使用这些蒙版进一步处理视频(采用蒙版内的平均强度)。为实现这一点,我将场景中的每个项目一个一个地绘制在 QImage
上,它的大小刚好足以包裹该项目。一切正常,但场景中的项目消失了。那是因为当我在 QImage
上绘制时,我正在从项目上取下笔。完成后,我将原来的笔放回原位,但场景中并没有重新出现这些项目。
我怎样才能“刷新”场景以使项目重新出现,或者防止项目完全消失?
我真的找不到任何人 运行 解决这个问题。所以也许我只是在做一些根本错误的事情。欢迎提出任何建议。
这是我的代码:
class MyThread(QtCore.QThread):
def __init__(self, scene):
super().__init__()
self.scene = scene
def run(self):
for item in self.scene.items():
# Render the ROI item to create a rasterized mask.
qimage = self.qimage_from_shape_item(item)
# do some stuff
@staticmethod
def qimage_from_shape_item(item: QtWidgets.QAbstractGraphicsShapeItem) -> QtGui.QImage:
# Get items pen and brush to set back later.
pen = item.pen()
brush = item.brush()
# Remove pen, set brush to white.
item.setPen(QtGui.QPen(QtCore.Qt.NoPen))
item.setBrush(QtCore.Qt.white)
# Determine the bounding box in pixel coordinates.
top = int(item.scenePos().y() + item.boundingRect().top())
left = int(item.scenePos().x() + item.boundingRect().left())
bottom = int(item.scenePos().y() + item.boundingRect().bottom()) + 1
right = int(item.scenePos().x() + item.boundingRect().right()) + 1
size = QtCore.QSize(right - left, bottom - top)
# Initialize qimage, use 8-bit grayscale.
qimage = QtGui.QImage(size, QtGui.QImage.Format_Grayscale8)
qimage.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(qimage)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# Offset the painter to paint item in its correct pixel location.
painter.translate(item.scenePos().x() - left, item.scenePos().y() - top)
# Paint the item.
item.paint(painter, QtWidgets.QStyleOptionGraphicsItem())
# Set the pen and brush back.
item.setPen(pen)
item.setBrush(brush)
# Set the pixel coordinate offset of the item to the QImage.
qimage.setOffset(QtCore.QPoint(left, top))
return qimage
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(widget)
view = QtWidgets.QGraphicsView()
layout.addWidget(view)
scene = QtWidgets.QGraphicsScene()
view.setScene(scene)
thread = MyThread(scene)
view.setFixedSize(400, 300)
scene.setSceneRect(0, 0, 400, 300)
rect_item = QtWidgets.QGraphicsRectItem()
p = QtCore.QPointF(123.4, 56.78)
rect_item.setPos(p)
r = QtCore.QRectF(0., 0., 161.8, 100.)
rect_item.setRect(r)
scene.addItem(rect_item)
button = QtWidgets.QPushButton("Get masks")
layout.addWidget(button)
button.clicked.connect(thread.start)
widget.show()
sys.exit(app.exec_())
问题是您“只能在主线程上创建和使用 GUI 小部件”,请参阅 this SO answer here 中的更多信息。
我的解决方法是将GUI交互部分,即qimage_from_shape_item(),从线程中取出,在主循环中处理。我想我直接使用这些项目仍然不是很好,虽然没有明显的闪烁效果或临时设置 NoPen 的任何东西。
另一种方法可能是使用 QGraphicsScene::render;但是,我不知道如何在不与场景中的其他项目交互的情况下逐一渲染项目。