- Default foreground / background colors can now be set using pyqtgraph.setConfigOption()

- Added pyqtgraph.systemInfo() for bug reporting
- GraphicsLayout does a better job of avoiding occupied cells when using automatic placement
- Fixed sizing issues with LabelItem
- Updated GraphicsLayout example
This commit is contained in:
Luke Campagnola 2012-06-29 14:39:27 -04:00
parent debe847f9f
commit ad7b5f0aad
13 changed files with 306 additions and 130 deletions

27
Qt.py
View File

@ -1,15 +1,20 @@
## Do all Qt imports from here to allow easier PyQt / PySide compatibility
#from PySide import QtGui, QtCore, QtOpenGL, QtSvg
from PyQt4 import QtGui, QtCore
try:
from PyQt4 import QtSvg
except ImportError:
pass
try:
from PyQt4 import QtOpenGL
except ImportError:
pass
USE_PYSIDE = False ## If False, import PyQt4. If True, import PySide
if USE_PYSIDE:
from PySide import QtGui, QtCore, QtOpenGL, QtSvg
VERSION_INFO = 'PySide ' + PySide.__version__
else:
from PyQt4 import QtGui, QtCore
try:
from PyQt4 import QtSvg
except ImportError:
pass
try:
from PyQt4 import QtOpenGL
except ImportError:
pass
if not hasattr(QtCore, 'Signal'):
QtCore.Signal = QtCore.pyqtSignal
VERSION_INFO = 'PyQt4 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR

View File

@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
REVISION = '621'
### import all the goodies and add some helper functions for easy CLI use
## 'Qt' is a local module; it is intended mainly to cover up the differences
## between PyQt4 and PySide.
from .Qt import QtGui
from .Qt import QtGui
## not really safe--If we accidentally create another QApplication, the process hangs (and it is very difficult to trace the cause)
#if QtGui.QApplication.instance() is None:
@ -30,13 +32,15 @@ else:
useOpenGL = False ## on windows there's a more even performance / bugginess tradeoff.
CONFIG_OPTIONS = {
'useOpenGL': useOpenGL, ## 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
'foregroundColor': (200,200,200),
'backgroundColor': (0,0,0),
'foreground': (150, 150, 150), ## default foreground color for axes, labels, etc.
'background': (0, 0, 0), ## default background for GraphicsWidget
'antialias': False,
'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets
}
def setConfigOption(opt, value):
CONFIG_OPTIONS[opt] = value
@ -44,6 +48,16 @@ def getConfigOption(opt):
return CONFIG_OPTIONS[opt]
def systemInfo():
print "sys.platform:", sys.platform
print "sys.version:", sys.version
from .Qt import VERSION_INFO
print "qt bindings:", VERSION_INFO
print "pyqtgraph:", REVISION
print "config:"
import pprint
pprint.pprint(CONFIG_OPTIONS)
## Rename orphaned .pyc files. This is *probably* safe :)
def renamePyc(startDir):

View File

