10
10
they define.
11
11
"""
12
12
13
+ from collections import namedtuple
13
14
from enum import Enum , auto
14
15
from numbers import Number
15
16
19
20
class _AutoStringNameEnum (Enum ):
20
21
"""Automate the ``name = 'name'`` part of making a (str, Enum)."""
21
22
23
+ def __new__ (cls , str_value , * value_aliases ):
24
+ """
25
+ Override Enum.__new__ to allow aliases.
26
+
27
+ For more details on strategies for allowing multiple names per enum
28
+ instance, see the discussion at
29
+ https://stackoverflow.com/questions/24105268/is-it-possible-to-override-new-in-an-enum-to-parse-strings-to-an-instance
30
+ """
31
+ obj = cls .__new__ (str_value )
32
+ obj ._value_ = str_value
33
+ for alias in value_aliases :
34
+ cls ._value2member_map_ [alias ] = obj
35
+ return obj
36
+
22
37
def _generate_next_value_ (name , start , count , last_values ):
23
38
return name
24
39
@@ -224,7 +239,7 @@ def demo():
224
239
}
225
240
226
241
227
- class NamedLineStyle ( str , _AutoStringNameEnum ):
242
+ class LineStyle ( ):
228
243
"""
229
244
Describe if the line is solid or dashed, and the dash pattern, if any.
230
245
@@ -258,9 +273,9 @@ class NamedLineStyle(str, _AutoStringNameEnum):
258
273
Setting ``onoffseq`` to ``None`` results in a solid *LineStyle*.
259
274
260
275
The default dashing patterns described in the table above are themselves
261
- all described in this notation, and can therefore be customized by editing
262
- the appropriate ``lines.*_pattern`` *rc* parameter, as described in
263
- :doc:`/tutorials/introductory/customizing`.
276
+ defined under the hood using an offset and an onoffseq, and can therefore
277
+ be customized by editing the appropriate ``lines.*_pattern`` *rc*
278
+ parameter, as described in :doc:`/tutorials/introductory/customizing`.
264
279
265
280
.. plot::
266
281
:alt: Demo of possible LineStyle's.
@@ -271,22 +286,17 @@ class NamedLineStyle(str, _AutoStringNameEnum):
271
286
.. note::
272
287
273
288
In addition to directly taking a ``linestyle`` argument,
274
- `~.lines.Line2D` exposes a ``~.lines.Line2D.set_dashes`` method that
275
- can be used to create a new *LineStyle* by providing just the
276
- ``onoffseq``, but does not let you customize the offset. This method is
277
- called when using the keyword *dashes* to the cycler , as shown in
278
- :doc:`property_cycle </tutorials/intermediate/color_cycle>`.
289
+ `~.lines.Line2D` exposes a ``~.lines.Line2D.set_dashes`` method (and
290
+ the :doc:`property_cycle </tutorials/intermediate/color_cycle>` has a
291
+ *dashes* keyword) that can be used to create a new *LineStyle* by
292
+ providing just the ``onoffseq``, but does not let you customize the
293
+ offset. This method simply sets the underlying linestyle, and is only
294
+ kept for backwards compatibility.
279
295
"""
280
- solid = auto ()
281
- dashed = auto ()
282
- dotted = auto ()
283
- dashdot = auto ()
284
- none = auto ()
285
- custom = auto ()
286
296
287
- class LineStyle (str ):
288
297
289
- def __init__ (self , ls , scale = 1 ):
298
+
299
+ def __init__ (self , ls , offset = None , scale = None ):
290
300
"""
291
301
Parameters
292
302
----------
@@ -301,56 +311,57 @@ def __init__(self, ls, scale=1):
301
311
"""
302
312
303
313
self ._linestyle_spec = ls
314
+ # if isinstance(ls, str):
315
+ # if ls in [' ', '', 'None']:
316
+ # ls = 'none'
317
+ # if ls in _ls_mapper:
318
+ # ls = _ls_mapper[ls]
319
+ # Enum.__init__(self)
320
+ # offset, onoffseq = None, None
321
+ # else:
322
+ # try:
323
+ # offset, onoffseq = ls
324
+ # except ValueError: # not enough/too many values to unpack
325
+ # raise ValueError('LineStyle should be a string or a 2-tuple, '
326
+ # 'instead received: ' + str(ls))
304
327
if isinstance (ls , str ):
305
- if ls in [' ' , '' , 'None' ]:
306
- ls = 'none'
307
- if ls in _ls_mapper :
308
- ls = _ls_mapper [ls ]
309
- Enum .__init__ (self )
310
- offset , onoffseq = None , None
311
- else :
312
- try :
313
- offset , onoffseq = ls
314
- except ValueError : # not enough/too many values to unpack
315
- raise ValueError ('LineStyle should be a string or a 2-tuple, '
316
- 'instead received: ' + str (ls ))
328
+ return
329
+ offset , onoffseq = ls
317
330
if offset is None :
318
331
_api .warn_deprecated (
319
332
"3.3" , message = "Passing the dash offset as None is deprecated "
320
333
"since %(since)s and support for it will be removed "
321
334
"%(removal)s; pass it as zero instead." )
322
335
offset = 0
323
-
324
- if onoffseq is not None :
325
- # normalize offset to be positive and shorter than the dash cycle
326
- dsum = sum (onoffseq )
327
- if dsum :
328
- offset %= dsum
329
- if len (onoffseq ) % 2 != 0 :
330
- raise ValueError ('LineStyle onoffseq must be of even length.' )
331
- if not all (isinstance (elem , Number ) for elem in onoffseq ):
332
- raise ValueError ('LineStyle onoffseq must be list of floats.' )
333
- self ._us_offset = offset
334
- self ._us_onoffseq = onoffseq
336
+ # force 0 <= offset < dash cycle length
337
+ self ._us_offset , self ._us_onoffseq = \
338
+ LineStyle ._normalize_offset (offset , onoffseq )
335
339
336
340
def __hash__ (self ):
337
341
if self == LineStyle .custom :
338
342
return (self ._us_offset , tuple (self ._us_onoffseq )).__hash__ ()
339
343
return _AutoStringNameEnum .__hash__ (self )
340
344
345
+ @staticmethod
346
+ def _normalize_offset (offset , onoffseq ):
347
+ """Normalize offset to be positive and shorter than the dash cycle."""
348
+ dsum = sum (onoffseq )
349
+ if dsum :
350
+ offset %= dsum
341
351
342
352
def get_dashes (self , lw = 1 ):
343
353
"""
344
354
Get the (scaled) dash sequence for this `.LineStyle`.
345
355
"""
346
- # defer lookup until draw time
356
+ # named linestyle lookup happens at draw time (here)
347
357
if self ._us_offset is None or self ._us_onoffseq is None :
348
- self ._us_offset , self ._us_onoffseq = \
349
- LineStyle ._get_dash_pattern (self .name )
350
- # normalize offset to be positive and shorter than the dash cycle
351
- dsum = sum (self ._us_onoffseq )
352
- self ._us_offset %= dsum
353
- return self ._scale_dashes (self ._us_offset , self ._us_onoffseq , lw )
358
+ offset , onoffseq = LineStyle ._get_dash_pattern (self .name )
359
+ else :
360
+ offset , onoff_seq = self ._us_offset , self ._us_onoffseq
361
+ # normalize offset to be positive and shorter than the dash cycle
362
+ dsum = sum (onoffseq )
363
+ offset %= dsum
364
+ return self ._scale_dashes (offset , onoffseq , lw )
354
365
355
366
@staticmethod
356
367
def _scale_dashes (offset , dashes , lw ):
@@ -462,6 +473,33 @@ def plot_linestyles(ax, linestyles, title):
462
473
plt .tight_layout ()
463
474
plt .show ()
464
475
476
+ def _LineStyle_new (cls , ls , offset = None , scale = None ):
477
+ if cls is NamedLineStyle or isinstance (ls , NamedLineStyle ):
478
+ return cls (ls )
479
+ elif isinstance (ls , LineStyle ):
480
+ scale = scale if scale is not None else ls .scale
481
+ offset = offset if offset is not None else ls .offset
482
+ onoffseq = ls .onoffseq
483
+ return LineStyle .__new__ (onoffseq , offset = offset , scale = scale )
484
+ elif isinstance (ls , str ):
485
+ return cls (ls )
486
+ else :
487
+ return super (LineStyle , cls ).__new__ (cls , ls , offset = offset ,
488
+ scale = scale )
489
+ # if len(onoffseq) % 2 != 0:
490
+ # raise ValueError('LineStyle onoffseq must be of even length.')
491
+ # if not all(isinstance(elem, Number) for elem in onoffseq):
492
+ # raise ValueError('LineStyle onoffseq must be list of floats.')
493
+
494
+
495
+ class NamedLineStyle (LineStyle , _AutoStringNameEnum ):
496
+ """There is only one instance of each named LineStyle ever."""
497
+ solid = 'solid' , '-'
498
+ dashed = 'dashed' , '--'
499
+ dotted = 'dotted' , ':'
500
+ dashdot = 'dashdot' , '-.'
501
+ none = 'none' , 'None' , ' '
502
+
465
503
466
504
LineStyle ._ls_mapper = _ls_mapper
467
505
LineStyle ._deprecated_lineStyles = _deprecated_lineStyles
0 commit comments