The ViewBox class of pyqtgraph has only two "mouse modes":
- PanMode: Left and Middle mouse work for drag. Right mouse work for zoom in/out by drag.
- RectMode: Left and Middle mouse work to zoom the box. Right mouse work for zoom in/out by drag.
The reason why Left and Middle mouse works the same lays in the source code of the ViewBox class.
You want the Left mouse to drag the picture, the Middle mouse work to zoom the box and the Middle mouse work to zoom the box for zoom in/out by drag. My solution may be radical, but it works. I modified the class, using the source code as a model (source : Pyqtgraph's ViewBox class):
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
class ViewBox(pg.ViewBox):
MyMode = 2
def __init__(self):
pg.ViewBox.__init__(self)
def setMouseMode(self, mode):
## First we change this method to accept our new configuration
if mode not in [ViewBox.PanMode, ViewBox.RectMode, ViewBox.MyMode]:
raise Exception("Mode must be ViewBox.PanMode, ViewBox.RectMode or ViewBox.MyMode")
self.state['mouseMode'] = mode
self.sigStateChanged.emit(self)
def mouseDragEvent(self, ev, axis=None):
ev.accept()
pos = ev.pos()
lastPos = ev.lastPos()
dif = pos - lastPos
dif = dif * -1
mouseEnabled = np.array(self.state['mouseEnabled'], dtype=np.float)
mask = mouseEnabled.copy()
if axis is not None:
mask[1-axis] = 0.0
## Here is the main change for it to work
if ev.button() & QtCore.Qt.LeftButton:
if self.state['mouseMode'] == ViewBox.RectMode and axis is None:
if ev.isFinish(): ## This is the final move in the drag; change the view scale now
#print "finish"
self.rbScaleBox.hide()
ax = QtCore.QRectF(QtCore.QPointF(ev.buttonDownPos(ev.button())), QtCore.QPointF(pos))
ax = self.childGroup.mapRectFromParent(ax)
self.showAxRect(ax)
self.axHistoryPointer += 1
self.axHistory = self.axHistory[:self.axHistoryPointer] + [ax]
else:
## update shape of scale box
self.updateScaleBox(ev.buttonDownPos(), ev.pos())
else:
tr = self.childGroup.transform()
tr = pg.functions.invertQTransform(tr)
tr = tr.map(dif*mask) - tr.map(QtCore.QPointF(0,0))
x = tr.x() if mask[0] == 1 else None
y = tr.y() if mask[1] == 1 else None
self._resetTarget()
if x is not None or y is not None:
self.translateBy(x=x, y=y)
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
elif ev.button() & QtCore.Qt.MidButton:
if self.state['mouseMode'] in {ViewBox.RectMode,ViewBox.MyMode} and axis is None:
if ev.isFinish(): ## This is the final move in the drag; change the view scale now
#print "finish"
self.rbScaleBox.hide()
ax = QtCore.QRectF(QtCore.QPointF(ev.buttonDownPos(ev.button())), QtCore.QPointF(pos))
ax = self.childGroup.mapRectFromParent(ax)
self.showAxRect(ax)
self.axHistoryPointer += 1
self.axHistory = self.axHistory[:self.axHistoryPointer] + [ax]
else:
## update shape of scale box
self.updateScaleBox(ev.buttonDownPos(), ev.pos())
else:
tr = self.childGroup.transform()
tr = pg.functions.invertQTransform(tr)
tr = tr.map(dif*mask) - tr.map(QtCore.QPointF(0,0))
x = tr.x() if mask[0] == 1 else None
y = tr.y() if mask[1] == 1 else None
self._resetTarget()
if x is not None or y is not None:
self.translateBy(x=x, y=y)
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
elif ev.button() & QtCore.Qt.RightButton:
if self.state['aspectLocked'] is not False:
mask[0] = 0
dif = ev.screenPos() - ev.lastScreenPos()
dif = np.array([dif.x(), dif.y()])
dif[0] *= -1
s = ((mask * 0.02) + 1) ** dif
tr = self.childGroup.transform()
tr = pg.functions.invertQTransform(tr)
x = s[0] if mouseEnabled[0] == 1 else None
y = s[1] if mouseEnabled[1] == 1 else None
center = QtCore.QPointF(tr.map(ev.buttonDownPos(QtCore.Qt.RightButton)))
self._resetTarget()
self.scaleBy(x=x, y=y, center=center)
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
We inherit Pyqtgraph's ViewBox class, and then change two methods:
setMouseMode() for compatibility
mouseDragEvent for the different function of the 3 clicks
Whit the class above you maintain the other two mouse modes as they are, but adding a new mode: MyMode.
Then instead of adding the ViewBox like this :
sub2 = win.addLayout()
sub2.addLabel("<b>One-button mouse interaction:</b><br>left-drag zoom to box, wheel to zoom out.")
sub2.nextRow()
v2 = sub2.addViewBox()
v2.setMouseMode(v2.RectMode)
l2 = pg.PlotDataItem(y)
v2.addItem(l2)
You should do it like this:
sub2 = win.addLayout()
sub2.addLabel("<b>One-button mouse interaction:</b><br>left-drag zoom to box, wheel to zoom out.")
sub2.nextRow()
v2 = ViewBox() ## This is our new class
sub2.addItem(v2)
v2.setMouseMode(v2.MyMode) ## Seting the new Mouse Mode
l2 = pg.PlotDataItem(y)
v2.addItem(l2)
I tested the code and did not found any issues, but I have to admit that the solution is a little bit exaggerated. I hope there is a simpler method.
Edit: The example is taken from Pyqtgraph's examples (ViewBox Features) (Here)