【发布时间】:2015-08-14 20:32:52
【问题描述】:
我创建了一个基于QWidget 的ImageView 小部件,其中包含一个QGraphicsView。此小部件显示图像,并允许您使用鼠标通过QGraphicsRectItem 绘制 ROI(感兴趣区域)。
ImageView 小部件运行良好,但如果拖动 ROI 矩形并且您想在另一个地方重绘它,则鼠标事件捕获的位置无法正确映射到场景。
这里有几张图片来解释我的意思。
包含 ImageView 小部件的对话框,其操作控制小部件本身:
如果启用选择,您可以绘制一个矩形:
注意矩形右下角的指针位置。 ROI 可以在图像内部绘制。
如果禁用选择,您可以拖动之前绘制的矩形:
在此之后,如果启用了选择并且您想重绘矩形:
指针位置被很好地捕获(这是事实!),正如您在对话框状态栏中看到的那样,但是这个位置(用于设置矩形几何形状)不再对应于矩形位置。
我对 Qt 很陌生,这里是代码:
代码
class ImageView(QtGui.QWidget):
scaleChanged = QtCore.pyqtSignal()
statusChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(ImageView, self).__init__(parent)
self.scale_factor = 0.0
# Imagen
self.image_item = QtGui.QGraphicsPixmapItem()
# ROI
self.ROI_item = FancyQGraphicsRectItem(self.image_item)
self.ROI_item.setFlag(self.ROI_item.ItemIsMovable)
self.ROI_item.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
self.ROI_item.setPen(QtGui.QPen(QtCore.Qt.white, 0, QtCore.Qt.DashDotLine))
self.ROI_item.setCursor(QtCore.Qt.OpenHandCursor)
self.ROI_added = False
# Escena
self.scene = QtGui.QGraphicsScene()
# Vista
self.view = FancyQGraphicsView()
self.view.ROI_item = self.ROI_item
self.view.statusChanged.connect(self.change_status)
self.view.setScene(self.scene)
self.view.setBackgroundRole(QtGui.QPalette.Dark)
self.view.setAlignment(QtCore.Qt.AlignCenter)
self.view.setFrameShape(QtGui.QFrame.NoFrame)
self.view.setRenderHint(QtGui.QPainter.Antialiasing, False)
self.view.setMouseTracking(True)
# Disposición
layout = QtGui.QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.view)
self.setLayout(layout)
def setImage(self, pixmap):
self.image_item.setPixmap(pixmap)
self.scene.addItem(self.image_item)
self.scene.setSceneRect(0, 0, self.image_item.boundingRect().right(), self.image_item.boundingRect().bottom())
self.view.setSceneSize()
def selectionEnable(self, value):
self.view.selectionEnable(value)
if value:
self.ROI_item.setCursor(QtCore.Qt.CrossCursor)
self.view.setInteractive(False)
self.view.viewport().setCursor(QtCore.Qt.CrossCursor)
if not self.ROI_added:
self.ROI_added = True
else:
self.view.viewport().setCursor(QtCore.Qt.ArrowCursor)
self.ROI_item.setCursor(QtCore.Qt.OpenHandCursor)
self.view.setInteractive(True)
def setupDrag(self, value):
if value:
self.view.setInteractive(False)
self.view.setDragMode(self.view.ScrollHandDrag)
else:
self.view.setDragMode(self.view.NoDrag)
self.view.setInteractive(True)
def normal_size(self):
if self.scale_factor != 1.0:
self.view.resetMatrix()
self.scale_factor = 1.0
self.scaleChanged.emit()
def scale_image(self, factor):
self.scale_factor *= factor
self.view.scale(factor, factor)
self.scaleChanged.emit()
def delete_roi(self):
self.ROI_item.setRect(0, 0, 0, 0)
@QtCore.pyqtSlot(str)
def change_status(self, message):
self.statusChanged.emit(message)
class FancyQGraphicsView(QtGui.QGraphicsView):
statusChanged = QtCore.pyqtSignal(str)
scene_size = (0, 0)
ROI_item = None
event_origin = None
selection = False
click = False
def mousePressEvent(self, event):
if self.selection:
event_pos = self.mapToScene(event.pos())
pos = (int(event_pos.x()), int(event_pos.y()))
if 0 <= pos[0] < self.scene_size[0] and 0 <= pos[1] < self.scene_size[1]:
self.event_origin = event_pos
else:
self.event_origin = None
self.click = True
else:
QtGui.QGraphicsView.mousePressEvent(self, event)
def mouseMoveEvent(self, event):
event_pos = self.mapToScene(event.pos())
if self.selection and self.click:
if self.event_origin:
self.statusChanged.emit("x1: {0:>5d} y1: {1:>5d} "
"x2: {2:>5d} y2: {3:>5d}".format(int(self.event_origin.x()),
int(self.event_origin.y()),
int(event_pos.x()),
int(event_pos.y())))
if event_pos.x() < 0:
event_pos.setX(0)
elif event_pos.x() > self.scene_size[0] - 1:
event_pos.setX(self.scene_size[0] - 1)
if event_pos.y() < 0:
event_pos.setY(0)
elif event_pos.y() > self.scene_size[1] - 1:
event_pos.setY(self.scene_size[1] - 1)
self.ROI_item.setRect(QtCore.QRectF(self.event_origin, event_pos).normalized())
print self.ROI_item.rect(), self.event_origin, event_pos
else:
self.statusChanged.emit("x: {0:>5d} y: {1:>5d}".format(int(event_pos.x()), int(event_pos.y())))
else:
self.statusChanged.emit("x: {0:>5d} y: {1:>5d}".format(int(event_pos.x()), int(event_pos.y())))
QtGui.QGraphicsView.mouseMoveEvent(self, event)
def mouseReleaseEvent(self, event):
if self.selection:
self.click = False
if self.event_origin:
self.event_origin = None
else:
QtGui.QGraphicsView.mouseReleaseEvent(self, event)
def selectionEnable(self, value):
self.selection = value
def setSceneSize(self):
rect = self.scene().sceneRect()
self.scene_size = (rect.width(), rect.height())
class FancyQGraphicsRectItem(QtGui.QGraphicsRectItem):
def mousePressEvent(self, event):
self.setCursor(QtCore.Qt.ClosedHandCursor)
QtGui.QGraphicsRectItem.mousePressEvent(self, event)
def mouseMoveEvent(self, event):
QtGui.QGraphicsRectItem.mouseMoveEvent(self, event)
# Maybe this could be modified
def mouseReleaseEvent(self, event):
self.setCursor(QtCore.Qt.OpenHandCursor)
QtGui.QGraphicsRectItem.mouseReleaseEvent(self, event)
为什么会这样?以前我尝试在矩形的图像中实现一个受限的可移动区域,但是当被拖动时,该项目无法正确识别其在场景中的位置。
【问题讨论】:
-
我知道这并不能解决您的问题,但是您是否有理由从头开始构建 ROI 功能?如果我没记错的话,Pyqtgraph 有可移动的投资回报率。
-
@three_pineapples 因为我想学习。 ;) 我知道 PyQtGraph,但因为我只需要这个基本功能,所以我想自己构建它。
-
酷!我只是想检查一下你没有浪费时间:)