Skip to content

Allow both linestyle definition "accents" and dash-patterns as linestyle... #3265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,7 @@ def tk_window_focus():
'matplotlib.tests.test_transforms',
'matplotlib.tests.test_triangulation',
'mpl_toolkits.tests.test_mplot3d',
'matplotlib.tests.test_fmt'
]


Expand Down
39 changes: 17 additions & 22 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def _process_plot_format(fmt):

# Is fmt just a colorspec?
try:
color = mcolors.colorConverter.to_rgb(fmt)
mcolors.colorConverter.to_rgb(fmt)
color = fmt

# We need to differentiate grayscale '1.0' from tri_down marker '1'
try:
Expand All @@ -80,27 +81,23 @@ def _process_plot_format(fmt):
except ValueError:
pass # No, not just a color.

# handle the multi char special cases and strip them from the
# string
if fmt.find('--') >= 0:
linestyle = '--'
fmt = fmt.replace('--', '')
if fmt.find('-.') >= 0:
linestyle = '-.'
fmt = fmt.replace('-.', '')
if fmt.find(' ') >= 0:
linestyle = 'None'
fmt = fmt.replace(' ', '')
for ls in sorted(mlines.lineStyles, key=len, reverse=True):
if fmt.find(ls) >= 0:
linestyle = ls
fmt = fmt.replace(ls, '')
break

# Check for double linestyle definition
if linestyle:
for ls in sorted(mlines.lineStyles, key=len):
if fmt.find(ls) >= 0:
raise ValueError(
'Illegal format string "%s"; two linestyle symbols' % fmt)

chars = [c for c in fmt]

for c in chars:
if c in mlines.lineStyles:
if linestyle is not None:
raise ValueError(
'Illegal format string "%s"; two linestyle symbols' % fmt)
linestyle = c
elif c in mlines.lineMarkers:
if c in mlines.lineMarkers:
if marker is not None:
raise ValueError(
'Illegal format string "%s"; two marker symbols' % fmt)
Expand All @@ -114,11 +111,9 @@ def _process_plot_format(fmt):
raise ValueError(
'Unrecognized character %c in format string' % c)

if linestyle is None and marker is None:
linestyle = rcParams['lines.linestyle']
if linestyle is None:
if linestyle is None and marker is not None:
linestyle = 'None'
if marker is None:
if marker is None and linestyle is not None:
marker = 'None'

return linestyle, marker, color
Expand Down
1 change: 0 additions & 1 deletion lib/matplotlib/cbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -2108,7 +2108,6 @@ def unmasked_index_ranges(mask, compressed=True):
(':', 'dotted')]

ls_mapper = dict(_linestyles)
ls_mapper.update([(ls[1], ls[0]) for ls in _linestyles])