@ -4,41 +4,73 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import user
import numpy as np
app = QtGui.QApplication([])
view = pg.GraphicsView()
l = pg.GraphicsLayout(border=pg.mkPen(0, 0, 255))
l = pg.GraphicsLayout(border=(100,100,100))
view.setCentralItem(l)
view.show()
view.resize(800,600)
## Title at top
text = """
This example demonstrates the use of GraphicsLayout to arrange items in a grid.<br>
The items added to the layout must be subclasses of QGraphicsWidget (this includes <br>
PlotItem, ViewBox, LabelItem, and GrphicsLayout itself).
"""
l.addLabel(text, col=1, colspan=4)
l.nextRow()
## Put vertical label on left side
l.addLabel('Long Vertical Label', angle=-90, rowspan=3)
## Add 3 plots into the first row (automatic position)
p1 = l.addPlot()
p2 = l.addPlot()
p3 = l.addPlot()
p1 = l.addPlot(title="Plot 1")
p2 = l.addPlot(title="Plot 2")
vb = l.addViewBox(lockAspect=True)
img = pg.ImageItem(np.random.normal(size=(100,100)))
vb.addItem(img)
vb.autoRange()
## Add a viewbox into the second row (automatic position)
## Add a sub-layout into the second row (automatic position)
## The added item should avoid the first column, which is already filled
l.nextRow()
vb = l.addViewBox(colspan=3)
l2 = l.addLayout(colspan=3, border=(50,0,0))
l2.setContentsMargins(10, 10, 10, 10)
l2.addLabel("Sub-layout: this layout demonstrates the use of shared axes and axis labels", colspan=3)
l2.nextRow()
l2.addLabel('Vertical Axis Label', angle=-90, rowspan=2)
p21 = l2.addPlot()
p22 = l2.addPlot()
l2.nextRow()
p23 = l2.addPlot()
p24 = l2.addPlot()
l2.nextRow()
l2.addLabel("HorizontalAxisLabel", col=1, colspan=2)
## hide axes on some plots
p21.hideAxis('bottom')
p22.hideAxis('bottom')
p22.hideAxis('left')
p24.hideAxis('left')
p21.hideButtons()
p22.hideButtons()
p23.hideButtons()
p24.hideButtons()
## Add 2 more plots into the third row (manual position)
p4 = l.addPlot(row=2, col=0)
p5 = l.addPlot(row=2, col=1, colspan=2)
p4 = l.addPlot(row=3, col=1)
p5 = l.addPlot(row=3, col=2, colspan=2)
## show some content
## show some content in the plots
p1.plot([1,3,2,4,3,5])
p2.plot([1,3,2,4,3,5])
p3.plot([1,3,2,4,3,5])
p4.plot([1,3,2,4,3,5])
p5.plot([1,3,2,4,3,5])
b = QtGui.QGraphicsRectItem(0, 0, 1, 1)
b.setPen(pg.mkPen(255,255,0))
vb.addItem(b)
vb.setRange(QtCore.QRectF(-1, -1, 3, 3))
## Start Qt event loop unless running in interactive mode.

View File

