PyQtgraph - 通过鼠标单击和拖动绘制 ROI

PyQtgraph - Draw ROI by mouse click & drag

我想通过在 PlotWidget 中单击和拖动事件来绘制 ROI。问题是 PlotWidget 已经保留了几个点击交互,其次很难判断鼠标在 PlotWidget 中的正确位置 - 特别是当图像比例已更改或 window 比例已更改时.

import pyqtgraph as pg
import pyqtgraph.opengl as gl
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from PyQt5 import Qt
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import vtk, sys
import numpy as np
from PIL import Image
class GUI:
    def __init__(self):
        self.init_gui() 

    def proxyWidget(self, item, width=None, height=None):
        proxy = QtGui.QGraphicsProxyWidget()
        if(height != None):
            height = item.sizeHint().height() if height==None else height
            item.setMaximumHeight(height)
        if(width!=None):
            width = item.sizeHint().width() if width==None else width
            item.setMaximumWidth(width)
        proxy.setWidget(item)
        return proxy

    def init_gui(self, win_height=800, win_width=1800):
        pg.setConfigOptions(imageAxisOrder='row-major')
        pg.setConfigOption('background', 'w')
        pg.setConfigOption('foreground', 'k')
        self.w = pg.GraphicsWindow(size=(win_width,win_height), border=True)
        self.img = pg.ImageItem()
        self.list_imgs       = QtGui.QListWidget()
        self.btn_Del_Mark    = QtGui.QPushButton('Del Mark')
        self.btn_MarkPed     = QtGui.QPushButton('Mark ped')
        self.lbl_list1       = QtGui.QLabel("List Images")
        self.lbl_list2       = QtGui.QLabel("List Markings")
        self.list_imgs       = QtGui.QListWidget()
        self.list_marks      = QtGui.QListWidget()
        self.layout = QtGui.QGridLayout()
        self.w.setLayout(self.layout)
        #self.w_3d = pg.GraphicsWindow()

        self.vtkWidget = QVTKRenderWindowInteractor()
        #self.w_3d.addItem(self.proxyWidget(self.vtkWidget))

        self.vtkWidget.Initialize()
        self.vtkWidget.Start()
        self.ren = vtk.vtkRenderer()
        self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
        self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()

        # Create source
        source = vtk.vtkSphereSource()
        source.SetCenter(0, 0, 0)
        source.SetRadius(5.0)

        # Create a mapper
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputConnection(source.GetOutputPort())

        # Create an actor
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)

        self.ren.AddActor(actor)

        self.ren.ResetCamera()
        self.iren.Initialize()
        self.iren.Start()
        path = "/home/brain/uni/frustum-pointnets/dataset/KITTI/object/testing/image_2/000000.png"
        imgdata = Image.open(path)
        self.imgArr = np.array(imgdata)
        #ToDo: undistort Image if neccessary

        self.img.setImage(self.imgArr)
        #self.vbLayout = self.w.addLayout(row=0,  col=3, rowspan=10,  colspan=20)
        imageGraph = pg.PlotWidget(name='Signalgraph')
        self.vb = imageGraph.plotItem.vb
        self.lbl_list1.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_list2.setAlignment(QtCore.Qt.AlignCenter)
        self.vb.setAspectLocked()
        self.vb.addItem(self.img)
        self.vb.invertY(True)
        self.vb.setMaximumSize(int(7/10.*win_width), int(9/20.*win_height))
        self.layout.addWidget(imageGraph,                           1 , 3, 10,  20)
        self.layout.addWidget(self.vtkWidget                      , 11, 3, 10,  20)
        self.layout.addWidget(self.lbl_list1 ,                                  0, 1, 1, 1)   
        self.layout.addWidget(self.lbl_list2 ,                                  0, 2, 1, 1)  
        self.layout.addWidget(self.list_imgs ,                                  1, 1, 20,1)   
        self.layout.addWidget(self.list_marks,                                  1, 2, 20,1)   
        sizeHint =  lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
        self.lbl_list1.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
        self.lbl_list2.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
        self.list_imgs.sizeHint  = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
        self.list_marks.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
        self.list_imgs.setMaximumWidth(int(1./10.*win_width))
        self.list_marks.setMaximumWidth(int(1./10.*win_width))

        self.vtkWidget.show()


if __name__ == "__main__":
    app = QtGui.QApplication([])
    guiobj = GUI()

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

我想通过单击鼠标开始绘制 ROI 并通过释放鼠标停止绘制...每个提示都会有所帮助。请考虑 PlotWidget 的内容是可拖动的,并且在绘制 ROI 时可能需要将其冻结。


