Performance enhancements
- HistogramLUTItem avoids using lookup table if possible - GradientEditorItem has a method to ask whether the gradient is trivial (can be applied without the use of a lookup table) - ROI, LinearRegionItem, InfiniteLine no longer redraw for every mouse movement
This commit is contained in:
parent
33b09dfa23
commit
78d4bc0838
@ -18,10 +18,10 @@ if 'linux' in sys.platform: ## linux has numerous bugs in opengl implementation
|
||||
elif 'darwin' in sys.platform: ## openGL greatly speeds up display on mac
|
||||
useOpenGL = True
|
||||
else:
|
||||
useOpenGL = True ## on windows there's a more even performance / bugginess tradeoff.
|
||||
useOpenGL = False ## on windows there's a more even performance / bugginess tradeoff.
|
||||
|
||||
CONFIG_OPTIONS = {
|
||||
'useOpenGL': None, ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
|
||||
'useOpenGL': useOpenGL, ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
|
||||
'leftButtonPan': True ## if false, left button drags a rubber band for zooming in viewbox
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
## Add path to library (just for examples; you do not need this)
|
||||
import sys, os, time
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
|
||||
from pyqtgraph.Qt import QtGui, QtCore
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
from pyqtgraph.ptime import time
|
||||
#QtGui.QApplication.setGraphicsSystem('raster')
|
||||
app = QtGui.QApplication([])
|
||||
#mw = QtGui.QMainWindow()
|
||||
@ -18,15 +18,22 @@ p = pg.plot()
|
||||
p.setRange(QtCore.QRectF(0, -10, 5000, 20))
|
||||
p.setLabel('bottom', 'Index', units='B')
|
||||
curve = p.plot()
|
||||
|
||||
#curve.setFillBrush((0, 0, 100, 100))
|
||||
#curve.setFillLevel(0)
|
||||
|
||||
#lr = pg.LinearRegionItem([100, 4900])
|
||||
#p.addItem(lr)
|
||||
|
||||
data = np.random.normal(size=(50,5000))
|
||||
ptr = 0
|
||||
lastTime = time.time()
|
||||
lastTime = time()
|
||||
fps = None
|
||||
def update():
|
||||
global curve, data, ptr, p, lastTime, fps
|
||||
curve.setData(data[ptr%10])
|
||||
ptr += 1
|
||||
now = time.time()
|
||||
now = time()
|
||||
dt = now - lastTime
|
||||
lastTime = now
|
||||
if fps is None:
|
||||
|
@ -25,6 +25,8 @@ win.show()
|
||||
ui.maxSpin1.setOpts(value=255, step=1)
|
||||
ui.minSpin1.setOpts(value=0, step=1)
|
||||
|
||||
#ui.graphicsView.useOpenGL() ## buggy, but you can try it if you need extra speed.
|
||||
|
||||
vb = pg.ViewBox()
|
||||
ui.graphicsView.setCentralItem(vb)
|
||||
vb.setAspectLocked()
|
||||
|
@ -467,6 +467,18 @@ class GradientEditorItem(TickSliderItem):
|
||||
|
||||
return table
|
||||
|
||||
def isLookupTrivial(self):
|
||||
"""Return true if the gradient has exactly two stops in it: black at 0.0 and white at 1.0"""
|
||||
ticks = self.listTicks()
|
||||
if len(ticks) != 2:
|
||||
return False
|
||||
if ticks[0][1] != 0.0 or ticks[1][1] != 1.0:
|
||||
return False
|
||||
c1 = fn.colorTuple(ticks[0][0].color)
|
||||
c2 = fn.colorTuple(ticks[1][0].color)
|
||||
if c1 != (0,0,0,255) or c2 != (255,255,255,255):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def mouseReleaseEvent(self, ev):
|
||||
|
@ -15,17 +15,30 @@ from GridItem import *
|
||||
from pyqtgraph.Point import Point
|
||||
import pyqtgraph.functions as fn
|
||||
import numpy as np
|
||||
import pyqtgraph.debug as debug
|
||||
|
||||
|
||||
__all__ = ['HistogramLUTItem']
|
||||
|
||||
|
||||
class HistogramLUTItem(GraphicsWidget):
|
||||
"""
|
||||
This is a graphicsWidget which provides controls for adjusting the display of an image.
|
||||
Includes:
|
||||
- Image histogram
|
||||
- Movable region over histogram to select black/white levels
|
||||
- Gradient editor to define color lookup table for single-channel images
|
||||
"""
|
||||
|
||||
sigLookupTableChanged = QtCore.Signal(object)
|
||||
sigLevelsChanged = QtCore.Signal(object)
|
||||
sigLevelChangeFinished = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, image=None):
|
||||
def __init__(self, image=None, fillHistogram=True):
|
||||
"""
|
||||
If *image* (ImageItem) is provided, then the control will be automatically linked to the image and changes to the control will be immediately reflected in the image's appearance.
|
||||
By default, the histogram is rendered with a fill. For performance, set *fillHistogram* = False.
|
||||
"""
|
||||
GraphicsWidget.__init__(self)
|
||||
self.lut = None
|
||||
self.imageItem = None
|
||||
@ -61,6 +74,8 @@ class HistogramLUTItem(GraphicsWidget):
|
||||
self.vb.sigRangeChanged.connect(self.viewRangeChanged)
|
||||
self.plot = PlotDataItem()
|
||||
self.plot.rotate(90)
|
||||
self.fillHistogram(fillHistogram)
|
||||
|
||||
self.vb.addItem(self.plot)
|
||||
self.autoHistogramRange()
|
||||
|
||||
@ -68,6 +83,12 @@ class HistogramLUTItem(GraphicsWidget):
|
||||
self.setImageItem(image)
|
||||
#self.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
|
||||
|
||||
def fillHistogram(self, fill=True, level=0.0, color=(100, 100, 200)):
|
||||
if fill:
|
||||
self.plot.setFillLevel(level)
|
||||
self.plot.setFillBrush(color)
|
||||
else:
|
||||
self.plot.setFillLevel(None)
|
||||
|
||||
#def sizeHint(self, *args):
|
||||
#return QtCore.QSizeF(115, 200)
|
||||
@ -129,6 +150,9 @@ class HistogramLUTItem(GraphicsWidget):
|
||||
|
||||
def gradientChanged(self):
|
||||
if self.imageItem is not None:
|
||||
if self.gradient.isLookupTrivial():
|
||||
self.imageItem.setLookupTable(None) #lambda x: x.astype(np.uint8))
|
||||
else:
|
||||
self.imageItem.setLookupTable(self.getLookupTable) ## send function pointer, not the result
|
||||
|
||||
self.lut = None
|
||||
@ -136,14 +160,14 @@ class HistogramLUTItem(GraphicsWidget):
|
||||
#self.imageItem.setLookupTable(self.gradient.getLookupTable(512))
|
||||
self.sigLookupTableChanged.emit(self)
|
||||
|
||||
def getLookupTable(self, img=None, n=None):
|
||||
def getLookupTable(self, img=None, n=None, alpha=False):
|
||||
if n is None:
|
||||
if img.dtype == np.uint8:
|
||||
n = 256
|
||||
else:
|
||||
n = 512
|
||||
if self.lut is None:
|
||||
self.lut = self.gradient.getLookupTable(n)
|
||||
self.lut = self.gradient.getLookupTable(n, alpha=alpha)
|
||||
return self.lut
|
||||
|
||||
def regionChanged(self):
|
||||
@ -159,17 +183,19 @@ class HistogramLUTItem(GraphicsWidget):
|
||||
self.update()
|
||||
|
||||
def imageChanged(self, autoLevel=False, autoRange=False):
|
||||
prof = debug.Profiler('HistogramLUTItem.imageChanged', disabled=True)
|
||||
h = self.imageItem.getHistogram()
|
||||
prof.mark('get histogram')
|
||||
if h[0] is None:
|
||||
return
|
||||
self.plot.setData(*h, fillLevel=0.0, brush=(100, 100, 200))
|
||||
self.plot.setData(*h)
|
||||
prof.mark('set plot')
|
||||
if autoLevel:
|
||||
mn = h[0][0]
|
||||
mx = h[0][-1]
|
||||
self.region.setRegion([mn, mx])
|
||||
#self.updateRange()
|
||||
#if autoRange:
|
||||
#self.updateRange()
|
||||
prof.mark('set region')
|
||||
prof.finish()
|
||||
|
||||
def getLevels(self):
|
||||
return self.region.getRegion()
|
||||
|
@ -224,10 +224,13 @@ class ImageItem(GraphicsObject):
|
||||
return
|
||||
if self.qimage is None:
|
||||
self.render()
|
||||
prof.mark('render QImage')
|
||||
if self.paintMode is not None:
|
||||
p.setCompositionMode(self.paintMode)
|
||||
prof.mark('set comp mode')
|
||||
|
||||
p.drawImage(QtCore.QPointF(0,0), self.qimage)
|
||||
prof.mark('p.drawImage')
|
||||
if self.border is not None:
|
||||
p.setPen(self.border)
|
||||
p.drawRect(self.boundingRect())
|
||||
|
@ -35,6 +35,7 @@ class InfiniteLine(UIGraphicsItem):
|
||||
self.maxRange = bounds
|
||||
self.moving = False
|
||||
self.setMovable(movable)
|
||||
self.mouseHovering = False
|
||||
self.p = [0, 0]
|
||||
self.setAngle(angle)
|
||||
if pos is None:
|
||||
@ -222,6 +223,16 @@ class InfiniteLine(UIGraphicsItem):
|
||||
|
||||
def hoverEvent(self, ev):
|
||||
if (not ev.isExit()) and self.movable and ev.acceptDrags(QtCore.Qt.LeftButton):
|
||||
self.setMouseHover(True)
|
||||
else:
|
||||
self.setMouseHover(False)
|
||||
|
||||
def setMouseHover(self, hover):
|
||||
## Inform the item that the mouse is(not) hovering over it
|
||||
if self.mouseHovering == hover:
|
||||
return
|
||||
self.mouseHovering = hover
|
||||
if hover:
|
||||
self.currentPen = fn.mkPen(255, 0,0)
|
||||
else:
|
||||
self.currentPen = self.pen
|
||||
|
@ -2,6 +2,7 @@ from pyqtgraph.Qt import QtGui, QtCore
|
||||
from UIGraphicsItem import UIGraphicsItem
|
||||
from InfiniteLine import InfiniteLine
|
||||
import pyqtgraph.functions as fn
|
||||
import pyqtgraph.debug as debug
|
||||
|
||||
__all__ = ['LinearRegionItem']
|
||||
|
||||
@ -24,6 +25,7 @@ class LinearRegionItem(UIGraphicsItem):
|
||||
self.bounds = QtCore.QRectF()
|
||||
self.blockLineSignal = False
|
||||
self.moving = False
|
||||
self.mouseHovering = False
|
||||
|
||||
if orientation == LinearRegionItem.Horizontal:
|
||||
self.lines = [
|
||||
@ -94,9 +96,11 @@ class LinearRegionItem(UIGraphicsItem):
|
||||
return br.normalized()
|
||||
|
||||
def paint(self, p, *args):
|
||||
#prof = debug.Profiler('LinearRegionItem.paint')
|
||||
UIGraphicsItem.paint(self, p, *args)
|
||||
p.setBrush(self.currentBrush)
|
||||
p.drawRect(self.boundingRect())
|
||||
#prof.finish()
|
||||
|
||||
def dataBounds(self, axis, frac=1.0):
|
||||
if axis == self.orientation:
|
||||
@ -197,6 +201,16 @@ class LinearRegionItem(UIGraphicsItem):
|
||||
|
||||
def hoverEvent(self, ev):
|
||||
if (not ev.isExit()) and ev.acceptDrags(QtCore.Qt.LeftButton):
|
||||
self.setMouseHover(True)
|
||||
else:
|
||||
self.setMouseHover(False)
|
||||
|
||||
def setMouseHover(self, hover):
|
||||
## Inform the item that the mouse is(not) hovering over it
|
||||
if self.mouseHovering == hover:
|
||||
return
|
||||
self.mouseHovering = hover
|
||||
if hover:
|
||||
c = self.brush.color()
|
||||
c.setAlpha(c.alpha() * 2)
|
||||
self.currentBrush = fn.mkBrush(c)
|
||||
|
@ -352,7 +352,9 @@ class PlotCurveItem(GraphicsObject):
|
||||
p2.closeSubpath()
|
||||
self.fillPath = p2
|
||||
|
||||
prof.mark('generate fill path')
|
||||
p.fillPath(self.fillPath, self.opts['brush'])
|
||||
prof.mark('draw fill path')
|
||||
|
||||
|
||||
## Copy pens and apply alpha adjustment
|
||||
|
@ -11,6 +11,7 @@ from ScatterPlotItem import ScatterPlotItem
|
||||
import numpy as np
|
||||
import scipy
|
||||
import pyqtgraph.functions as fn
|
||||
import pyqtgraph.debug as debug
|
||||
|
||||
class PlotDataItem(GraphicsObject):
|
||||
"""GraphicsItem for displaying plot curves, scatter plots, or both."""
|
||||
@ -215,7 +216,7 @@ class PlotDataItem(GraphicsObject):
|
||||
"""
|
||||
|
||||
#self.clear()
|
||||
|
||||
prof = debug.Profiler('PlotDataItem.setData (0x%x)' % id(self), disabled=True)
|
||||
y = None
|
||||
x = None
|
||||
if len(args) == 1:
|
||||
@ -262,7 +263,7 @@ class PlotDataItem(GraphicsObject):
|
||||
if 'y' in kargs:
|
||||
y = kargs['y']
|
||||
|
||||
|
||||
prof.mark('interpret data')
|
||||
## pull in all style arguments.
|
||||
## Use self.opts to fill in anything not present in kargs.
|
||||
|
||||
@ -305,12 +306,16 @@ class PlotDataItem(GraphicsObject):
|
||||
self.yData = y.view(np.ndarray)
|
||||
self.xDisp = None
|
||||
self.yDisp = None
|
||||
prof.mark('set data')
|
||||
|
||||
self.updateItems()
|
||||
prof.mark('update items')
|
||||
view = self.getViewBox()
|
||||
if view is not None:
|
||||
view.itemBoundsChanged(self) ## inform view so it can update its range if it wants
|
||||
self.sigPlotChanged.emit(self)
|
||||
prof.mark('emit')
|
||||
prof.finish()
|
||||
|
||||
|
||||
def updateItems(self):
|
||||
|
@ -55,7 +55,7 @@ class ROI(GraphicsObject):
|
||||
self.rotateAllowed = True
|
||||
|
||||
self.freeHandleMoved = False ## keep track of whether free handles have moved since last change signal was emitted.
|
||||
|
||||
self.mouseHovering = False
|
||||
if pen is None:
|
||||
pen = (255, 255, 255)
|
||||
self.setPen(pen)
|
||||
@ -334,12 +334,23 @@ class ROI(GraphicsObject):
|
||||
|
||||
def hoverEvent(self, ev):
|
||||
if self.translatable and (not ev.isExit()) and ev.acceptDrags(QtCore.Qt.LeftButton):
|
||||
self.currentPen = fn.mkPen(255, 255, 0)
|
||||
self.setMouseHover(True)
|
||||
self.sigHoverEvent.emit(self)
|
||||
else:
|
||||
self.setMouseHover(False)
|
||||
|
||||
def setMouseHover(self, hover):
|
||||
## Inform the ROI that the mouse is(not) hovering over it
|
||||
if self.mouseHovering == hover:
|
||||
return
|
||||
self.mouseHovering = hover
|
||||
if hover:
|
||||
self.currentPen = fn.mkPen(255, 255, 0)
|
||||
else:
|
||||
self.currentPen = self.pen
|
||||
self.update()
|
||||
|
||||
|
||||
def mouseDragEvent(self, ev):
|
||||
if ev.isStart():
|
||||
#p = ev.pos()
|
||||
|
@ -16,6 +16,7 @@ from FileDialog import FileDialog
|
||||
from pyqtgraph.GraphicsScene import GraphicsScene
|
||||
import numpy as np
|
||||
import pyqtgraph.functions as fn
|
||||
import pyqtgraph.debug as debug
|
||||
import pyqtgraph
|
||||
|
||||
__all__ = ['GraphicsView']
|
||||
@ -395,6 +396,11 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
#self.pev = pev
|
||||
#self.currentItem.mouseMoveEvent(pev)
|
||||
|
||||
#def paintEvent(self, ev):
|
||||
#prof = debug.Profiler('GraphicsView.paintEvent (0x%x)' % id(self))
|
||||
#QtGui.QGraphicsView.paintEvent(self, ev)
|
||||
#prof.finish()
|
||||
|
||||
|
||||
def pixelSize(self):
|
||||
"""Return vector with the length and width of one view pixel in scene coordinates"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user