def align_iterators(func, *iterables):
Expand Down
24 changes: 17 additions & 7 deletions lib/matplotlib/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,27 +463,37 @@ def set_linestyle(self, ls):
"""
Set the linestyle(s) for the collection.

ACCEPTS: ['solid' | 'dashed', 'dashdot', 'dotted' |
(offset, on-off-dash-seq) ]

Parameters
----------
ls : { '-', '--', '-.', ':'} and more see description
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move most of this text to the main description of the docs? It comes down to how it renders when you build the documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?! What text do you mean, the parameter description, type or name? What main description of the docs, the one of Line2D? The tables of keyword arguments actually do not work correctly with numpydoc. Is that what you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set linestyles(s) of collection

text about all the gory details

Parameters
---------------
ls : str
    The linestyle

The line style. The written out linestyles
'solid', 'dashed', 'dashdot' and 'dotted' and drawstyle in
combination with a linestyle, e.g., ``'steps--'`` are also allowed.

Alternatively a dash tuple of the following form can be provided::

(offset, onoffseq),

where ``onoffseq`` is an even length tuple of on and off ink
in points.
"""
try:
dashd = backend_bases.GraphicsContextBase.dashd
if cbook.is_string_like(ls):
ls = cbook.ls_mapper.get(ls, ls)
if ls in dashd:
dashes = [dashd[ls]]
elif ls in cbook.ls_mapper:
dashes = [dashd[cbook.ls_mapper[ls]]]
else:
raise ValueError()
elif cbook.iterable(ls):
try:
dashes = []
for x in ls:
if cbook.is_string_like(x):
x = cbook.ls_mapper.get(x, x)
if x in dashd:
dashes.append(dashd[x])
elif x in cbook.ls_mapper:
dashes.append(dashd[cbook.ls_mapper[x]])
else:
raise ValueError()
elif cbook.iterable(x) and len(x) == 2:
Expand All @@ -492,7 +502,7 @@ def set_linestyle(self, ls):
raise ValueError()
except ValueError:
if len(ls) == 2:
dashes = ls
dashes = [ls]
else:
raise ValueError()
else:
Expand Down
118 changes: 56 additions & 62 deletions lib/matplotlib/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from .artist import allow_rasterization
from matplotlib import docstring
from matplotlib.markers import MarkerStyle
import matplotlib.backend_bases

# Imported here for backward compatibility, even though they don't
# really belong.
from matplotlib.markers import TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN
Expand Down Expand Up @@ -194,15 +196,9 @@ class Line2D(Artist):


"""
lineStyles = _lineStyles = { # hidden names deprecated
'-': '_draw_solid',
'--': '_draw_dashed',
'-.': '_draw_dash_dot',
':': '_draw_dotted',
'None': '_draw_nothing',
' ': '_draw_nothing',
'': '_draw_nothing',
}
lineStyles = _lineStyles = [ # hidden names deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this change from a dictionary to a list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linestyle drawing functions, their names being the values here, are removed, as linestyles are handled in the backend code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an api break on a public part of the API. It seems we don't use it anythere else in the code base (and hopefully no one else does either!) this needs to be documented in api_changes.rst.

While touching this, might as will make it private (add an _ to the name).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could just leave it untouched as mentioned in my last comment. I just did not realize that this might be in public use.
Apart from the keys it references internals.

'-', '--', '-.', ':' # , 'None', ' ', #''
]

_drawStyles_l = {
'default': '_draw_lines',
Expand Down Expand Up @@ -328,7 +324,6 @@ def __init__(self, xdata, ydata,
self.set_markevery(markevery)
self.set_antialiased(antialiased)
self.set_markersize(markersize)
self._dashSeq = None

self.set_markerfacecolor(markerfacecolor)
self.set_markerfacecoloralt(markerfacecoloralt)
Expand Down Expand Up @@ -408,7 +403,7 @@ def contains(self, mouseevent):
olderrflags = np.seterr(all='ignore')
try:
# Check for collision
if self._linestyle in ['None', None]:
if not self._linestyle:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has 'None' been completely validated out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should happen at line 958 in set_linestyle:

        if linestyle in [' ', 'None', 'none', '']:
            linestyle = ''

# If no line, return the nearby point(s)
d = (xt - mouseevent.x) ** 2 + (yt - mouseevent.y) ** 2
ind, = np.nonzero(np.less_equal(d, pixels ** 2))
Expand Down Expand Up @@ -702,11 +697,10 @@ def draw(self, renderer):
if self.get_sketch_params() is not None:
gc.set_sketch_params(*self.get_sketch_params())

funcname = self._lineStyles.get(self._linestyle, '_draw_nothing')
if funcname != '_draw_nothing':
linestyle = self._linestyle
if linestyle:
tpath, affine = transf_path.get_transformed_path_and_affine()
if len(tpath.vertices):
self._lineFunc = getattr(self, funcname)
funcname = self.drawStyles.get(self._drawstyle, '_draw_lines')
drawFunc = getattr(self, funcname)
drawFunc(renderer, gc, tpath, affine.frozen())
Expand Down Expand Up @@ -921,17 +915,17 @@ def set_linestyle(self, linestyle):
Set the linestyle of the line (also accepts drawstyles)


================ =================
linestyle description
================ =================
``'-'`` solid
``'--'`` dashed
``'-.'`` dash_dot
``':'`` dotted
``'None'`` draw nothing
``' '`` draw nothing
``''`` draw nothing
================ =================
=========================== =================
linestyle description
=========================== =================
``'-'`` or ``'solid'`` solid line
``'--'`` or ``'dashed'`` dashed line
``'-.'`` or ``'dash_dot'`` dash-dotted line
``':'`` or ``'dotted'`` dotted line
``'None'`` draw nothing
``' '`` draw nothing
``''`` draw nothing
=========================== =================

'steps' is equivalent to 'steps-pre' and is maintained for
backward-compatibility.
Expand All @@ -941,11 +935,26 @@ def set_linestyle(self, linestyle):
:meth:`set_drawstyle`
To set the drawing style (stepping) of the plot.

ACCEPTS: [``'-'`` | ``'--'`` | ``'-.'`` | ``':'`` | ``'None'`` |
``' '`` | ``''``]
Parameters
----------
ls : { '-', '--', '-.', ':'} and more see description
The line style. The written out linestyles
'solid', 'dashed', 'dashdot' and 'dotted' and drawstyle in
combination with a linestyle, e.g., ``'steps--'`` are also allowed.

Alternatively a dash tuple of the following form can be provided::

(offset, onoffseq),

and any drawstyle in combination with a linestyle, e.g., ``'steps--'``.
where ``onoffseq`` is an even length tuple of on and off ink
in points.
"""
if not is_string_like(linestyle):
if len(linestyle) != 2:
raise ValueError()

self._linestyle = linestyle
return

for ds in self.drawStyleKeys: # long names are first in the list
if linestyle.startswith(ds):
Expand All @@ -956,14 +965,19 @@ def set_linestyle(self, linestyle):
linestyle = '-'
break

if linestyle not in self._lineStyles:
if linestyle in ls_mapper:
linestyle = ls_mapper[linestyle]
else:
linestyle = ls_mapper.get(linestyle, linestyle)
# TODO: check if in dashd of backend?
#else:
# verbose.report('Unrecognized line style %s, %s' %
# (linestyle, type(linestyle)))
if linestyle in [' ', 'None', 'none', '']:
linestyle = ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems like an API change to me, previously 'None' was the canonical way to store 'don't draw anything' and now it is an empty string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I will make it 'None' and change the above to deal with it. In patches and collections there never was such a convention, what about these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just playing with the idea to just change get_linestyle to rewrite it, because having no linestyle compare to False is kind of convenient. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does make a tremendous about of sense to have the line style that means don't draw evaluate to false.

Historically we have had small API breaks on 1.X releases, particularly when they affect things that should not be part of the public API and do have protocol for deprecating APIs. My point is not that this is a show stopper, just that we need to be a thoughtful about it and make sure to document it.

else:
dashd = matplotlib.backend_bases.GraphicsContextBase.dashd
if linestyle not in dashd:
verbose.report('Unrecognized line style %s, %s' %
(linestyle, type(linestyle)))
if linestyle in [' ', '']:
linestyle = 'None'
linestyle = ''
self._linestyle = linestyle

@docstring.dedent_interpd
Expand Down Expand Up @@ -1060,11 +1074,11 @@ def set_dashes(self, seq):
if seq == (None, None) or len(seq) == 0:
self.set_linestyle('-')
else:
self.set_linestyle('--')
self._dashSeq = seq # TODO: offset ignored for now
self._linestyle = (0, seq) # TODO: offset ignored for now

def _draw_lines(self, renderer, gc, path, trans):
self._lineFunc(renderer, gc, path, trans)
gc.set_linestyle(self._linestyle)
renderer.draw_path(gc, path, trans)

