@@ -50,7 +50,9 @@ def __init__(self):
50
50
"""
51
51
self .plotly_fig = Figure (data = Data (), layout = Layout ())
52
52
self .mpl_fig = None
53
- self .current_ax_patches = []
53
+ self .current_mpl_ax = None
54
+ self .bar_containers = None
55
+ self .current_bars = []
54
56
self .axis_ct = 0
55
57
self .mpl_x_bounds = (0 , 1 )
56
58
self .mpl_y_bounds = (0 , 1 )
@@ -137,6 +139,10 @@ def open_axes(self, ax, props):
137
139
138
140
"""
139
141
self .msg += " Opening axes\n "
142
+ self .current_mpl_ax = ax
143
+ self .bar_containers = [c for c in ax .containers # empty is OK
144
+ if c .__class__ .__name__ == 'BarContainer' ]
145
+ self .current_bars = []
140
146
self .axis_ct += 1
141
147
# set defaults in axes
142
148
xaxis = XAxis (
@@ -173,20 +179,27 @@ def close_axes(self, ax):
173
179
174
180
Bars from bar charts are given to PlotlyRenderer one-by-one,
175
181
thus they need to be taken care of at the close of each axes object.
176
- The self.current_ax_patches variable should be empty unless a bar
177
- chart has been created or a rectangle object has been drawn that has
178
- an edge exactly on the lines x=0 or y=0.
182
+ The self.current_bars variable should be empty unless a bar
183
+ chart has been created.
179
184
180
185
Positional arguments:
181
186
ax -- an mpl axes object, not required at this time.
182
187
183
188
"""
184
- for patch_coll in self .current_ax_patches :
185
- self .draw_bar (patch_coll )
186
- self .current_ax_patches = [] # clear this for next axes obj
189
+ self .draw_bars (self .current_bars )
187
190
self .msg += " Closing axes\n "
188
191
189
- def draw_bar (self , patch_coll ):
192
+ def draw_bars (self , bars ):
193
+
194
+ # sort bars according to bar containers
195
+ mpl_traces = []
196
+ for container in self .bar_containers :
197
+ mpl_traces .append ([bar_props for bar_props in self .current_bars
198
+ if bar_props ['mplobj' ] in container ])
199
+ for trace in mpl_traces :
200
+ self .draw_bar (trace )
201
+
202
+ def draw_bar (self , coll ):
190
203
"""Draw a collection of similar patches as a bar chart.
191
204
192
205
After bars are sorted, an appropriate data dictionary must be created
@@ -198,30 +211,67 @@ def draw_bar(self, patch_coll):
198
211
patch_coll -- a collection of patches to be drawn as a bar chart.
199
212
200
213
"""
201
- orientation = patch_coll [0 ]['orientation' ]
214
+ tol = 1e-10
215
+ trace = [mpltools .make_bar (** bar_props ) for bar_props in coll ]
216
+ widths = [bar_props ['x1' ] - bar_props ['x0' ] for bar_props in trace ]
217
+ heights = [bar_props ['y1' ] - bar_props ['y0' ] for bar_props in trace ]
218
+ vertical = abs (
219
+ sum (widths [0 ]- widths [iii ] for iii in range (len (widths )))
220
+ ) < tol
221
+ horizontal = abs (
222
+ sum (heights [0 ]- heights [iii ] for iii in range (len (heights )))
223
+ ) < tol
224
+ if vertical and horizontal :
225
+ # Check for monotonic x. Can't both be true!
226
+ x_zeros = [bar_props ['x0' ] for bar_props in trace ]
227
+ if all ((x_zeros [iii + 1 ] > x_zeros [iii ]
228
+ for iii in range (len (x_zeros [:- 1 ])))):
229
+ orientation = 'v'
230
+ else :
231
+ orientation = 'h'
232
+ elif vertical :
233
+ orientation = 'v'
234
+ else :
235
+ orientation = 'h'
202
236
if orientation == 'v' :
203
237
self .msg += " Attempting to draw a vertical bar chart\n "
204
- patch_coll .sort (key = lambda b : b ['x0' ])
205
- x = [bar ['x0' ]+ (bar ['x1' ]- bar ['x0' ])/ 2 for bar in patch_coll ]
206
- y = [bar ['y1' ] for bar in patch_coll ]
207
- bar_gap = mpltools .get_bar_gap ([bar ['x0' ] for bar in patch_coll ],
208
- [bar ['x1' ] for bar in patch_coll ])
238
+ old_heights = [bar_props ['y1' ] for bar_props in trace ]
239
+ for bar in trace :
240
+ bar ['y0' ], bar ['y1' ] = 0 , bar ['y1' ] - bar ['y0' ]
241
+ new_heights = [bar_props ['y1' ] for bar_props in trace ]
242
+ # check if we're stacked or not...
243
+ for old , new in zip (old_heights , new_heights ):
244
+ if abs (old - new ) > tol :
245
+ self .plotly_fig ['layout' ]['barmode' ]= 'stack'
246
+ self .plotly_fig ['layout' ]['hovermode' ]= 'x'
247
+ x = [bar ['x0' ]+ (bar ['x1' ]- bar ['x0' ])/ 2 for bar in trace ]
248
+ y = [bar ['y1' ] for bar in trace ]
249
+ bar_gap = mpltools .get_bar_gap ([bar ['x0' ] for bar in trace ],
250
+ [bar ['x1' ] for bar in trace ])
209
251
else :
210
252
self .msg += " Attempting to draw a horizontal bar chart\n "
211
- patch_coll .sort (key = lambda b : b ['y0' ])
212
- x = [bar ['x1' ] for bar in patch_coll ]
213
- y = [bar ['y0' ]+ (bar ['y1' ]- bar ['y0' ])/ 2 for bar in patch_coll ]
214
- bar_gap = mpltools .get_bar_gap ([bar ['y0' ] for bar in patch_coll ],
215
- [bar ['y1' ] for bar in patch_coll ])
253
+ old_rights = [bar_props ['x1' ] for bar_props in trace ]
254
+ for bar in trace :
255
+ bar ['x0' ], bar ['x1' ] = 0 , bar ['x1' ] - bar ['x0' ]
256
+ new_rights = [bar_props ['x1' ] for bar_props in trace ]
257
+ # check if we're stacked or not...
258
+ for old , new in zip (old_rights , new_rights ):
259
+ if abs (old - new ) > tol :
260
+ self .plotly_fig ['layout' ]['barmode' ]= 'stack'
261
+ self .plotly_fig ['layout' ]['hovermode' ]= 'y'
262
+ x = [bar ['x1' ] for bar in trace ]
263
+ y = [bar ['y0' ]+ (bar ['y1' ]- bar ['y0' ])/ 2 for bar in trace ]
264
+ bar_gap = mpltools .get_bar_gap ([bar ['y0' ] for bar in trace ],
265
+ [bar ['y1' ] for bar in trace ])
216
266
bar = Bar (orientation = orientation ,
217
267
x = x ,
218
268
y = y ,
219
269
xaxis = 'x{0}' .format (self .axis_ct ),
220
270
yaxis = 'y{0}' .format (self .axis_ct ),
221
- opacity = patch_coll [0 ]['alpha' ],
271
+ opacity = trace [0 ]['alpha' ], # TODO: get all alphas if array?
222
272
marker = Marker (
223
- color = patch_coll [0 ]['facecolor' ],
224
- line = Line (width = patch_coll [0 ]['edgewidth' ])))
273
+ color = trace [0 ]['facecolor' ], # TODO: get all
274
+ line = Line (width = trace [0 ]['edgewidth' ]))) # TODO: get all
225
275
if len (bar ['x' ]) > 1 :
226
276
self .msg += " Heck yeah, I drew that bar chart\n "
227
277
self .plotly_fig ['data' ] += bar ,
@@ -232,6 +282,7 @@ def draw_bar(self, patch_coll):
232
282
warnings .warn ('found box chart data with length <= 1, '
233
283
'assuming data redundancy, not plotting.' )
234
284
285
+
235
286
def draw_marked_line (self , ** props ):
236
287
"""Create a data dict for a line obj.
237
288
@@ -400,65 +451,14 @@ def draw_path(self, **props):
400
451
401
452
"""
402
453
self .msg += " Attempting to draw a path\n "
403
- is_bar = mpltools .is_bar (** props )
404
- is_barh = mpltools .is_barh (** props )
405
- if is_bar : # if we think it's a bar, add it!
406
- self .msg += " Assuming path is a vertical bar\n "
407
- bar = mpltools .make_bar (orientation = 'v' , ** props )
408
- self .file_bar (bar )
409
- if is_barh : # perhaps a horizontal bar?
410
- self .msg += " Assuming path is a horizontal bar\n "
411
- bar = mpltools .make_bar (orientation = 'h' , ** props )
412
- self .file_bar (bar )
413
- if not (is_bar or is_barh ):
454
+ is_bar = mpltools .is_bar (self .current_mpl_ax .containers , ** props )
455
+ if is_bar :
456
+ self .current_bars += [props ]
457
+ else :
414
458
self .msg += " This path isn't a bar, not drawing\n "
415
459
warnings .warn ("I found a path object that I don't think is part "
416
460
"of a bar chart. Ignoring." )
417
461
418
- def file_bar (self , bar ):
419
- """Puts a given bar into an appropriate bar or barh collection.
420
-
421
- Bars come from the mplexporter one-by-one. To try to put them into
422
- appropriate data sets, we must compare them to existing data.
423
-
424
- Positional arguments:
425
- bar -- a bar dictionary created in mpltools.make_bar.py.
426
-
427
- bar.keys() -- [
428
- 'bar', (mpl path object)
429
- 'orientation', (bar direction, 'v' or 'h' for horizontal or vertical)
430
- 'x0', ([x0, y0] = bottom-left corner of rectangle)
431
- 'y0',
432
- 'x1', ([x1, y1] = top-right corner of rectangle):
433
- 'y1',
434
- 'alpha', (opacity of rectangle)
435
- 'edgecolor', (boundary line color)
436
- 'facecolor', (rectangle color)
437
- 'edgewidth', (boundary line width)
438
- 'dasharray', (linestyle for boundary line)
439
- 'zorder', (precedence when stacked)
440
- ]
441
-
442
- """
443
- self .msg += " Putting a bar into the proper bar collection\n "
444
- if len (self .current_ax_patches ) == 0 :
445
- self .msg += " Started a new bar collection with this " \
446
- "bar\n "
447
- self .current_ax_patches .append ([])
448
- self .current_ax_patches [- 1 ] += bar ,
449
- else :
450
- match = False
451
- for patch_collection in self .current_ax_patches :
452
- if mpltools .check_bar_match (patch_collection [0 ], bar ):
453
- match = True
454
- patch_collection += bar ,
455
- self .msg += " Filed bar into existing bar " \
456
- "collection\n "
457
- if not match :
458
- self .msg += " Started a new bar collection with " \
459
- "this bar\n "
460
- self .current_ax_patches .append ([])
461
- self .current_ax_patches [- 1 ] += bar ,
462
462
463
463
def draw_text (self , ** props ):
464
464
"""Create an annotation dict for a text obj.
0 commit comments