- 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:
parent
debe847f9f
commit
ad7b5f0aad
27
Qt.py
27
Qt.py
@ -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
|
||||
|
22
__init__.py
22
__init__.py
@ -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):
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user