@ -4,6 +4,7 @@ from pyqtgraph.Point import Point
import pyqtgraph.debug as debug
import weakref
import pyqtgraph.functions as fn
import pyqtgraph as pg
from .GraphicsWidget import GraphicsWidget
__all__ = ['AxisItem']
@ -65,8 +66,6 @@ class AxisItem(GraphicsWidget):
self.setRange(0, 1)
if pen is None:
pen = QtGui.QPen(QtGui.QColor(100, 100, 100))
self.setPen(pen)
self._linkedView = None
@ -189,8 +188,18 @@ class AxisItem(GraphicsWidget):
self.setMaximumWidth(w)
self.setMinimumWidth(w)
def pen(self):
if self._pen is None:
return fn.mkPen(pg.getConfigOption('foreground'))
return self._pen
def setPen(self, pen):
self.pen = pen
"""
Set the pen used for drawing text, axes, ticks, and grid lines.
if pen == None, the default will be used (see :func:`setConfigOption
<pyqtgraph.setConfigOption>`)
"""
self._pen = pen
self.picture = None
self.update()
@ -370,8 +379,6 @@ class AxisItem(GraphicsWidget):
"""
minVal, maxVal = sorted((minVal, maxVal))
if self.logMode:
return self.logTickValues(minVal, maxVal, size)
ticks = []
tickLevels = self.tickSpacing(minVal, maxVal, size)
@ -391,18 +398,33 @@ class AxisItem(GraphicsWidget):
values = filter(lambda x: all(np.abs(allValues-x) > spacing*0.01), values)
allValues = np.concatenate([allValues, values])
ticks.append((spacing, values))
if self.logMode:
return self.logTickValues(minVal, maxVal, size, ticks)
return ticks
def logTickValues(self, minVal, maxVal, size):
v1 = int(np.floor(minVal))
v2 = int(np.ceil(maxVal))
major = list(range(v1+1, v2))
def logTickValues(self, minVal, maxVal, size, stdTicks):
minor = []
for v in range(v1, v2):
minor.extend(v + np.log10(np.arange(1, 10)))
minor = [x for x in minor if x>minVal and x<maxVal]
return [(1.0, major), (None, minor)]
## start with the tick spacing given by tickValues().
## Any level whose spacing is < 1 needs to be converted to log scale
ticks = []
for (spacing, t) in stdTicks:
if spacing >= 1.0:
ticks.append((spacing, t))
if len(ticks) < 3:
v1 = int(np.floor(minVal))
v2 = int(np.ceil(maxVal))
#major = list(range(v1+1, v2))
minor = []
for v in range(v1, v2):
minor.extend(v + np.log10(np.arange(1, 10)))
minor = [x for x in minor if x>minVal and x<maxVal]
ticks.append((None, minor))
return ticks
def tickStrings(self, values, scale, spacing):
"""Return the strings that should be placed next to ticks. This method is called
@ -477,7 +499,7 @@ class AxisItem(GraphicsWidget):
#print tickStart, tickStop, span
## draw long line along axis
p.setPen(self.pen)
p.setPen(self.pen())
p.drawLine(*span)
p.translate(0.5,0) ## resolves some damn pixel ambiguity
@ -542,7 +564,11 @@ class AxisItem(GraphicsWidget):
p2[axis] = tickStop
if self.grid is False:
p2[axis] += tickLength*tickDir
p.setPen(QtGui.QPen(QtGui.QColor(150, 150, 150, lineAlpha)))
tickPen = self.pen()
color = tickPen.color()
color.setAlpha(lineAlpha)
tickPen.setColor(color)
p.setPen(tickPen)
p.drawLine(Point(p1), Point(p2))
tickPositions[i].append(x)
prof.mark('draw ticks')
@ -594,7 +620,7 @@ class AxisItem(GraphicsWidget):
textFlags = QtCore.Qt.AlignCenter|QtCore.Qt.AlignTop
rect = QtCore.QRectF(x-100, tickStop+max(0,self.tickLength), 200, height)
p.setPen(QtGui.QPen(QtGui.QColor(150, 150, 150)))
p.setPen(self.pen())
p.drawText(rect, textFlags, vstr)
prof.mark('draw text')
prof.finish()

View File

@ -175,7 +175,7 @@ class TickSliderItem(GraphicsWidget):
def resizeEvent(self, ev):
wlen = max(40, self.widgetLength())
self.setLength(wlen-self.tickSize)
self.setLength(wlen-self.tickSize-2)
self.setOrientation(self.orientation)
#bounds = self.scene().itemsBoundingRect()
#bounds.setLeft(min(-self.tickSize*0.5, bounds.left()))
@ -186,7 +186,7 @@ class TickSliderItem(GraphicsWidget):
def setLength(self, newLen):
#private
for t, x in list(self.ticks.items()):
t.setPos(x * newLen, t.pos().y())
t.setPos(x * newLen + 1, t.pos().y())
self.length = float(newLen)
#def mousePressEvent(self, ev):
@ -491,8 +491,8 @@ class GradientEditorItem(TickSliderItem):
def setLength(self, newLen):
#private (but maybe public)
TickSliderItem.setLength(self, newLen)
self.backgroundRect.setRect(0, -self.rectSize, newLen, self.rectSize)
self.gradRect.setRect(0, -self.rectSize, newLen, self.rectSize)
self.backgroundRect.setRect(1, -self.rectSize, newLen, self.rectSize)
self.gradRect.setRect(1, -self.rectSize, newLen, self.rectSize)
self.updateGradient()
def currentColorChanged(self, color):

View File