编辑:

我试图用以下几行临时覆盖点击事件,但不知何故,点击事件似乎在其他地方被触发,因为我的函数没有被调用...



    def on_btn_MarkPed(self):
        #self.vb.setMouseEnabled(x=False, y=False)
        self.creatRoiByMouse("Pedestrian")

    def on_btn_MarkCycl(self):
        self.creatRoiByMouse("Cyclist")

    def on_btn_MarkVehicle(self):
        self.creatRoiByMouse("Vehicle")

    def creatRoiByMouse(self, class2Mark):
        self.img.mousePressEvent   = self.ImgMousePressEvent
        self.img.mouseReleaseEvent = self.ImgMouseReleaseEvent

    def ImgMousePressEvent(self, event):
        print(event)
        pass
#
#
    def ImgMouseReleaseEvent(self, event):
        print(event)
        pass

我知道这个 post 已经过时了,但如果有一天其他人发现了它,我想我会 post 我的解决方案。这很不优雅,但对我有用。在我的应用程序中,我有一个标记为“绘制”的按钮,它调用一个临时覆盖鼠标拖动事件的函数来简单地绘制一个框。覆盖的鼠标拖动事件使用完成信号恢复本机鼠标拖动事件。如果在按下绘制按钮之后但在绘制之前按下,此代码还会覆盖转义键以取消绘制。在我的代码中,imageArrayItem 是绘图小部件中的现有 imageItem,Dialog 是包含绘图小部件的 QDialog。

def clickDraw(self):
    app.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
    imageArrayItem.getViewBox().setMouseMode(ViewBox.RectMode)
    imageArrayItem.getViewBox().rbScaleBox.setPen(fn.mkPen((255, 255, 255), width = 1))
    imageArrayItem.getViewBox().rbScaleBox.setBrush(fn.mkBrush(255, 255, 255, 100))
    
    def mouseDragEvent(ev, axis = None): # This is a modified version of the original mouseDragEvent function in pyqtgraph.ViewBox
        ev.accept()  # accept all buttons
        dif = (ev.pos() - ev.lastPos()) * -1
        mouseEnabled = np.array(imageArrayItem.getViewBox().state['mouseEnabled'], dtype = np.float)
        mask = mouseEnabled.copy()
        if ev.button() & QtCore.Qt.LeftButton:
            if imageArrayItem.getViewBox().state['mouseMode'] == ViewBox.RectMode:
                if ev.isFinish():
                    QtCore.QTimer.singleShot(0, self.restoreCursor)
                    imageArrayItem.getViewBox().rbScaleBox.hide()
                    ax = QtCore.QRectF(Point(ev.buttonDownPos(ev.button())), Point(ev.pos()))
                    ax = imageArrayItem.getViewBox().childGroup.mapRectFromParent(ax)
                    imageArrayItem.getViewBox().mouseDragEvent = temp # reset to original mouseDragEvent
                    imageArrayItem.getViewBox().setMouseMode(ViewBox.PanMode)
                else:
                    imageArrayItem.getViewBox().updateScaleBox(ev.buttonDownPos(), ev.pos()) # update shape of scale box
        elif ev.button() & QtCore.Qt.MidButton: # allow for panning with middle mouse button
            tr = dif*mask
            tr = imageArrayItem.getViewBox().mapToView(tr) - imageArrayItem.getViewBox().mapToView(Point(0,0))
            x = tr.x() if mask[0] == 1 else None
            y = tr.y() if mask[1] == 1 else None
            imageArrayItem.getViewBox()._resetTarget()
            if x is not None or y is not None:
                imageArrayItem.getViewBox().translateBy(x=x, y=y)
            imageArrayItem.getViewBox().sigRangeChangedManually.emit(imageArrayItem.getViewBox().state['mouseEnabled'])
            
    def keyPressE_mouseDrag(event): # Override "esc" key to cancel draw
            if event.key() == QtCore.Qt.Key_Escape:
                QtCore.QTimer.singleShot(0, self.restoreCursor)
                imageArrayItem.getViewBox().rbScaleBox.hide()
                imageArrayItem.getViewBox().mouseDragEvent = temp # reset to original mouseDragEvent
                imageArrayItem.getViewBox().setMouseMode(ViewBox.PanMode)
            else:
                QtWidgets.QDialog.keyPressEvent(Dialog, event)
        Dialog.keyPressEvent = keyPressE_mouseDrag
        temp = imageArrayItem.getViewBox().mouseDragEvent # save original mouseDragEvent for later
        imageArrayItem.getViewBox().mouseDragEvent = mouseDragEvent # set to modified mouseDragEvent