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:
Luke Campagnola 2012-04-04 09:29:35 -04:00
parent 33b09dfa23
commit 78d4bc0838
12 changed files with 120 additions and 21 deletions

View File

@ -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
}

View File

@ -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:

View File

@ -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()

View File

@ -467,7 +467,19 @@ 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):
TickSliderItem.mouseReleaseEvent(self, ev)

View File

@ -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,7 +83,13 @@ 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,21 +150,24 @@ class HistogramLUTItem(GraphicsWidget):
def gradientChanged(self):
if self.imageItem is not None:
self.imageItem.setLookupTable(self.getLookupTable) ## send function pointer, not the result
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
#if self.imageItem is not None:
#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()

View File

@ -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())

View File

@ -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

View File

@ -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,13 +201,23 @@ 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)
else:
self.currentBrush = self.brush
self.update()
#def hoverEnterEvent(self, ev):
#print "rgn hover enter"
#ev.ignore()

View File

@ -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

View File

@ -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):

View File

@ -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,11 +334,22 @@ 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():

View File

@ -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"""