Skip to content

Commit

Permalink
Introduce API option to control whether lines are drawn as segmented …
Browse files Browse the repository at this point in the history
…lines (pyqtgraph#2185)

* Introduce API option to control whether lines are drawn as segmented lines

- Adds a global options to set mode for drawing segmented lines
- Mode can be 'auto' (use existing method),  'on' (always draw segmented lines), 'off' (never draw segmented lines)
- Global option can be overridden for given ``PlotCurveItem`` instance using new setter method
- **This is a proposal!** Added activation of anti-aliasing as criterion to decide whether to draw segmented lines in 'auto' mode

* Added segmented line mode as option to `examples/PlotSpeedTest.py`

Included 'segmentedLineMode' as argument to `setData` method in `PlotCurveItem.py`

* Change of segmented line mode in `PlotSpeedTest.py` implemented analogue to pen options

* Removed option to set segmentedLineMode through setData keyword

Signed-off-by: Sietze van Buuren <[email protected]>

* Segmented line mode option now also accepts booleans `True` (same as `'on'`) and `False` (same as `'off'`) as possible modes

* Removed `True`/`False` option possibilities for segmentedLineMode in `PlotCurveItem.py`
Added documentation to `config_options.rst` for global option segmentedLineMode
  • Loading branch information
swvanbuuren authored May 5, 2022
1 parent e0b3a30 commit 1dd6b9d
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 3 deletions.
4 changes: 4 additions & 0 deletions doc/source/config_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ enableExperimental bool False Enable experimental fe
* Only a very limited subset of the full options of PlotCurveItem is implemented.
* Single precision is used. This may cause drawing artifacts.
crashWarning bool False If True, print warnings about situations that may result in a crash.
segmentedLineMode str 'auto' For 'on', lines are always plotted in segments. For 'off', lines are never
plotted in segments. For 'auto', whether lines are plotted in segments is
automatically decided based on pen poperties and whether anti-aliasing is
enabled.
================== =================== ================== ================================================================================


Expand Down
8 changes: 7 additions & 1 deletion pyqtgraph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,20 @@
# change in the future.
'useCupy': False, # When True, attempt to use cupy ( currently only with ImageItem and related functions )
'useNumba': False, # When True, use numba
}
'segmentedLineMode': 'auto', # segmented line mode, controls if lines are plotted in segments or continuous
# 'auto': whether lines are plotted in segments is automatically decided using pen properties and whether anti-aliasing is enabled
# 'on' or True: lines are always plotted in segments
# 'off' or False: lines are never plotted in segments
}


def setConfigOption(opt, value):
if opt not in CONFIG_OPTIONS:
raise KeyError('Unknown configuration option "%s"' % opt)
if opt == 'imageAxisOrder' and value not in ('row-major', 'col-major'):
raise ValueError('imageAxisOrder must be either "row-major" or "col-major"')
if opt == 'segmentedLineMode' and value not in ('auto', 'on', 'off'):
raise ValueError('segmentedLineMode must be "auto", "on" or "off"')
CONFIG_OPTIONS[opt] = value

def setConfigOptions(**opts):
Expand Down
7 changes: 6 additions & 1 deletion pyqtgraph/examples/PlotSpeedTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ def paint(self, painter, opt, widget):
dict(name='connect', type='list', limits=['all', 'pairs', 'finite', 'array'], value='all'),
dict(name='fill', type='bool', value=False),
dict(name='skipFiniteCheck', type='bool', value=False),
dict(name='plotMethod', title='Plot Method', type='list', limits=['pyqtgraph', 'drawPolyline'])
dict(name='plotMethod', title='Plot Method', type='list', limits=['pyqtgraph', 'drawPolyline']),
dict(name='segmentedLineMode', title='Segmented lines', type='list', limits=['auto', 'on', 'off'], value='auto'),
]

params = ptree.Parameter.create(name='Parameters', type='group', children=children)
Expand Down Expand Up @@ -131,12 +132,16 @@ def onPenChanged(param, pen):
def onFillChanged(param, enable):
curve.setFillLevel(0.0 if enable else None)

def onSegmentedLineModeChanged(param, mode):
curve.setSegmentedLineMode(mode)

params.child('sigopts').sigTreeStateChanged.connect(makeData)
params.child('useOpenGL').sigValueChanged.connect(onUseOpenGLChanged)
params.child('enableExperimental').sigValueChanged.connect(onEnableExperimentalChanged)
params.child('pen').sigValueChanged.connect(onPenChanged)
params.child('fill').sigValueChanged.connect(onFillChanged)
params.child('plotMethod').sigValueChanged.connect(curve.setMethod)
params.child('segmentedLineMode').sigValueChanged.connect(onSegmentedLineModeChanged)
params.sigTreeStateChanged.connect(resetTimings)

makeData()
Expand Down
34 changes: 33 additions & 1 deletion pyqtgraph/graphicsItems/PlotCurveItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ def __init__(self, *args, **kargs):
'connect': 'all',
'mouseWidth': 8, # width of shape responding to mouse click
'compositionMode': None,
'skipFiniteCheck': False
'skipFiniteCheck': False,
'segmentedLineMode': getConfigOption('segmentedLineMode'),
}
if 'pen' not in kargs:
self.opts['pen'] = fn.mkPen('w')
Expand Down Expand Up @@ -624,7 +625,35 @@ def getPath(self):

return self.path

def setSegmentedLineMode(self, mode):
"""
Sets the mode that decides whether or not lines are drawn as segmented lines. Drawing lines
as segmented lines is more performant than the standard drawing method with continuous
lines.
Parameters
----------
mode : str
``'auto'`` (default) segmented lines are drawn if the pen's width > 1, pen style is a
solid line, the pen color is opaque and anti-aliasing is not enabled.
``'on'`` lines are always drawn as segmented lines
``'off'`` lines are never drawn as segmented lines, i.e. the drawing
method with continuous lines is used
"""
if mode not in ('auto', 'on', 'off'):
raise ValueError(f'segmentedLineMode must be "auto", "on" or "off", got {mode} instead')
self.opts['segmentedLineMode'] = mode
self.invalidateBounds()
self.update()

def _shouldUseDrawLineSegments(self, pen):
mode = self.opts['segmentedLineMode']
if mode in ('on'):
return True
if mode in ('off'):
return False
return (
pen.widthF() > 1.0
# non-solid pen styles need single polyline to be effective
Expand All @@ -634,6 +663,9 @@ def _shouldUseDrawLineSegments(self, pen):
and pen.isSolid() # pen.brush().style() == Qt.BrushStyle.SolidPattern
# ends of adjacent line segments overlapping is visible when not opaque
and pen.color().alphaF() == 1.0
# anti-aliasing introduces transparent pixels and therefore also causes visible overlaps
# for adjacent line segments
and not self.opts['antialias']
)

def _getLineSegments(self):
Expand Down

0 comments on commit 1dd6b9d

Please sign in to comment.