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
我想通过在 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