Modifying mouse click and drag events in ImageItem - signals.connect versus functions

858 views
Skip to first unread message

Talia Weiss

unread,
May 17, 2018, 12:35:37 PM5/17/18
to pyqtgraph
Hi all,

I'm trying to write some code that will allow me to paint on an image in different colors and I'm very confused about how to go about it.  ImageItem does implement a small drawing example but as I want to extend the functionality further the ImageItem-draw example script has been of little help.

Basically I need access to mouse click and mouse drag events on an imageItem.  I thought that grabbing the scene and connecting sigMouseClicked would give me access to that, but I have no idea how to interpret either the evt.pos() or evt.scenePos() points it returns.  I have an ImageItem inside a PlotItem so I have axes for the image, and the points returned do not correspond at all to where I am clicking.

I've additionally tried to make a new class inheriting ImageItem, but overwriting the mouseClick and mouseDrag events don't seem to work.

I'm overall very confused about where the mousedrag and mouseclick events for things like dragging and scaling the scene are even handled?  It doesn't seem to be in pg.GraphicsItem/etc.  This is important because I want to be able to paint on an image and drag it still (maybe by shift-dragging instead of just dragging, which would draw to the screen)?

Any advice or insight anyone could give me on this would be greatly appreciated.

Example code: 
def paintOn(evt):
    global img
    pos = evt.pos()
    if evt.button() == QtCore.Qt.LeftButton:
        x, y = (int(pos.x()), int(pos.y()))
        print(pos)
        print(evt.scenePos())
        w, h, _ = img.image.shape
        #x = x - h
        #y = y-h
        imcopy = img.image.copy()
        imcopy[y-10:y+10, x-10:x+10, :] = [255, 255, 255]
        img.setImage(imcopy, autoLevels=False)

# Interpret image data as row-major instead of row-major
pg.setConfigOptions(imageAxisOrder='row-major')

app = pg.mkQApp() #alias for QtGui.QApplication([])

## Create window with ImageView widget
win = pg.GraphicsLayoutWidget()
win.setWindowTitle('Watershed')
win.show()

## Add a PlotItem
p1 = win.addPlot()
# an ImageItem then goes in the viewbox to display an image
img = pg.ImageItem()
p1.addItem(img)

## Initially display the first frame
img.setImage(np.zeros((100, 100, 3)))
## make the image the correct orientation
## only have to do this once
img.getViewBox().invertY(True)


#get the scene of the imag
sc = img.scene()
sc.sigMouseClicked.connect(paintOn)

Luke Campagnola

unread,
May 17, 2018, 11:58:02 PM5/17/18
to pyqt...@googlegroups.com
Regarding the use of scene events: you just need to map the position of the event to the coordinate system of your ImageItem, like:

    image_pos = image_item.mapFromScene(event.scenePos())

If this makes no sense at all, then I recommend reading about Qt GraphicsView and how it handles coordinate system (http://doc.qt.io/archives/qt-4.8/graphicsview.html).

All that said, probably catching events directly from the ImageItem is the best way to go for your purpose. This requires creating a subclass of ImageItem that reimplements the methods mouseClickEvent, mouseDragEvent, and hoverEvent:

    import pyqtgraph as pg


    class DrawingImage(pg.ImageItem):
        def mouseClickEvent(self, event):
            print("Click", event.pos())

        def mouseDragEvent(self, event):
            if event.isStart():
                print("Start drag", event.pos())
            elif event.isFinish():
                print("Stop drag", event.pos())
            else:
                print("Drag", event.pos())

        def hoverEvent(self, event):
            if not event.isExit():
                # the mouse is hovering over the image; make sure no other items
                # will receive left click/drag events from here.
                event.acceptDrags(pg.QtCore.Qt.LeftButton)
                event.acceptClicks(pg.QtCore.Qt.LeftButton)


    img = DrawingImage(pg.np.random.normal(size=(100, 150)), axisOrder='row-major')
    view = pg.plot()
    view.addItem(img)

In the example above, event.pos() is already expressed in the coordinate system of the image, so no mapping is necessary.
Also note that because we are capturing left-drag events, you won't be able to pan the view using the left mouse button, but middle-drag will still work.


--
You received this message because you are subscribed to the Google Groups "pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyqtgraph+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pyqtgraph/c14ef223-1051-4c18-b124-39ca1c104d69%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Talia Weiss

unread,
Aug 20, 2018, 1:25:16 PM8/20/18
to pyqtgraph
Sorry it took so long to get back to you.

Thanks this makes a lot of sense!  What helped the most was the hoverEvent example you provided - event.acceptClicks and event.acceptDrags basically determines whether handling of the event happens in this level (i.e. the subclass of ImageItem) versus the original ImageItem or higher in the hierarchy?


I have another related question - what about overriding keypress behavior?  Should it be done all the way on the GraphicsWindow level (like had been suggested https://stackoverflow.com/questions/40423999/pyqtgraph-where-to-find-signal-for-key-preses), or can I also subclass something like ViewBox?

Obviously adding a keyPressEvent to ImageItem didn't do anything (because there is no scene object?) but when I try to subclass ViewBox just adding a print(ev.key()) to keyPressEvent function I just get segmentation faults.

Thanks again so much for your help
To unsubscribe from this group and stop receiving emails from it, send an email to pyqtgraph+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages