Merge branch 'dont-copy-context-menu' into develop
This allows ViewBox context menus to be modified by directly manipulating ViewBox.menu
This commit is contained in:
commit
6e74df28a0
142
examples/contextMenu.py
Normal file
142
examples/contextMenu.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Demonstrates adding a custom context menu to a GraphicsItem
|
||||||
|
and extending the context menu of a ViewBox.
|
||||||
|
|
||||||
|
PyQtGraph implements a system that allows each item in a scene to implement its
|
||||||
|
own context menu, and for the menus of its parent items to be automatically
|
||||||
|
displayed as well.
|
||||||
|
|
||||||
|
"""
|
||||||
|
import initExample ## Add path to library (just for examples; you do not need this)
|
||||||
|
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from pyqtgraph.Qt import QtCore, QtGui
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
win = pg.GraphicsWindow()
|
||||||
|
win.setWindowTitle('pyqtgraph example: context menu')
|
||||||
|
|
||||||
|
|
||||||
|
view = win.addViewBox()
|
||||||
|
|
||||||
|
# add two new actions to the ViewBox context menu:
|
||||||
|
zoom1 = view.menu.addAction('Zoom to box 1')
|
||||||
|
zoom2 = view.menu.addAction('Zoom to box 2')
|
||||||
|
|
||||||
|
# define callbacks for these actions
|
||||||
|
def zoomTo1():
|
||||||
|
# note that box1 is defined below
|
||||||
|
view.autoRange(items=[box1])
|
||||||
|
zoom1.triggered.connect(zoomTo1)
|
||||||
|
|
||||||
|
def zoomTo2():
|
||||||
|
# note that box1 is defined below
|
||||||
|
view.autoRange(items=[box2])
|
||||||
|
zoom2.triggered.connect(zoomTo2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MenuBox(pg.GraphicsObject):
|
||||||
|
"""
|
||||||
|
This class draws a rectangular area. Right-clicking inside the area will
|
||||||
|
raise a custom context menu which also includes the context menus of
|
||||||
|
its parents.
|
||||||
|
"""
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
self.pen = pg.mkPen('r')
|
||||||
|
|
||||||
|
# menu creation is deferred because it is expensive and often
|
||||||
|
# the user will never see the menu anyway.
|
||||||
|
self.menu = None
|
||||||
|
|
||||||
|
# note that the use of super() is often avoided because Qt does not
|
||||||
|
# allow to inherit from multiple QObject subclasses.
|
||||||
|
pg.GraphicsObject.__init__(self)
|
||||||
|
|
||||||
|
|
||||||
|
# All graphics items must have paint() and boundingRect() defined.
|
||||||
|
def boundingRect(self):
|
||||||
|
return QtCore.QRectF(0, 0, 10, 10)
|
||||||
|
|
||||||
|
def paint(self, p, *args):
|
||||||
|
p.setPen(self.pen)
|
||||||
|
p.drawRect(self.boundingRect())
|
||||||
|
|
||||||
|
|
||||||
|
# On right-click, raise the context menu
|
||||||
|
def mouseClickEvent(self, ev):
|
||||||
|
if ev.button() == QtCore.Qt.RightButton:
|
||||||
|
if self.raiseContextMenu(ev):
|
||||||
|
ev.accept()
|
||||||
|
|
||||||
|
def raiseContextMenu(self, ev):
|
||||||
|
menu = self.getContextMenus()
|
||||||
|
|
||||||
|
# Let the scene add on to the end of our context menu
|
||||||
|
# (this is optional)
|
||||||
|
menu = self.scene().addParentContextMenus(self, menu, ev)
|
||||||
|
|
||||||
|
pos = ev.screenPos()
|
||||||
|
menu.popup(QtCore.QPoint(pos.x(), pos.y()))
|
||||||
|
return True
|
||||||
|
|
||||||
|
# This method will be called when this item's _children_ want to raise
|
||||||
|
# a context menu that includes their parents' menus.
|
||||||
|
def getContextMenus(self, event=None):
|
||||||
|
if self.menu is None:
|
||||||
|
self.menu = QtGui.QMenu()
|
||||||
|
self.menu.setTitle(self.name+ " options..")
|
||||||
|
|
||||||
|
green = QtGui.QAction("Turn green", self.menu)
|
||||||
|
green.triggered.connect(self.setGreen)
|
||||||
|
self.menu.addAction(green)
|
||||||
|
self.menu.green = green
|
||||||
|
|
||||||
|
blue = QtGui.QAction("Turn blue", self.menu)
|
||||||
|
blue.triggered.connect(self.setBlue)
|
||||||
|
self.menu.addAction(blue)
|
||||||
|
self.menu.green = blue
|
||||||
|
|
||||||
|
alpha = QtGui.QWidgetAction(self.menu)
|
||||||
|
alphaSlider = QtGui.QSlider()
|
||||||
|
alphaSlider.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
alphaSlider.setMaximum(255)
|
||||||
|
alphaSlider.setValue(255)
|
||||||
|
alphaSlider.valueChanged.connect(self.setAlpha)
|
||||||
|
alpha.setDefaultWidget(alphaSlider)
|
||||||
|
self.menu.addAction(alpha)
|
||||||
|
self.menu.alpha = alpha
|
||||||
|
self.menu.alphaSlider = alphaSlider
|
||||||
|
return self.menu
|
||||||
|
|
||||||
|
# Define context menu callbacks
|
||||||
|
def setGreen(self):
|
||||||
|
self.pen = pg.mkPen('g')
|
||||||
|
# inform Qt that this item must be redrawn.
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setBlue(self):
|
||||||
|
self.pen = pg.mkPen('b')
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setAlpha(self, a):
|
||||||
|
self.setOpacity(a/255.)
|
||||||
|
|
||||||
|
|
||||||
|
# This box's context menu will include the ViewBox's menu
|
||||||
|
box1 = MenuBox("Menu Box #1")
|
||||||
|
view.addItem(box1)
|
||||||
|
|
||||||
|
# This box's context menu will include both the ViewBox's menu and box1's menu
|
||||||
|
box2 = MenuBox("Menu Box #2")
|
||||||
|
box2.setParentItem(box1)
|
||||||
|
box2.setPos(5, 5)
|
||||||
|
box2.scale(0.2, 0.2)
|
||||||
|
|
||||||
|
## Start Qt event loop unless running in interactive mode or using pyside.
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
|
||||||
|
QtGui.QApplication.instance().exec_()
|
@ -520,27 +520,20 @@ class GraphicsScene(QtGui.QGraphicsScene):
|
|||||||
============== ==================================================
|
============== ==================================================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#items = self.itemsNearEvent(ev)
|
|
||||||
menusToAdd = []
|
menusToAdd = []
|
||||||
while item is not self:
|
while item is not self:
|
||||||
item = item.parentItem()
|
item = item.parentItem()
|
||||||
|
|
||||||
if item is None:
|
if item is None:
|
||||||
item = self
|
item = self
|
||||||
|
|
||||||
if not hasattr(item, "getContextMenus"):
|
if not hasattr(item, "getContextMenus"):
|
||||||
continue
|
continue
|
||||||
|
subMenus = item.getContextMenus(event) or []
|
||||||
|
if isinstance(subMenus, list): ## so that some items (like FlowchartViewBox) can return multiple menus
|
||||||
|
menusToAdd.extend(subMenus)
|
||||||
|
else:
|
||||||
|
menusToAdd.append(subMenus)
|
||||||
|
|
||||||
subMenus = item.getContextMenus(event)
|
if menusToAdd:
|
||||||
if subMenus is None:
|
|
||||||
continue
|
|
||||||
if type(subMenus) is not list: ## so that some items (like FlowchartViewBox) can return multiple menus
|
|
||||||
subMenus = [subMenus]
|
|
||||||
|
|
||||||
for sm in subMenus:
|
|
||||||
menusToAdd.append(sm)
|
|
||||||
|
|
||||||
if len(menusToAdd) > 0:
|
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
|
|
||||||
for m in menusToAdd:
|
for m in menusToAdd:
|
||||||
|
@ -618,9 +618,6 @@ class NodeGraphicsItem(GraphicsObject):
|
|||||||
def getMenu(self):
|
def getMenu(self):
|
||||||
return self.menu
|
return self.menu
|
||||||
|
|
||||||
def getContextMenus(self, event):
|
|
||||||
return [self.menu]
|
|
||||||
|
|
||||||
def raiseContextMenu(self, ev):
|
def raiseContextMenu(self, ev):
|
||||||
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
|
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
|
||||||
pos = ev.screenPos()
|
pos = ev.screenPos()
|
||||||
|
@ -437,10 +437,6 @@ class TerminalGraphicsItem(GraphicsObject):
|
|||||||
multi = self.menu.multiAct.isChecked()
|
multi = self.menu.multiAct.isChecked()
|
||||||
self.term.setMultiValue(multi)
|
self.term.setMultiValue(multi)
|
||||||
|
|
||||||
## probably never need this
|
|
||||||
#def getContextMenus(self, ev):
|
|
||||||
#return [self.getMenu()]
|
|
||||||
|
|
||||||
def removeSelf(self):
|
def removeSelf(self):
|
||||||
self.term.node().removeTerminal(self.term)
|
self.term.node().removeTerminal(self.term)
|
||||||
|
|
||||||
|
@ -585,3 +585,6 @@ class GraphicsItem(object):
|
|||||||
#def update(self):
|
#def update(self):
|
||||||
#self._qtBaseClass.update(self)
|
#self._qtBaseClass.update(self)
|
||||||
#print "Update:", self
|
#print "Update:", self
|
||||||
|
|
||||||
|
def getContextMenus(self, event):
|
||||||
|
return [self.getMenu()] if hasattr(self, "getMenu") else []
|
||||||
|
@ -1203,10 +1203,6 @@ class Handle(UIGraphicsItem):
|
|||||||
def getMenu(self):
|
def getMenu(self):
|
||||||
return self.menu
|
return self.menu
|
||||||
|
|
||||||
|
|
||||||
def getContextMenus(self, event):
|
|
||||||
return [self.menu]
|
|
||||||
|
|
||||||
def raiseContextMenu(self, ev):
|
def raiseContextMenu(self, ev):
|
||||||
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
|
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
|
||||||
|
|
||||||
|
@ -1067,31 +1067,15 @@ class ViewBox(GraphicsWidget):
|
|||||||
self.raiseContextMenu(ev)
|
self.raiseContextMenu(ev)
|
||||||
|
|
||||||
def raiseContextMenu(self, ev):
|
def raiseContextMenu(self, ev):
|
||||||
#print "viewbox.raiseContextMenu called."
|
|
||||||
|
|
||||||
#menu = self.getMenu(ev)
|
|
||||||
menu = self.getMenu(ev)
|
menu = self.getMenu(ev)
|
||||||
self.scene().addParentContextMenus(self, menu, ev)
|
self.scene().addParentContextMenus(self, menu, ev)
|
||||||
#print "2:", [str(a.text()) for a in self.menu.actions()]
|
menu.popup(ev.screenPos().toPoint())
|
||||||
pos = ev.screenPos()
|
|
||||||
#pos2 = ev.scenePos()
|
|
||||||
#print "3:", [str(a.text()) for a in self.menu.actions()]
|
|
||||||
#self.sigActionPositionChanged.emit(pos2)
|
|
||||||
|
|
||||||
menu.popup(QtCore.QPoint(pos.x(), pos.y()))
|
|
||||||
#print "4:", [str(a.text()) for a in self.menu.actions()]
|
|
||||||
|
|
||||||
def getMenu(self, ev):
|
def getMenu(self, ev):
|
||||||
self._menuCopy = self.menu.copy() ## temporary storage to prevent menu disappearing
|
return self.menu
|
||||||
return self._menuCopy
|
|
||||||
|
|
||||||
def getContextMenus(self, event):
|
def getContextMenus(self, event):
|
||||||
if self.menuEnabled():
|
return self.menu.actions() if self.menuEnabled() else []
|
||||||
return self.menu.subMenus()
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
#return [self.getMenu(event)]
|
|
||||||
|
|
||||||
|
|
||||||
def mouseDragEvent(self, ev, axis=None):
|
def mouseDragEvent(self, ev, axis=None):
|
||||||
## if axis is specified, event will only affect that axis.
|
## if axis is specified, event will only affect that axis.
|
||||||
|
@ -88,22 +88,6 @@ class ViewBoxMenu(QtGui.QMenu):
|
|||||||
|
|
||||||
self.updateState()
|
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):
|
def setExportMethods(self, methods):
|
||||||
self.exportMethods = methods
|
self.exportMethods = methods
|
||||||
self.export.clear()
|
self.export.clear()
|
||||||
@ -159,6 +143,10 @@ class ViewBoxMenu(QtGui.QMenu):
|
|||||||
self.ctrl[1].invertCheck.setChecked(state['yInverted'])
|
self.ctrl[1].invertCheck.setChecked(state['yInverted'])
|
||||||
self.valid = True
|
self.valid = True
|
||||||
|
|
||||||
|
def popup(self, *args):
|
||||||
|
if not self.valid:
|
||||||
|
self.updateState()
|
||||||
|
QtGui.QMenu.popup(self, *args)
|
||||||
|
|
||||||
def autoRange(self):
|
def autoRange(self):
|
||||||
self.view().autoRange() ## don't let signal call this directly--it'll add an unwanted argument
|
self.view().autoRange() ## don't let signal call this directly--it'll add an unwanted argument
|
||||||
|
Loading…
x
Reference in New Issue
Block a user