def _draw_steps_pre(self, renderer, gc, path, trans):
vertices = self._xy
Expand All @@ -1075,7 +1089,7 @@ def _draw_steps_pre(self, renderer, gc, path, trans):

path = Path(steps)
path = path.transformed(self.get_transform())
self._lineFunc(renderer, gc, path, IdentityTransform())
self._draw_lines(renderer, gc, path, IdentityTransform())

def _draw_steps_post(self, renderer, gc, path, trans):
vertices = self._xy
Expand All @@ -1086,7 +1100,7 @@ def _draw_steps_post(self, renderer, gc, path, trans):

path = Path(steps)
path = path.transformed(self.get_transform())
self._lineFunc(renderer, gc, path, IdentityTransform())
self._draw_lines(renderer, gc, path, IdentityTransform())

def _draw_steps_mid(self, renderer, gc, path, trans):
vertices = self._xy
Expand All @@ -1100,26 +1114,7 @@ def _draw_steps_mid(self, renderer, gc, path, trans):

path = Path(steps)
path = path.transformed(self.get_transform())
self._lineFunc(renderer, gc, path, IdentityTransform())

def _draw_solid(self, renderer, gc, path, trans):
gc.set_linestyle('solid')
renderer.draw_path(gc, path, trans)

def _draw_dashed(self, renderer, gc, path, trans):
gc.set_linestyle('dashed')
if self._dashSeq is not None:
gc.set_dashes(0, self._dashSeq)

renderer.draw_path(gc, path, trans)

def _draw_dash_dot(self, renderer, gc, path, trans):
gc.set_linestyle('dashdot')
renderer.draw_path(gc, path, trans)

def _draw_dotted(self, renderer, gc, path, trans):
gc.set_linestyle('dotted')
renderer.draw_path(gc, path, trans)
self._draw_lines(renderer, gc, path, IdentityTransform())

def update_from(self, other):
"""copy properties from other to self"""
Expand All @@ -1132,13 +1127,11 @@ def update_from(self, other):
self._markerfacecoloralt = other._markerfacecoloralt
self._markeredgecolor = other._markeredgecolor
self._markeredgewidth = other._markeredgewidth
self._dashSeq = other._dashSeq
self._dashcapstyle = other._dashcapstyle
self._dashjoinstyle = other._dashjoinstyle
self._solidcapstyle = other._solidcapstyle
self._solidjoinstyle = other._solidjoinstyle

self._linestyle = other._linestyle
self._marker = MarkerStyle(other._marker.get_marker(),
other._marker.get_fillstyle())
self._drawstyle = other._drawstyle
Expand Down Expand Up @@ -1309,7 +1302,8 @@ def get_solid_capstyle(self):

def is_dashed(self):
'return True if line is dashstyle'
return self._linestyle in ('--', '-.', ':')
return (self._linestyle in ('dashed', 'dashdot', 'dotted') or
not is_string_like(self._linestyle))


class VertexSelector(object):
Expand Down
16 changes: 15 additions & 1 deletion lib/matplotlib/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,24 @@ def set_linestyle(self, ls):
"""
Set the patch linestyle

ACCEPTS: ['solid' | 'dashed' | 'dashdot' | 'dotted']
Parameters
----------
ls : { '-', '--', '-.', ':'} and more see description
The line style. The written out linestyles
'solid', 'dashed', 'dashdot' and 'dotted' and drawstyle in
combination with a linestyle, e.g., ``'steps--'`` are also allowed.

Alternatively a dash tuple of the following form can be provided::

(offset, onoffseq),

where ``onoffseq`` is an even length tuple of on and off ink
in points.
"""
if ls is None:
ls = "solid"

ls = cbook.ls_mapper.get(ls, ls)
self._linestyle = ls

def set_ls(self, ls):
Expand Down
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading