pyqtgraph/graphicsItems/ViewBox/ViewBoxMenu.py
Luke Campagnola 6932c34126 - Added workaround for Qt bug: https://bugreports.qt-project.org/browse/QTBUG-18616. (GraphicsItem.setParent needs to check for scene change first)
This _could_ cause other problems, but they will certainly be fewer than the existing problems.

- Fixed bugs with ViewBox linking to views which are subsequently deleted
2012-06-18 14:00:19 -04:00

262 lines
9.1 KiB
Python

from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.WidgetGroup import WidgetGroup
from .axisCtrlTemplate import Ui_Form as AxisCtrlTemplate
import weakref
class ViewBoxMenu(QtGui.QMenu):
def __init__(self, view):
QtGui.QMenu.__init__(self)
self.view = weakref.ref(view) ## keep weakref to view to avoid circular reference (don't know why, but this prevents the ViewBox from being collected)
self.valid = False ## tells us whether the ui needs to be updated
self.viewMap = weakref.WeakValueDictionary() ## weakrefs to all views listed in the link combos
self.setTitle("ViewBox options")
self.viewAll = QtGui.QAction("View All", self)
self.viewAll.triggered.connect(self.autoRange)
self.addAction(self.viewAll)
self.axes = []
self.ctrl = []
self.widgetGroups = []
self.dv = QtGui.QDoubleValidator(self)
for axis in 'XY':
m = QtGui.QMenu()
m.setTitle("%s Axis" % axis)
w = QtGui.QWidget()
ui = AxisCtrlTemplate()
ui.setupUi(w)
a = QtGui.QWidgetAction(self)
a.setDefaultWidget(w)
m.addAction(a)
self.addMenu(m)
self.axes.append(m)
self.ctrl.append(ui)
wg = WidgetGroup(w)
self.widgetGroups.append(w)
connects = [
(ui.mouseCheck.toggled, 'MouseToggled'),
(ui.manualRadio.clicked, 'ManualClicked'),
(ui.minText.editingFinished, 'MinTextChanged'),
(ui.maxText.editingFinished, 'MaxTextChanged'),
(ui.autoRadio.clicked, 'AutoClicked'),
(ui.autoPercentSpin.valueChanged, 'AutoSpinChanged'),
(ui.linkCombo.currentIndexChanged, 'LinkComboChanged'),
(ui.autoPanCheck.toggled, 'AutoPanToggled'),
(ui.visibleOnlyCheck.toggled, 'VisibleOnlyToggled')
]
for sig, fn in connects:
sig.connect(getattr(self, axis.lower()+fn))
self.ctrl[0].invertCheck.hide() ## no invert for x-axis
self.ctrl[1].invertCheck.toggled.connect(self.yInvertToggled)
## exporting is handled by GraphicsScene now
#self.export = QtGui.QMenu("Export")
#self.setExportMethods(view.exportMethods)
#self.addMenu(self.export)
self.leftMenu = QtGui.QMenu("Mouse Mode")
group = QtGui.QActionGroup(self)
pan = self.leftMenu.addAction("3 button", self.set3ButtonMode)
zoom = self.leftMenu.addAction("1 button", self.set1ButtonMode)
pan.setCheckable(True)
zoom.setCheckable(True)
pan.setActionGroup(group)
zoom.setActionGroup(group)
self.mouseModes = [pan, zoom]
self.addMenu(self.leftMenu)
self.view().sigStateChanged.connect(self.viewStateChanged)
self.updateState()
def copy(self):
m = QtGui.QMenu()
for sm in self.subMenus():
if isinstance(sm, QtGui.QMenu):
m.addMenu(sm)
else:
m.addAction(sm)
m.setTitle(self.title())
return m
def subMenus(self):
if not self.valid:
self.updateState()
return [self.viewAll] + self.axes + [self.leftMenu]
def setExportMethods(self, methods):
self.exportMethods = methods
self.export.clear()
for opt, fn in methods.items():
self.export.addAction(opt, self.exportMethod)
def viewStateChanged(self):
self.valid = False
if self.ctrl[0].minText.isVisible() or self.ctrl[1].minText.isVisible():
self.updateState()
def updateState(self):
## Something about the viewbox has changed; update the menu GUI
state = self.view().getState(copy=False)
if state['mouseMode'] == ViewBox.PanMode:
self.mouseModes[0].setChecked(True)
else:
self.mouseModes[1].setChecked(True)
for i in [0,1]: # x, y
tr = state['targetRange'][i]
self.ctrl[i].minText.setText("%0.5g" % tr[0])
self.ctrl[i].maxText.setText("%0.5g" % tr[1])
if state['autoRange'][i] is not False:
self.ctrl[i].autoRadio.setChecked(True)
if state['autoRange'][i] is not True:
self.ctrl[i].autoPercentSpin.setValue(state['autoRange'][i]*100)
else:
self.ctrl[i].manualRadio.setChecked(True)
self.ctrl[i].mouseCheck.setChecked(state['mouseEnabled'][i])
## Update combo to show currently linked view
c = self.ctrl[i].linkCombo
c.blockSignals(True)
try:
view = state['linkedViews'][i] ## will always be string or None
if view is None:
view = ''
ind = c.findText(view)
if ind == -1:
ind = 0
c.setCurrentIndex(ind)
finally:
c.blockSignals(False)
self.ctrl[i].autoPanCheck.setChecked(state['autoPan'][i])
self.ctrl[i].visibleOnlyCheck.setChecked(state['autoVisibleOnly'][i])
self.ctrl[1].invertCheck.setChecked(state['yInverted'])
self.valid = True
def autoRange(self):
self.view().autoRange() ## don't let signal call this directly--it'll add an unwanted argument
def xMouseToggled(self, b):
self.view().setMouseEnabled(x=b)
def xManualClicked(self):
self.view().enableAutoRange(ViewBox.XAxis, False)
def xMinTextChanged(self):
self.ctrl[0].manualRadio.setChecked(True)
self.view().setXRange(float(self.ctrl[0].minText.text()), float(self.ctrl[0].maxText.text()), padding=0)
def xMaxTextChanged(self):
self.ctrl[0].manualRadio.setChecked(True)
self.view().setXRange(float(self.ctrl[0].minText.text()), float(self.ctrl[0].maxText.text()), padding=0)
def xAutoClicked(self):
val = self.ctrl[0].autoPercentSpin.value() * 0.01
self.view().enableAutoRange(ViewBox.XAxis, val)
def xAutoSpinChanged(self, val):
self.ctrl[0].autoRadio.setChecked(True)
self.view().enableAutoRange(ViewBox.XAxis, val*0.01)
def xLinkComboChanged(self, ind):
self.view().setXLink(str(self.ctrl[0].linkCombo.currentText()))
def xAutoPanToggled(self, b):
self.view().setAutoPan(x=b)
def xVisibleOnlyToggled(self, b):
self.view().setAutoVisible(x=b)
def yMouseToggled(self, b):
self.view().setMouseEnabled(y=b)
def yManualClicked(self):
self.view().enableAutoRange(ViewBox.YAxis, False)
def yMinTextChanged(self):
self.ctrl[1].manualRadio.setChecked(True)
self.view().setYRange(float(self.ctrl[1].minText.text()), float(self.ctrl[1].maxText.text()), padding=0)
def yMaxTextChanged(self):
self.ctrl[1].manualRadio.setChecked(True)
self.view().setYRange(float(self.ctrl[1].minText.text()), float(self.ctrl[1].maxText.text()), padding=0)
def yAutoClicked(self):
val = self.ctrl[1].autoPercentSpin.value() * 0.01
self.view().enableAutoRange(ViewBox.YAxis, val)
def yAutoSpinChanged(self, val):
self.ctrl[1].autoRadio.setChecked(True)
self.view().enableAutoRange(ViewBox.YAxis, val*0.01)
def yLinkComboChanged(self, ind):
self.view().setYLink(str(self.ctrl[1].linkCombo.currentText()))
def yAutoPanToggled(self, b):
self.view().setAutoPan(y=b)
def yVisibleOnlyToggled(self, b):
self.view().setAutoVisible(y=b)
def yInvertToggled(self, b):
self.view().invertY(b)
def exportMethod(self):
act = self.sender()
self.exportMethods[str(act.text())]()
def set3ButtonMode(self):
self.view().setLeftButtonAction('pan')
def set1ButtonMode(self):
self.view().setLeftButtonAction('rect')
def setViewList(self, views):
names = ['']
self.viewMap.clear()
## generate list of views to show in the link combo
for v in views:
name = v.name
if name is None: ## unnamed views do not show up in the view list (although they are linkable)
continue
names.append(name)
self.viewMap[name] = v
for i in [0,1]:
c = self.ctrl[i].linkCombo
current = asUnicode(c.currentText())
c.blockSignals(True)
changed = True
try:
c.clear()
for name in names:
c.addItem(name)
if name == current:
changed = False
c.setCurrentIndex(c.count()-1)
finally:
c.blockSignals(False)
if changed:
c.setCurrentIndex(0)
c.currentIndexChanged.emit(c.currentIndex())
from .ViewBox import ViewBox