@ -21,21 +21,29 @@ class GraphicsLayout(GraphicsWidget):
self.border = border
self.layout = QtGui.QGraphicsGridLayout()
self.setLayout(self.layout)
self.items = {}
self.rows = {}
self.items = {} ## item: [(row, col), (row, col), ...] lists all cells occupied by the item
self.rows = {} ## row: {col1: item1, col2: item2, ...} maps cell location to item
self.currentRow = 0
self.currentCol = 0
self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding))
#def resizeEvent(self, ev):
#ret = GraphicsWidget.resizeEvent(self, ev)
#print self.pos(), self.mapToDevice(self.rect().topLeft())
#return ret
def nextRow(self):
"""Advance to next row for automatic item placement"""
self.currentRow += 1
self.currentCol = 0
self.currentCol = -1
self.nextColumn()
def nextColumn(self, colspan=1):
"""Advance to next column, while returning the current column number
def nextColumn(self):
"""Advance to next available column
(generally only for internal use--called by addItem)"""
self.currentCol += colspan
return self.currentCol-colspan
self.currentCol += 1
while self.getItem(self.currentRow, self.currentCol) is not None:
self.currentCol += 1
def nextCol(self, *args, **kargs):
"""Alias of nextColumn"""
@ -66,6 +74,8 @@ class GraphicsLayout(GraphicsWidget):
Create a LabelItem with *text* and place it in the next available cell (or in the cell specified)
All extra keyword arguments are passed to :func:`LabelItem.__init__ <pyqtgraph.LabelItem.__init__>`
Returns the created item.
To create a vertical label, use *angle*=-90
"""
text = LabelItem(text, **kargs)
self.addItem(text, row, col, rowspan, colspan)
@ -89,18 +99,24 @@ class GraphicsLayout(GraphicsWidget):
if row is None:
row = self.currentRow
if col is None:
col = self.nextCol(colspan)
col = self.currentCol
if row not in self.rows:
self.rows[row] = {}
self.rows[row][col] = item
self.items[item] = (row, col)
self.items[item] = []
for i in range(rowspan):
for j in range(colspan):
row2 = row + i
col2 = col + j
if row2 not in self.rows:
self.rows[row2] = {}
self.rows[row2][col2] = item
self.items[item].append((row2, col2))
self.layout.addItem(item, row, col, rowspan, colspan)
self.nextColumn()
def getItem(self, row, col):
"""Return the item in (*row*, *col*)"""
return self.row[row][col]
"""Return the item in (*row*, *col*). If the cell is empty, return None."""
return self.rows.get(row, {}).get(col, None)
def boundingRect(self):
return self.rect()
@ -124,9 +140,10 @@ class GraphicsLayout(GraphicsWidget):
ind = self.itemIndex(item)
self.layout.removeAt(ind)
self.scene().removeItem(item)
r,c = self.items[item]
for r,c in self.items[item]:
del self.rows[r][c]
del self.items[item]
del self.rows[r][c]
self.update()
def clear(self):

View File

@ -50,7 +50,7 @@ class HistogramLUTItem(GraphicsWidget):
self.layout.setSpacing(0)
self.vb = ViewBox()
self.vb.setMaximumWidth(152)
self.vb.setMinimumWidth(52)
self.vb.setMinimumWidth(45)
self.vb.setMouseEnabled(x=False, y=True)
self.gradient = GradientEditorItem()
self.gradient.setOrientation('right')

View File

@ -1,5 +1,6 @@
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph.functions as fn
import pyqtgraph as pg
from .GraphicsWidget import GraphicsWidget
@ -18,14 +19,13 @@ class LabelItem(GraphicsWidget):
GraphicsWidget.__init__(self, parent)
self.item = QtGui.QGraphicsTextItem(self)
self.opts = {
'color': 'CCC',
'color': None,
'justify': 'center'
}
self.opts.update(args)
self.sizeHint = {}
self._sizeHint = {}
self.setText(text)
self.setAngle(angle)
def setAttr(self, attr, value):
"""Set default text properties. See setText() for accepted parameters."""
@ -44,15 +44,17 @@ class LabelItem(GraphicsWidget):
==================== ==============================
"""
self.text = text
opts = self.opts.copy()
opts = self.opts
for k in args:
opts[k] = args[k]
optlist = []
if 'color' in opts:
if isinstance(opts['color'], QtGui.QColor):
opts['color'] = fn.colorStr(opts['color'])[:6]
optlist.append('color: #' + opts['color'])
color = self.opts['color']
if color is None:
color = pg.getConfigOption('foreground')
color = fn.mkColor(color)
optlist.append('color: #' + fn.colorStr(color)[:6])
if 'size' in opts:
optlist.append('font-size: ' + opts['size'])
if 'bold' in opts and opts['bold'] in [True, False]:
@ -64,7 +66,7 @@ class LabelItem(GraphicsWidget):
self.item.setHtml(full)
self.updateMin()
self.resizeEvent(None)
self.update()
self.updateGeometry()
def resizeEvent(self, ev):
#c1 = self.boundingRect().center()
@ -72,16 +74,35 @@ class LabelItem(GraphicsWidget):
#dif = c1 - c2
#self.item.moveBy(dif.x(), dif.y())
#print c1, c2, dif, self.item.pos()
self.item.setPos(0,0)
bounds = self.itemRect()
left = self.mapFromItem(self.item, QtCore.QPointF(0,0)) - self.mapFromItem(self.item, QtCore.QPointF(1,0))
rect = self.rect()
if self.opts['justify'] == 'left':
self.item.setPos(0,0)
if left.x() != 0:
bounds.moveLeft(rect.left())
if left.y() < 0:
bounds.moveTop(rect.top())
elif left.y() > 0:
bounds.moveBottom(rect.bottom())
elif self.opts['justify'] == 'center':
bounds = self.item.mapRectToParent(self.item.boundingRect())
self.item.setPos(self.width()/2. - bounds.width()/2., 0)
bounds.moveCenter(rect.center())
#bounds = self.itemRect()
#self.item.setPos(self.width()/2. - bounds.width()/2., 0)
elif self.opts['justify'] == 'right':
bounds = self.item.mapRectToParent(self.item.boundingRect())
self.item.setPos(self.width() - bounds.width(), 0)
#if self.width() > 0:
#self.item.setTextWidth(self.width())
if left.x() != 0:
bounds.moveRight(rect.right())
if left.y() < 0:
bounds.moveBottom(rect.bottom())
elif left.y() > 0:
bounds.moveTop(rect.top())
#bounds = self.itemRect()
#self.item.setPos(self.width() - bounds.width(), 0)
self.item.setPos(bounds.topLeft() - self.itemRect().topLeft())
self.updateMin()
def setAngle(self, angle):
self.angle = angle
@ -89,27 +110,31 @@ class LabelItem(GraphicsWidget):
self.item.rotate(angle)
self.updateMin()
def updateMin(self):
bounds = self.item.mapRectToParent(self.item.boundingRect())
bounds = self.itemRect()
self.setMinimumWidth(bounds.width())
self.setMinimumHeight(bounds.height())
self.sizeHint = {
self._sizeHint = {
QtCore.Qt.MinimumSize: (bounds.width(), bounds.height()),
QtCore.Qt.PreferredSize: (bounds.width(), bounds.height()),
QtCore.Qt.MaximumSize: (-1, -1), #bounds.width()*2, bounds.height()*2),
QtCore.Qt.MinimumDescent: (0, 0) ##?? what is this?
}
self.update()
self.updateGeometry()
def sizeHint(self, hint, constraint):
if hint not in self.sizeHint:
if hint not in self._sizeHint:
return QtCore.QSizeF(0, 0)
return QtCore.QSizeF(*self.sizeHint[hint])
return QtCore.QSizeF(*self._sizeHint[hint])
def itemRect(self):
return self.item.mapRectToParent(self.item.boundingRect())
#def paint(self, p, *args):
#p.setPen(fn.mkPen('r'))
#p.drawRect(self.rect())
#p.drawRect(self.item.boundingRect())
#p.setPen(fn.mkPen('g'))
#p.drawRect(self.itemRect())

View File

@ -105,6 +105,8 @@ class ViewBox(GraphicsWidget):
'mouseMode': ViewBox.PanMode if pyqtgraph.getConfigOption('leftButtonPan') else ViewBox.RectMode,
'enableMenu': enableMenu,
'wheelScaleFactor': -1.0 / 8.0,
'background': None,
}
@ -118,11 +120,17 @@ class ViewBox(GraphicsWidget):
self.setFlag(self.ItemIsFocusable, True) ## so we can receive key presses
## childGroup is required so that ViewBox has local coordinates similar to device coordinates.
## this is a workaround for a Qt + OpenGL but that causes improper clipping
## this is a workaround for a Qt + OpenGL bug that causes improper clipping
## https://bugreports.qt.nokia.com/browse/QTBUG-23723
self.childGroup = ChildGroup(self)
self.childGroup.sigItemsChanged.connect(self.itemsChanged)
self.background = QtGui.QGraphicsRectItem(self.rect())
self.background.setParentItem(self)
self.background.setZValue(-1e6)
self.background.setPen(fn.mkPen(None))
self.updateBackground()
#self.useLeftButtonPan = pyqtgraph.getConfigOption('leftButtonPan') # normally use left button to pan
# this also enables capture of keyPressEvents.
@ -286,6 +294,7 @@ class ViewBox(GraphicsWidget):
#self.updateAutoRange()
self.updateMatrix()
self.sigStateChanged.emit(self)
self.background.setRect(self.rect())
#self.linkedXChanged()
#self.linkedYChanged()
@ -1155,6 +1164,16 @@ class ViewBox(GraphicsWidget):
#self.scene().render(p)
#p.end()
def updateBackground(self):
bg = self.state['background']
#print self, bg
if bg is None:
self.background.hide()
else:
self.background.show()
self.background.setBrush(fn.mkBrush(bg))
def updateViewLists(self):
def cmpViews(a, b):
wins = 100 * cmp(a.window() is self.window(), b.window() is self.window())

View File

@ -15,7 +15,7 @@ class GradientWidget(GraphicsView):
def __init__(self, parent=None, orientation='bottom', *args, **kargs):
GraphicsView.__init__(self, parent, useOpenGL=False, background=None)
self.maxDim = 27
self.maxDim = 31
kargs['tickPen'] = 'k'
self.item = GradientEditorItem(*args, **kargs)
self.item.sigGradientChanged.connect(self.sigGradientChanged)
@ -24,7 +24,7 @@ class GradientWidget(GraphicsView):
self.setCacheMode(self.CacheNone)
self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing)
self.setFrameStyle(QtGui.QFrame.NoFrame | QtGui.QFrame.Plain)
self.setBackgroundRole(QtGui.QPalette.NoRole)
#self.setBackgroundRole(QtGui.QPalette.NoRole)
#self.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
#self.setAutoFillBackground(False)
#self.setAttribute(QtCore.Qt.WA_PaintOnScreen, False)

View File

@ -6,6 +6,7 @@ Distributed under MIT/X11 license. See license.txt for more infomation.
"""
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
try:
from pyqtgraph.Qt import QtOpenGL
@ -13,12 +14,8 @@ try:
except ImportError:
HAVE_OPENGL = False
#from numpy import vstack
#import time
from pyqtgraph.Point import Point
#from vector import *
import sys, os
#import debug
from .FileDialog import FileDialog
from pyqtgraph.GraphicsScene import GraphicsScene
import numpy as np
@ -29,6 +26,20 @@ import pyqtgraph
__all__ = ['GraphicsView']
class GraphicsView(QtGui.QGraphicsView):
"""Re-implementation of QGraphicsView that removes scrollbars and allows unambiguous control of the
viewed coordinate range. Also automatically creates a GraphicsScene and a central QGraphicsWidget
that is automatically scaled to the full view geometry.
This widget is the basis for :class:`PlotWidget <pyqtgraph.PlotWidget>`,
:class:`GraphicsLayoutWidget <pyqtgraph.GraphicsLayoutWidget>`, and the view widget in
:class:`ImageView <pyqtgraph.ImageView>`.
By default, the view coordinate system matches the widget's pixel coordinates and
automatically updates when the view is resized. This can be overridden by setting
autoPixelRange=False. The exact visible range can be set with setRange().
The view can be panned using the middle mouse button and scaled using the right mouse button if
enabled via enableMouse() (but ordinarily, we use ViewBox for this functionality)."""
sigRangeChanged = QtCore.Signal(object, object)
sigMouseReleased = QtCore.Signal(object)
@ -37,17 +48,25 @@ class GraphicsView(QtGui.QGraphicsView):
sigScaleChanged = QtCore.Signal(object)
lastFileDir = None
def __init__(self, parent=None, useOpenGL=None, background='k'):
"""Re-implementation of QGraphicsView that removes scrollbars and allows unambiguous control of the
viewed coordinate range. Also automatically creates a QGraphicsScene and a central QGraphicsWidget
that is automatically scaled to the full view geometry.
def __init__(self, parent=None, useOpenGL=None, background='default'):
"""
============ ============================================================
Arguments:
parent Optional parent widget
useOpenGL If True, the GraphicsView will use OpenGL to do all of its
rendering. This can improve performance on some systems,
but may also introduce bugs (the combination of
QGraphicsView and QGLWidget is still an 'experimental'
feature of Qt)
background Set the background color of the GraphicsView. Accepts any
single argument accepted by
:func:`mkColor <pyqtgraph.mkColor>`. By
default, the background color is determined using the
'backgroundColor' configuration option (see
:func:`setConfigOption <pyqtgraph.setConfigOption>`.
============ ============================================================
"""
By default, the view coordinate system matches the widget's pixel coordinates and
automatically updates when the view is resized. This can be overridden by setting
autoPixelRange=False. The exact visible range can be set with setRange().
The view can be panned using the middle mouse button and scaled using the right mouse button if
enabled via enableMouse() (but ordinarily, we use ViewBox for this functionality)."""
self.closed = False
QtGui.QGraphicsView.__init__(self, parent)
@ -62,9 +81,7 @@ class GraphicsView(QtGui.QGraphicsView):
## This might help, but it's probably dangerous in the general case..
#self.setOptimizationFlag(self.DontSavePainterState, True)
if background is not None:
brush = fn.mkBrush(background)
self.setBackgroundBrush(brush)
self.setBackground(background)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.setFrameShape(QtGui.QFrame.NoFrame)
@ -98,7 +115,22 @@ class GraphicsView(QtGui.QGraphicsView):
self.scaleCenter = False ## should scaling center around view center (True) or mouse click (False)
self.clickAccepted = False
def setBackground(self, background):
"""
Set the background color of the GraphicsView.
To use the defaults specified py pyqtgraph.setConfigOption, use background='default'.
To make the background transparent, use background=None.
"""
self._background = background
if background == 'default':
background = pyqtgraph.getConfigOption('background')
if background is None:
self.setBackgroundRole(QtGui.QPalette.NoRole)
else:
brush = fn.mkBrush(background)
self.setBackgroundBrush(brush)
def close(self):
self.centralWidget = None
self.scene().clear()
@ -126,7 +158,8 @@ class GraphicsView(QtGui.QGraphicsView):
return self.setCentralWidget(item)
def setCentralWidget(self, item):
"""Sets a QGraphicsWidget to automatically fill the entire view."""
"""Sets a QGraphicsWidget to automatically fill the entire view (the item will be automatically
resize whenever the GraphicsView is resized)."""
if self.centralWidget is not None:
self.scene().removeItem(self.centralWidget)
self.centralWidget = item
@ -152,15 +185,18 @@ class GraphicsView(QtGui.QGraphicsView):
return
if self.autoPixelRange:
self.range = QtCore.QRectF(0, 0, self.size().width(), self.size().height())
GraphicsView.setRange(self, self.range, padding=0, disableAutoPixel=False)
GraphicsView.setRange(self, self.range, padding=0, disableAutoPixel=False) ## we do this because some subclasses like to redefine setRange in an incompatible way.
self.updateMatrix()
def updateMatrix(self, propagate=True):
self.setSceneRect(self.range)
if self.aspectLocked:
self.fitInView(self.range, QtCore.Qt.KeepAspectRatio)
if self.autoPixelRange:
self.resetTransform()
else:
self.fitInView(self.range, QtCore.Qt.IgnoreAspectRatio)
if self.aspectLocked:
self.fitInView(self.range, QtCore.Qt.KeepAspectRatio)
else:
self.fitInView(self.range, QtCore.Qt.IgnoreAspectRatio)
self.sigRangeChanged.emit(self, self.range)

View File

@ -18,7 +18,7 @@ class HistogramLUTWidget(GraphicsView):
self.item = HistogramLUTItem(*args, **kargs)
self.setCentralItem(self.item)
self.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
self.setMinimumWidth(92)
self.setMinimumWidth(95)
def sizeHint(self):

View File

@ -1,6 +1,7 @@
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph.multiprocess as mp
import pyqtgraph as pg
from .GraphicsView import GraphicsView
import numpy as np
import mmap, tempfile, ctypes, atexit
@ -20,6 +21,7 @@ class RemoteGraphicsView(QtGui.QWidget):
QtGui.QWidget.__init__(self)
self._proc = mp.QtProcess()
self.pg = self._proc._import('pyqtgraph')
self.pg.setConfigOptions(self.pg.CONFIG_OPTIONS)
rpgRemote = self._proc._import('pyqtgraph.widgets.RemoteGraphicsView')
self._view = rpgRemote.Renderer(*args, **kwds)
self._view._setProxyOptions(deferGetattr=True)
@ -82,7 +84,7 @@ class RemoteGraphicsView(QtGui.QWidget):
class Renderer(pg.GraphicsView):
class Renderer(GraphicsView):
sceneRendered = QtCore.Signal(object)
@ -97,7 +99,7 @@ class Renderer(pg.GraphicsView):
self.shm = mmap.mmap(fd, mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_WRITE)
atexit.register(self.close)
pg.GraphicsView.__init__(self, *args, **kwds)
GraphicsView.__init__(self, *args, **kwds)
self.scene().changed.connect(self.update)
self.img = None
self.renderTimer = QtCore.QTimer()
@ -113,11 +115,11 @@ class Renderer(pg.GraphicsView):
def update(self):
self.img = None
return pg.GraphicsView.update(self)
return GraphicsView.update(self)
def resize(self, size):
oldSize = self.size()
pg.GraphicsView.resize(self, size)
GraphicsView.resize(self, size)
self.resizeEvent(QtGui.QResizeEvent(size, oldSize))
self.update()
@ -141,29 +143,29 @@ class Renderer(pg.GraphicsView):
typ = QtCore.QEvent.Type(typ)
btns = QtCore.Qt.MouseButtons(btns)
mods = QtCore.Qt.KeyboardModifiers(mods)
return pg.GraphicsView.mousePressEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
return GraphicsView.mousePressEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
def mouseMoveEvent(self, typ, pos, gpos, btn, btns, mods):
typ = QtCore.QEvent.Type(typ)
btns = QtCore.Qt.MouseButtons(btns)
mods = QtCore.Qt.KeyboardModifiers(mods)
return pg.GraphicsView.mouseMoveEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
return GraphicsView.mouseMoveEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
def mouseReleaseEvent(self, typ, pos, gpos, btn, btns, mods):
typ = QtCore.QEvent.Type(typ)
btns = QtCore.Qt.MouseButtons(btns)
mods = QtCore.Qt.KeyboardModifiers(mods)
return pg.GraphicsView.mouseReleaseEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
return GraphicsView.mouseReleaseEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
def wheelEvent(self, pos, gpos, d, btns, mods, ori):
btns = QtCore.Qt.MouseButtons(btns)
mods = QtCore.Qt.KeyboardModifiers(mods)
return pg.GraphicsView.wheelEvent(self, QtGui.QWheelEvent(pos, gpos, d, btns, mods, ori))
return GraphicsView.wheelEvent(self, QtGui.QWheelEvent(pos, gpos, d, btns, mods, ori))
def keyEvent(self, typ, mods, text, autorep, count):
typ = QtCore.QEvent.Type(typ)
mods = QtCore.Qt.KeyboardModifiers(mods)
pg.GraphicsView.keyEvent(self, QtGui.QKeyEvent(typ, mods, text, autorep, count))
GraphicsView.keyEvent(self, QtGui.QKeyEvent(typ, mods, text, autorep, count))
return ev.accepted()