• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sandialabs / toyplot / 9912662049

12 Jul 2024 06:45PM UTC coverage: 94.497% (-0.1%) from 94.629%
9912662049

Pull #214

github

web-flow
Merge cce091bb5 into 93ab5d60c
Pull Request #214: add as_float, change repr(x) -> str(x)

70 of 87 new or added lines in 10 files covered. (80.46%)

31 existing lines in 2 files now uncovered.

5409 of 5724 relevant lines covered (94.5%)

8.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.49
/toyplot/canvas.py
1
# Copyright 2014, Sandia Corporation. Under the terms of Contract
2
# DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain
3
# rights in this software.
4

5
"""Implements the :class:`toyplot.canvas.Canvas` class, which defines the space that is available for creating plots.
6
"""
7

8

9
import collections
9✔
10
import numbers
9✔
11

12
import numpy
9✔
13
import toyplot.coordinates
9✔
14
import toyplot.broadcast
9✔
15
import toyplot.color
9✔
16
import toyplot.config
9✔
17
import toyplot.layout
9✔
18
import toyplot.scenegraph
9✔
19
import toyplot.style
9✔
20
import toyplot.units
9✔
21

22

23
##########################################################################
24
# Canvas
25

26
class Canvas(object):
9✔
27
    """Top-level container for Toyplot drawings.
28

29
    Parameters
30
    ----------
31
    width: number, string, or (number, string) tuple, optional
32
        Width of the canvas.  Assumes CSS pixels if units aren't provided.
33
        Defaults to 600px (6.25") if unspecified.  See :ref:`units` for details on
34
        how Toyplot handles real world units.
35
    height: number, string, or (number, string) tuple, optional
36
        Height of the canvas.  Assumes CSS pixels if units aren't provided.
37
        Defaults to the canvas width if unspecified.  See :ref:`units` for details
38
        on how Toyplot handles real world units.
39
    style: dict, optional
40
        Collection of CSS styles to apply to the canvas.
41
    hyperlink: string, optional
42
        When specified, the entire canvas is hyperlinked to the given URI.
43
        Note that hyperlinks set on other entities (such as axes, marks, or
44
        text) will override this.
45
    autorender: boolean, optional
46
        Turn autorendering on / off for this canvas.  Defaults to the value in
47
        :data:`toyplot.config.autorender`.
48
    autoformat: string, optional
49
        Specify the format ("html" or "png") to be used for autorendering this
50
        canvas.  Defaults to the value in :data:`toyplot.config.autoformat`.
51

52
    Examples
53
    --------
54

55
    The following would create a Canvas 8 inches wide and 6 inches tall, with a yellow background:
56

57
    >>> canvas = toyplot.Canvas("8in", "6in", style={"background-color":"yellow"})
58
    """
59

60
    def __init__(self, width=None, height=None, style=None, hyperlink=None, autorender=None, autoformat=None):
9✔
61
        if width is None:
9✔
62
            width = toyplot.config.width
9✔
63
        if height is None:
9✔
64
            height = toyplot.config.height
9✔
65
        self._width = toyplot.units.convert(width, "px", default="px") if width is not None else 600
9✔
66
        self._height = toyplot.units.convert(height, "px", default="px") if height is not None else self._width
9✔
67
        self._hyperlink = None
9✔
68
        self._style = {
9✔
69
            "background-color": "transparent",
70
            "border-color": toyplot.color.black,
71
            "border-style": "none",
72
            "border-width": 1.0,
73
            "fill": toyplot.color.black,
74
            "fill-opacity": 1.0,
75
            "font-family": "Helvetica",
76
            "font-size": "12px",
77
            "opacity": 1.0,
78
            "stroke": toyplot.color.black,
79
            "stroke-opacity": 1.0,
80
            "stroke-width": 1.0,
81
        }
82

83
        self.hyperlink = hyperlink
9✔
84
        self.style = style
9✔
85

86
        self._scenegraph = toyplot.scenegraph.SceneGraph()
9✔
87
        self.autorender(autorender, autoformat)
9✔
88

89
    def _repr_html_(self):
9✔
90
        from . import html
9✔
91
        return toyplot.html.tostring(self, style={"text-align":"center"})
9✔
92

93
    def _repr_png_(self):
9✔
94
        from . import png
9✔
95
        return toyplot.png.render(self)
9✔
96

97
    @property
9✔
98
    def height(self):
9✔
99
        """Height of the canvas in CSS pixels."""
100
        return self._height
9✔
101

102
    @height.setter
9✔
103
    def height(self, value):
9✔
UNCOV
104
        self._height = toyplot.units.convert(value, "px", default="px")
×
105

106
    @property
9✔
107
    def hyperlink(self):
9✔
108
        """URI that will be hyperlinked from the entire canvas."""
UNCOV
109
        return self._hyperlink
×
110

111
    @hyperlink.setter
9✔
112
    def hyperlink(self, value):
9✔
113
        self._hyperlink = toyplot.require.hyperlink(value)
9✔
114

115
    @property
9✔
116
    def style(self):
9✔
117
        """Collection of CSS styles that will be applied to the canvas."""
UNCOV
118
        return self._style
×
119

120
    @style.setter
9✔
121
    def style(self, value):
9✔
122
        self._style = toyplot.style.combine(
9✔
123
            self._style,
124
            toyplot.style.require(value, allowed=set(["background-color", "border-color", "border-style", "border-width"])),
125
            )
126

127
    @property
9✔
128
    def width(self):
9✔
129
        """Width of the canvas in CSS pixels."""
130
        return self._width
9✔
131

132
    @width.setter
9✔
133
    def width(self, value):
9✔
UNCOV
134
        self._width = toyplot.units.convert(value, "px", default="px")
×
135

136

137
    def autorender(self, enable=None, autoformat=None):
9✔
138
        """Enable / disable canvas autorendering.
139

140
        Autorendering - which is enabled by default when a canvas is created -
141
        controls how the canvas should be displayed automatically without
142
        caller intervention in certain interactive environments, such as Jupyter
143
        notebooks.
144

145
        Note that autorendering is disabled when a canvas is explicitly
146
        shown using any of the rendering backends.
147

148
        Parameters
149
        ----------
150
        enable: boolean, optional
151
            Turn autorendering on / off for this canvas.  Defaults to the value
152
            in :data:`toyplot.config.autorender`.
153
        format: string, optional
154
            Specify the format ("html" or "png") to be used for autorendering
155
            this canvas.  Defaults to the value in
156
            :data:`toyplot.config.autoformat`.
157
        """
158
        if enable is None:
9✔
159
            enable = toyplot.config.autorender
9✔
160
        if autoformat is None:
9✔
161
            autoformat = toyplot.config.autoformat
9✔
162

163
        Canvas._autorender = [entry for entry in Canvas._autorender if entry[0] != self]
9✔
164
        if enable:
9✔
165
            Canvas._autorender.append((self, autoformat))
9✔
166

167
    def cartesian(
9✔
168
            self,
169
            aspect=None,
170
            bounds=None,
171
            corner=None,
172
            grid=None,
173
            hyperlink=None,
174
            label=None,
175
            margin=50,
176
            padding=10,
177
            palette=None,
178
            rect=None,
179
            show=True,
180
            xlabel=None,
181
            xmax=None,
182
            xmin=None,
183
            xscale="linear",
184
            xshow=True,
185
            xticklocator=None,
186
            ylabel=None,
187
            ymax=None,
188
            ymin=None,
189
            yscale="linear",
190
            yshow=True,
191
            yticklocator=None,
192
        ):
193
        """Add a set of Cartesian axes to the canvas.
194

195
        See Also
196
        --------
197
        :ref:`cartesian-coordinates`
198

199
        Parameters
200
        ----------
201
        aspect: string, optional
202
            Set to "fit-range" to automatically expand the domain so that its
203
            aspect ratio matches the aspect ratio of the range.
204
        bounds: (xmin, xmax, ymin, ymax) tuple, optional
205
            Use the bounds property to position / size the axes by specifying the
206
            position of each of its boundaries.  Assumes CSS pixels if units
207
            aren't provided, and supports all units described in :ref:`units`,
208
            including percentage of the canvas width / height.
209
        corner: (corner, inset, width, height) tuple, optional
210
            Use the corner property to position / size the axes by specifying its
211
            width and height, plus an inset from a corner of the canvas.  Allowed
212
            corner values are "top-left", "top", "top-right", "right",
213
            "bottom-right", "bottom", "bottom-left", and "left".  The width and
214
            height may be specified using absolute units as described in
215
            :ref:`units`, or as a percentage of the canvas width / height.  The
216
            inset only supports absolute drawing units.  All units default to CSS
217
            pixels if unspecified.
218
        grid: (rows, columns, index) tuple, or (rows, columns, i, j) tuple, or (rows, columns, i, rowspan, j, columnspan) tuple, optional
219
            Use the grid property to position / size the axes using a collection of
220
            grid cells filling the canvas.  Specify the number of rows and columns in
221
            the grid, then specify either a zero-based cell index (which runs in
222
            left-ot-right, top-to-bottom order), a pair of i, j cell coordinates, or
223
            a set of i, column-span, j, row-span coordinates so the legend can cover
224
            more than one cell.
225
        hyperlink: string, optional
226
            When specified, the range (content area of the axes) is hyperlinked
227
            to the given URI.  Note that this overrides the canvas hyperlink,
228
            if any, and is overridden by hyperlinks set on other entities such
229
            as marks or text.
230
        label: string, optional
231
            Human-readable axes label.
232
        margin: number, (top, side) tuple, (top, side, bottom) tuple, or (top, right, bottom, left) tuple, optional
233
            Specifies the amount of empty space to leave between the axes
234
            contents and the containing grid cell When using the `grid`
235
            parameter for positioning.  Assumes CSS pixels by default, and
236
            supports all of the absolute units described in :ref:`units`.
237
        padding: number, string, or (number, string) tuple,  optional
238
            Distance between the axes and plotted data.  Assumes CSS pixels if units aren't provided.
239
            See :ref:`units` for details on how Toyplot handles real-world units.
240
        palette: :class:`toyplot.color.Palette`, optional
241
            Specifies a palette to be used when automatically assigning per-series colors.
242
        rect: (x, y, width, height) tuple, optional
243
            Use the rect property to position / size the axes by specifying its
244
            upper-left-hand corner, width, and height.  Assumes CSS pixels if
245
            units aren't provided, and supports all units described in
246
            :ref:`units`, including percentage of the canvas width / height.
247
        show: bool, optional
248
            Set to `False` to hide the axes (the axes contents will still be visible).
249
        xmin, xmax, ymin, ymax: float, optional
250
            Used to explicitly override the axis domain (normally, the domain is
251
            implicitly defined by the marks added to the axes).
252
        xshow, yshow: bool, optional
253
            Set to `False` to hide individual axes.
254
        xlabel, ylabel: string, optional
255
            Human-readable axis labels.
256
        xticklocator, yticklocator: :class:`toyplot.locator.TickLocator`, optional
257
            Controls the placement and formatting of axis ticks and tick labels.
258
        xscale, yscale: "linear", "log", "log10", "log2", or a ("log", <base>) tuple, optional
259
            Specifies the mapping from data to canvas coordinates along an
260
            axis.  See :ref:`log-scales` for examples.
261

262
        Returns
263
        -------
264
        axes: :class:`toyplot.coordinates.Cartesian`
265
        """
266
        xmin_range, xmax_range, ymin_range, ymax_range = toyplot.layout.region(
9✔
267
            0, self._width, 0, self._height,
268
            bounds=bounds,
269
            rect=rect,
270
            corner=corner,
271
            grid=grid,
272
            margin=margin,
273
            )
274

275
        axes = toyplot.coordinates.Cartesian(
9✔
276
            aspect=aspect,
277
            hyperlink=hyperlink,
278
            label=label,
279
            padding=padding,
280
            palette=palette,
281
            scenegraph=self._scenegraph,
282
            show=show,
283
            xaxis=None,
284
            xlabel=xlabel,
285
            xmax=xmax,
286
            xmax_range=xmax_range,
287
            xmin=xmin,
288
            xmin_range=xmin_range,
289
            xscale=xscale,
290
            xshow=xshow,
291
            xticklocator=xticklocator,
292
            ylabel=ylabel,
293
            yaxis=None,
294
            ymax=ymax,
295
            ymax_range=ymax_range,
296
            ymin=ymin,
297
            ymin_range=ymin_range,
298
            yscale=yscale,
299
            yshow=yshow,
300
            yticklocator=yticklocator,
301
            )
302

303
        self._scenegraph.add_edge(self, "render", axes)
9✔
304

305
        return axes
9✔
306

307
    def legend(
9✔
308
            self,
309
            entries,
310
            bounds=None,
311
            corner=None,
312
            grid=None,
313
            label=None,
314
            margin=50,
315
            rect=None,
316
            ):
317
        """Add a legend to the canvas.
318

319
        See Also
320
        --------
321
        :ref:`labels-and-legends`
322

323
        Parameters
324
        ----------
325
        entries: sequence of entries to add to the legend Each entry to be
326
            displayed in the legend must be either a (label, mark) tuple or a
327
            (label, marker) tuple.  Labels are human-readable text, markers are
328
            specified using the syntax described in :ref:`markers`, and marks can
329
            be any instance of :class:`toyplot.mark.Mark`.
330
        bounds: (xmin, xmax, ymin, ymax) tuple, optional
331
            Use the bounds property to position / size the legend by specifying the
332
            position of each of its boundaries.  The boundaries may be specified in
333
            absolute drawing units, or as a percentage of the canvas width / height
334
            using strings that end with "%".
335
        corner: (corner, inset, width, height) tuple, optional
336
            Use the corner property to position / size the legend by specifying its
337
            width and height, plus an inset from a corner of the canvas.  Allowed
338
            corner values are "top-left", "top", "top-right", "right",
339
            "bottom-right", "bottom", "bottom-left", and "left".  The width and
340
            height may be specified in absolute drawing units, or as a percentage of
341
            the canvas width / height using strings that end with "%".  The inset is
342
            specified in absolute drawing units.
343
        grid: (rows, columns, index) tuple, or (rows, columns, i, j) tuple, or (rows, columns, i, rowspan, j, columnspan) tuple, optional
344
            Use the grid property to position / size the legend using a collection of
345
            grid cells filling the canvas.  Specify the number of rows and columns in
346
            the grid, then specify either a zero-based cell index (which runs in
347
            left-ot-right, top-to-bottom order), a pair of i, j cell coordinates, or
348
            a set of i, column-span, j, row-span coordinates so the legend can cover
349
            more than one cell.
350
        label: str, optional
351
            Optional human-readable label for the legend.
352
        margin: number, (top, side) tuple, (top, side, bottom) tuple, or (top, right, bottom, left) tuple, optional
353
            Specifies the amount of empty space to leave between legend and the
354
            containing grid cell when using the `grid` parameter for positioning.
355
        rect: (x, y, width, height) tuple, optional
356
            Use the rect property to position / size the legend by specifying its
357
            upper-left-hand corner, width, and height.  Each parameter may be specified
358
            in absolute drawing units, or as a percentage of the canvas width / height
359
            using strings that end with "%".
360

361
        Returns
362
        -------
363
        legend: :class:`toyplot.coordinates.Table`
364
        """
365

366
        xmin, xmax, ymin, ymax = toyplot.layout.region(
9✔
367
            0, self._width, 0, self._height,
368
            bounds=bounds,
369
            corner=corner,
370
            grid=grid,
371
            margin=margin,
372
            rect=rect,
373
            )
374

375
        table = toyplot.coordinates.Table(
9✔
376
            annotation=True,
377
            brows=0,
378
            columns=2,
379
            filename=None,
380
            label=label,
381
            lcolumns=0,
382
            scenegraph=self._scenegraph,
383
            rcolumns=0,
384
            rows=len(entries),
385
            trows=0,
386
            xmax_range=xmax,
387
            xmin_range=xmin,
388
            ymax_range=ymax,
389
            ymin_range=ymin,
390
            )
391

392
        table.cells.column[0].align = "right"
9✔
393
        table.cells.column[1].align = "left"
9✔
394

395
        for index, (label, spec) in enumerate(entries): # pylint: disable=redefined-argument-from-local
9✔
396
            if isinstance(spec, toyplot.mark.Mark):
9✔
397
                markers = spec.markers
9✔
398
            else:
399
                markers = [toyplot.marker.convert(spec)]
9✔
400
            text = ""
9✔
401
            for marker in markers:
9✔
402
                if text:
9✔
403
                    text = text + " "
9✔
404
                text = text + marker
9✔
405

406
            table.cells.cell[index, 0].data = text
9✔
407
            table.cells.cell[index, 1].data = label
9✔
408

409
        self._scenegraph.add_edge(self, "render", table)
9✔
410

411
        return table
9✔
412

413
    def matrix(
9✔
414
            self,
415
            data,
416
            blabel=None,
417
            blocator=None,
418
            bounds=None,
419
            bshow=None,
420
            colorshow=False,
421
            corner=None,
422
            filename=None,
423
            grid=None,
424
            label=None,
425
            llabel=None,
426
            llocator=None,
427
            lshow=None,
428
            margin=50,
429
            rect=None,
430
            rlabel=None,
431
            rlocator=None,
432
            rshow=None,
433
            step=1,
434
            tlabel=None,
435
            tlocator=None,
436
            tshow=None,
437
        ):
438
        """Add a matrix visualization to the canvas.
439

440
        See Also
441
        --------
442
        :ref:`matrix-visualization`
443

444
        Parameters
445
        ----------
446
        data: matrix, or (matrix, :class:`toyplot.color.Map`) tuple, required
447
            The given data will be used to populate the visualization, using either the
448
            given colormap or a default.
449
        blabel: str, optional
450
            Human readable label along the bottom of the matrix.
451
        blocator: :class:`toyplot.locator.TickLocator`, optional
452
            Supplies column labels along the bottom of the matrix.
453
        bounds: (xmin, xmax, ymin, ymax) tuple, optional
454
            Use the bounds property to position / size the legend by specifying the
455
            position of each of its boundaries.  The boundaries may be specified in
456
            absolute drawing units, or as a percentage of the canvas width / height
457
            using strings that end with "%".
458
        bshow: bool, optional
459
            Set to `False` to hide column labels along the bottom of the matrix.
460
        colorshow: bool, optional
461
            Set to `True` to add a color scale to the visualization.
462
        corner: (corner, inset, width, height) tuple, optional
463
            Use the corner property to position / size the legend by specifying its
464
            width and height, plus an inset from a corner of the canvas.  Allowed
465
            corner values are "top-left", "top", "top-right", "right",
466
            "bottom-right", "bottom", "bottom-left", and "left".  The width and
467
            height may be specified in absolute drawing units, or as a percentage of
468
            the canvas width / height using strings that end with "%".  The inset is
469
            specified in absolute drawing units.
470
        filename: str, optional
471
            Supplies a default filename for users who choose to download the
472
            matrix contents.  Note that users and web browsers are free to
473
            ignore or override this.
474
        grid: (rows, columns, index) tuple, or (rows, columns, i, j) tuple, or (rows, columns, i, rowspan, j, columnspan) tuple, optional
475
            Use the grid property to position / size the legend using a collection of
476
            grid cells filling the canvas.  Specify the number of rows and columns in
477
            the grid, then specify either a zero-based cell index (which runs in
478
            left-ot-right, top-to-bottom order), a pair of i, j cell coordinates, or
479
            a set of i, column-span, j, row-span coordinates so the legend can cover
480
            more than one cell.
481
        label: str, optional
482
            Optional human-readable label for the matrix.
483
        llabel: str, optional
484
            Human readable label along the left side of the matrix.
485
        llocator: :class:`toyplot.locator.TickLocator`, optional
486
            Supplies row labels along the left side of the matrix.
487
        lshow: bool, optional
488
            Set to `False` to hide row labels along the left side of the matrix.
489
        margin: number, (top, side) tuple, (top, side, bottom) tuple, or (top, right, bottom, left) tuple, optional
490
            Specifies the amount of empty space to leave between the matrix and
491
            the containing grid cell when using the `grid` parameter for
492
            positioning.
493
        rect: (x, y, width, height) tuple, optional
494
            Use the rect property to position / size the legend by specifying its
495
            upper-left-hand corner, width, and height.  Each parameter may be specified
496
            in absolute drawing units, or as a percentage of the canvas width / height
497
            using strings that end with "%".
498
        rlabel: str, optional
499
            Human readable label along the right side of the matrix.
500
        rlocator: :class:`toyplot.locator.TickLocator`, optional
501
            Supplies row labels along the right side of the matrix.
502
        rshow: bool, optional
503
            Set to `False` to hide row labels along the right side of the matrix.
504
        step: integer, optional
505
            Specifies the number of rows or columns to skip between indices.
506
            This is useful when the matrix cells become too small relative to
507
            the index text.
508
        tlabel: str, optional
509
            Human readable label along the top of the matrix.
510
        tlocator: :class:`toyplot.locator.TickLocator`, optional
511
            Supplies column labels along the top of the matrix.
512
        tshow: bool, optional
513
            Set to `False` to hide column labels along the top of the matrix.
514

515
        Returns
516
        -------
517
        axes: :class:`toyplot.coordinates.Table`
518
        """
519
        if isinstance(data, tuple):
9✔
520
            matrix = toyplot.require.scalar_matrix(data[0])
9✔
521
            colormap = toyplot.require.instance(data[1], toyplot.color.Map)
9✔
522
        else:
523
            matrix = toyplot.require.scalar_matrix(data)
9✔
524
            colormap = toyplot.color.brewer.map("BlueRed", domain_min=matrix.min(), domain_max=matrix.max())
9✔
525

526
        colors = colormap.colors(matrix)
9✔
527

528
        xmin_range, xmax_range, ymin_range, ymax_range = toyplot.layout.region(
9✔
529
            0, self._width, 0, self._height, bounds=bounds, rect=rect, corner=corner, grid=grid, margin=margin)
530

531
        table = toyplot.coordinates.Table(
9✔
532
            annotation=False,
533
            brows=2,
534
            columns=matrix.shape[1],
535
            filename=filename,
536
            label=label,
537
            lcolumns=2,
538
            scenegraph=self._scenegraph,
539
            rcolumns=2,
540
            rows=matrix.shape[0],
541
            trows=2,
542
            xmax_range=xmax_range,
543
            xmin_range=xmin_range,
544
            ymax_range=ymax_range,
545
            ymin_range=ymin_range,
546
            )
547

548
        table.top.row[[0, 1]].height = 20
9✔
549
        table.bottom.row[[0, 1]].height = 20
9✔
550
        table.left.column[[0, 1]].width = 20
9✔
551
        table.right.column[[0, 1]].width = 20
9✔
552

553
        table.left.column[1].align = "right"
9✔
554
        table.right.column[0].align = "left"
9✔
555

556
        table.cells.column[[0, -1]].lstyle = {"font-weight":"bold"}
9✔
557
        table.cells.row[[0, -1]].lstyle = {"font-weight":"bold"}
9✔
558

559
        if tlabel is not None:
9✔
560
            cell = table.top.row[0].merge()
9✔
561
            cell.data = tlabel
9✔
562

563
        if llabel is not None:
9✔
564
            cell = table.left.column[0].merge()
9✔
565
            cell.data = llabel
9✔
566
            cell.angle = 90
9✔
567

568
        if rlabel is not None:
9✔
569
            cell = table.right.column[1].merge()
9✔
570
            cell.data = rlabel
9✔
571
            cell.angle = 90
9✔
572

573
        if blabel is not None:
9✔
574
            cell = table.bottom.row[1].merge()
9✔
575
            cell.data = blabel
9✔
576

577
        if tshow is None:
9✔
578
            tshow = True
9✔
579
        if tshow:
9✔
580
            if tlocator is None:
9✔
581
                tlocator = toyplot.locator.Integer(step=step)
9✔
582
            for j, label, title in zip(*tlocator.ticks(0, matrix.shape[1] - 1)): # pylint: disable=redefined-argument-from-local
9✔
583
                table.top.cell[1, int(j)].data = label
9✔
584
                #table.top.cell[1, j].title = title
585

586
        if lshow is None:
9✔
587
            lshow = True
9✔
588
        if lshow:
9✔
589
            if llocator is None:
9✔
590
                llocator = toyplot.locator.Integer(step=step)
9✔
591
            for i, label, title in zip(*llocator.ticks(0, matrix.shape[0] - 1)): # pylint: disable=redefined-argument-from-local
9✔
592
                table.left.cell[int(i), 1].data = label
9✔
593
                #table.left.cell[i, 1].title = title
594

595
        if rshow is None and rlocator is not None:
9✔
596
            rshow = True
9✔
597
        if rshow:
9✔
598
            if rlocator is None:
9✔
599
                rlocator = toyplot.locator.Integer(step=step)
9✔
600
            for i, label, title in zip(*rlocator.ticks(0, matrix.shape[0] - 1)): # pylint: disable=redefined-argument-from-local
9✔
601
                table.right.cell[int(i), 0].data = label
9✔
602
                #table.right.cell[i, 0].title = title
603

604
        if bshow is None and blocator is not None:
9✔
605
            bshow = True
9✔
606
        if bshow:
9✔
607
            if blocator is None:
9✔
608
                blocator = toyplot.locator.Integer(step=step)
9✔
609
            for j, label, title in zip(*blocator.ticks(0, matrix.shape[1] - 1)): # pylint: disable=redefined-argument-from-local
9✔
610
                table.bottom.cell[0, int(j)].data = label
9✔
611
                #table.bottom.cell[0, j].title = title
612

613
        table.body.cells.data = matrix
9✔
614
        table.body.cells.format = toyplot.format.NullFormatter()
9✔
615

616
        for i in numpy.arange(matrix.shape[0]):
9✔
617
            for j in numpy.arange(matrix.shape[1]):
9✔
618
                cell = table.body.cell[i, j]
9✔
619
                cell.style = {"stroke": "none", "fill": toyplot.color.to_css(colors[i, j])}
9✔
620
                cell.title = "%.6f" % matrix[i, j]
9✔
621

622
        self._scenegraph.add_edge(self, "render", table)
9✔
623

624
        if colorshow:
9✔
625
            self.color_scale(
9✔
626
                colormap=colormap,
627
                x1=xmax_range,
628
                y1=ymax_range,
629
                x2=xmax_range,
630
                y2=ymin_range,
631
                width=10,
632
                padding=10,
633
                show=True,
634
                label="",
635
                ticklocator=None,
636
                scale="linear",
637
                )
638

639
        return table
9✔
640

641
    def color_scale(
9✔
642
            self,
643
            colormap,
644
            x1=None,
645
            y1=None,
646
            x2=None,
647
            y2=None,
648
            bounds=None,
649
            corner=None,
650
            grid=None,
651
            label=None,
652
            margin=50,
653
            max=None,
654
            min=None,
655
            offset=None,
656
            padding=10,
657
            rect=None,
658
            scale="linear",
659
            show=True,
660
            ticklocator=None,
661
            width=10,
662
        ):
663
        """Add a color scale to the canvas.
664

665
        The color scale displays a mapping from scalar values to colors, for
666
        the given colormap.  Note that the supplied colormap must have an
667
        explicitly defined domain (specified when the colormap was created),
668
        otherwise the mapping would be undefined.
669

670
        Parameters
671
        ----------
672
        colormap: :class:`toyplot.color.Map`, required
673
          Colormap to be displayed.
674
        min, max: float, optional
675
          Used to explicitly override the domain that will be shown.
676
        show: bool, optional
677
          Set to `False` to hide the axis (the color bar will still be visible).
678
        label: string, optional
679
          Human-readable label placed below the axis.
680
        ticklocator: :class:`toyplot.locator.TickLocator`, optional
681
          Controls the placement and formatting of axis ticks and tick labels.
682
        scale: "linear", "log", "log10", "log2", or a ("log", <base>) tuple, optional
683
          Specifies the mapping from data to canvas coordinates along an axis.
684

685
        Returns
686
        -------
687
        axes: :class:`toyplot.coordinates.Numberline`
688
        """
689
        axes = self.numberline(
9✔
690
            bounds=bounds,
691
            corner=corner,
692
            grid=grid,
693
            label=label,
694
            margin=margin,
695
            max=max,
696
            min=min,
697
            padding=padding,
698
            palette=None,
699
            rect=rect,
700
            scale=scale,
701
            show=show,
702
            ticklocator=ticklocator,
703
            x1=x1,
704
            x2=x2,
705
            y1=y1,
706
            y2=y2,
707
            )
708

709
        axes.colormap(
9✔
710
            colormap=colormap,
711
            width=width,
712
            offset=offset,
713
        )
714

715
        return axes
9✔
716

717
    def numberline(
9✔
718
            self,
719
            x1=None,
720
            y1=None,
721
            x2=None,
722
            y2=None,
723
            bounds=None,
724
            corner=None,
725
            grid=None,
726
            label=None,
727
            margin=50,
728
            max=None,
729
            min=None,
730
            padding=None,
731
            palette=None,
732
            rect=None,
733
            scale="linear",
734
            show=True,
735
            spacing=None,
736
            ticklocator=None,
737
        ):
738
        """Add a 1D numberline to the canvas.
739

740
        Parameters
741
        ----------
742
        min, max: float, optional
743
          Used to explicitly override the numberline domain (normally, the domain is
744
          implicitly defined by any marks added to the numberline).
745
        show: bool, optional
746
          Set to `False` to hide the numberline (the numberline contents will still be visible).
747
        label: string, optional
748
          Human-readable label placed below the numberline axis.
749
        ticklocator: :class:`toyplot.locator.TickLocator`, optional
750
          Controls the placement and formatting of axis ticks and tick labels.  See :ref:`tick-locators`.
751
        scale: "linear", "log", "log10", "log2", or a ("log", <base>) tuple, optional
752
          Specifies the mapping from data to canvas coordinates along the axis.  See :ref:`log-scales`.
753
        spacing: number, string, or (number, string) tuple,  optional
754
          Distance between plotted data added to the numberline.  Assumes CSS
755
          pixels if units aren't provided.  See :ref:`units` for details on how
756
          Toyplot handles real-world units.
757
        padding: number, string, or (number, string) tuple,  optional
758
          Distance between the numberline axis and plotted data.  Assumes CSS
759
          pixels if units aren't provided.  See :ref:`units` for details on how
760
          Toyplot handles real-world units.  Defaults to the same value as
761
          `spacing`.
762

763
        Returns
764
        -------
765
        axes: :class:`toyplot.coordinates.Cartesian`
766
        """
767
        xmin_range, xmax_range, ymin_range, ymax_range = toyplot.layout.region(
9✔
768
            0, self._width, 0, self._height, bounds=bounds, rect=rect, corner=corner, grid=grid, margin=margin)
769

770
        if x1 is None:
9✔
771
            x1 = xmin_range
9✔
772
        else:
773
            x1 = toyplot.units.convert(x1, target="px", default="px", reference=self._width)
9✔
774
            if x1 < 0:
9✔
775
                x1 = self._width + x1
9✔
776

777
        if y1 is None:
9✔
778
            y1 = 0.5 * (ymin_range + ymax_range)
9✔
779
        else:
780
            y1 = toyplot.units.convert(y1, target="px", default="px", reference=self._height)
9✔
781
            if y1 < 0:
9✔
782
                y1 = self._height + y1
9✔
783

784
        if x2 is None:
9✔
785
            x2 = xmax_range
9✔
786
        else:
787
            x2 = toyplot.units.convert(x2, target="px", default="px", reference=self._width)
9✔
788
            if x2 < 0:
9✔
789
                x2 = self._width + x2
9✔
790

791
        if y2 is None:
9✔
792
            y2 = 0.5 * (ymin_range + ymax_range)
9✔
793
        else:
794
            y2 = toyplot.units.convert(y2, target="px", default="px", reference=self._height)
9✔
795
            if y2 < 0:
9✔
796
                y2 = self._height + y2
9✔
797

798
        if spacing is None:
9✔
799
            spacing = 20
9✔
800

801
        if padding is None:
9✔
802
            padding = spacing
9✔
803

804
        axes = toyplot.coordinates.Numberline(
9✔
805
            label=label,
806
            max=max,
807
            min=min,
808
            padding=padding,
809
            palette=palette,
810
            scenegraph=self._scenegraph,
811
            scale=scale,
812
            show=show,
813
            spacing=spacing,
814
            ticklocator=ticklocator,
815
            x1=x1,
816
            x2=x2,
817
            y1=y1,
818
            y2=y2,
819
            )
820

821
        self._scenegraph.add_edge(self, "render", axes)
9✔
822

823
        return axes
9✔
824

825
    def table(
9✔
826
            self,
827
            data=None,
828
            rows=None,
829
            columns=None,
830
            annotation=False,
831
            bounds=None,
832
            brows=None,
833
            corner=None,
834
            filename=None,
835
            grid=None,
836
            label=None,
837
            lcolumns=None,
838
            margin=50,
839
            rcolumns=None,
840
            rect=None,
841
            trows=None,
842
        ):
843
        """Add a set of table axes to the canvas.
844

845
        Parameters
846
        ----------
847

848
        Returns
849
        -------
850
        axes: :class:`toyplot.coordinates.Table`
851
        """
852
        if data is not None:
9✔
853
            data = toyplot.data.Table(data)
9✔
854
            rows = data.shape[0] if rows is None else max(rows, data.shape[0])
9✔
855
            columns = data.shape[1] if columns is None else max(
9✔
856
                columns, data.shape[1])
857
            if trows is None:
9✔
858
                trows = 1
9✔
859
        if rows is None or columns is None: # pragma: no cover
860
            raise ValueError("You must specify data, or rows and columns.")
861
        if trows is None:
9✔
862
            trows = 0
9✔
863
        if brows is None:
9✔
864
            brows = 0
9✔
865
        if lcolumns is None:
9✔
866
            lcolumns = 0
9✔
867
        if rcolumns is None:
9✔
868
            rcolumns = 0
9✔
869

870
        xmin_range, xmax_range, ymin_range, ymax_range = toyplot.layout.region(
9✔
871
            0, self._width, 0, self._height,
872
            bounds=bounds,
873
            rect=rect,
874
            corner=corner,
875
            grid=grid,
876
            margin=margin,
877
            )
878
        table = toyplot.coordinates.Table(
9✔
879
            annotation=annotation,
880
            brows=brows,
881
            columns=columns,
882
            filename=filename,
883
            label=label,
884
            lcolumns=lcolumns,
885
            scenegraph=self._scenegraph,
886
            rcolumns=rcolumns,
887
            rows=rows,
888
            trows=trows,
889
            xmax_range=xmax_range,
890
            xmin_range=xmin_range,
891
            ymax_range=ymax_range,
892
            ymin_range=ymin_range,
893
            )
894

895
        if data is not None:
9✔
896
            for j, (key, column) in enumerate(data.items()):
9✔
897
                if trows:
9✔
898
                    table.top.cell[trows - 1, j].data = key
9✔
899
                for i, (value, mask) in enumerate(zip(column, numpy.ma.getmaskarray(column))):
9✔
900
                    if not mask:
9✔
901
                        table.body.cell[i, j].data = value
9✔
902
                if issubclass(column._data.dtype.type, numpy.floating):
9✔
903
                    if trows:
9✔
904
                        table.top.cell[0, j].align = "center"
9✔
905
                    table.body.cell[:, j].format = toyplot.format.FloatFormatter()
9✔
906
                    table.body.cell[:, j].align = "separator"
9✔
907
                elif issubclass(column._data.dtype.type, numpy.character):
9✔
908
                    table.cells.cell[:, j].align = "left"
9✔
909
                elif issubclass(column._data.dtype.type, numpy.integer):
9✔
910
                    table.cells.cell[:, j].align = "right"
9✔
911

912
            if trows:
9✔
913
                # Format top cells for use as a header
914
                table.top.cells.lstyle = {"font-weight": "bold"}
9✔
915
                # Enable a single horizontal line between top and body.
916
                table.cells.grid.hlines[trows] = "single"
9✔
917

918
        self._scenegraph.add_edge(self, "render", table)
9✔
919

920
        return table
9✔
921

922
    def image(
9✔
923
            self,
924
            data,
925
            bounds=None,
926
            corner=None,
927
            grid=None,
928
            margin=50,
929
            rect=None,
930
        ):
931
        """Add an image to the canvas.
932

933
        Parameters
934
        ----------
935
        data: image, or (image, colormap) tuple
936
        bounds: (xmin, xmax, ymin, ymax) tuple, optional
937
          Use the bounds property to position / size the image by specifying the
938
          position of each of its boundaries.  Assumes CSS pixels if units
939
          aren't provided, and supports all units described in :ref:`units`,
940
          including percentage of the canvas width / height.
941
        rect: (x, y, width, height) tuple, optional
942
          Use the rect property to position / size the image by specifying its
943
          upper-left-hand corner, width, and height.  Assumes CSS pixels if
944
          units aren't provided, and supports all units described in
945
          :ref:`units`, including percentage of the canvas width / height.
946
        corner: (corner, inset, width, height) tuple, optional
947
          Use the corner property to position / size the image by specifying its
948
          width and height, plus an inset from a corner of the canvas.  Allowed
949
          corner values are "top-left", "top", "top-right", "right",
950
          "bottom-right", "bottom", "bottom-left", and "left".  The width and
951
          height may be specified using absolute units as described in
952
          :ref:`units`, or as a percentage of the canvas width / height.  The
953
          inset only supports absolute drawing units.  All units default to CSS
954
          pixels if unspecified.
955
        grid: (rows, columns, index) tuple, or (rows, columns, i, j) tuple, or (rows, columns, i, rowspan, j, columnspan) tuple, optional
956
          Use the grid property to position / size the image using a collection of
957
          grid cells filling the canvas.  Specify the number of rows and columns in
958
          the grid, then specify either a zero-based cell index (which runs in
959
          left-ot-right, top-to-bottom order), a pair of i, j cell coordinates, or
960
          a set of i, column-span, j, row-span coordinates so the legend can cover
961
          more than one cell.
962
        margin: size of the margin around grid cells, optional
963
          Specifies the amount of empty space to leave between grid cells When using the
964
          `grid` parameter for positioning.  Assumes CSS pixels by default, and supports
965
          all of the absolute units described in :ref:`units`.
966
        """
967
        colormap = None
9✔
968
        if isinstance(data, tuple):
9✔
969
            data, colormap = data
9✔
970
            if not isinstance(colormap, toyplot.color.Map):
9✔
971
                raise ValueError("Expected toyplot.color.Map, received %s." % colormap) # pragma: no cover
972
            data = numpy.atleast_3d(data)
9✔
973
            if data.shape[2] != 1:
9✔
974
                raise ValueError("Expected an image with one channel.") # pragma: no cover
975
            data = colormap.colors(data)
9✔
976

977
        xmin_range, xmax_range, ymin_range, ymax_range = toyplot.layout.region(
9✔
978
            0, self._width, 0, self._height,
979
            bounds=bounds,
980
            rect=rect,
981
            corner=corner,
982
            grid=grid,
983
            margin=margin,
984
            )
985

986
        mark = toyplot.mark.Image(
9✔
987
            xmin_range,
988
            xmax_range,
989
            ymin_range,
990
            ymax_range,
991
            data=data,
992
            )
993

994
        self._scenegraph.add_edge(self, "render", mark)
9✔
995

996
        return mark
9✔
997

998
    def text(
9✔
999
            self,
1000
            x,
1001
            y,
1002
            text,
1003
            angle=0.0,
1004
            fill=None,
1005
            opacity=1.0,
1006
            title=None,
1007
            style=None):
1008
        """Add text to the canvas.
1009

1010
        Parameters
1011
        ----------
1012
        x, y: float
1013
          Coordinates of the text anchor in canvas drawing units.  Note that canvas
1014
          Y coordinates increase from top-to-bottom.
1015
        text: string
1016
          The text to be displayed.
1017
        title: string, optional
1018
          Human-readable title for the mark.  The SVG / HTML backends render the
1019
          title as a tooltip.
1020
        style: dict, optional
1021
          Collection of CSS styles to apply to the mark.  See
1022
          :class:`toyplot.mark.Text` for a list of useful styles.
1023

1024
        Returns
1025
        -------
1026
        text: :class:`toyplot.mark.Text`
1027
        """
1028
        table = toyplot.data.Table()
9✔
1029
        table["x"] = toyplot.require.scalar_vector(x)
9✔
1030
        table["y"] = toyplot.require.scalar_vector(y, table.shape[0])
9✔
1031
        table["text"] = toyplot.broadcast.pyobject(text, table.shape[0])
9✔
1032
        table["angle"] = toyplot.broadcast.scalar(angle, table.shape[0])
9✔
1033
        table["fill"] = toyplot.broadcast.pyobject(fill, table.shape[0])
9✔
1034
        table["toyplot:fill"] = toyplot.color.broadcast(
9✔
1035
            colors=fill,
1036
            shape=(table.shape[0],),
1037
            default=toyplot.color.black,
1038
            )
1039
        table["opacity"] = toyplot.broadcast.scalar(opacity, table.shape[0])
9✔
1040
        table["title"] = toyplot.broadcast.pyobject(title, table.shape[0])
9✔
1041
        style = toyplot.style.require(style, allowed=toyplot.style.allowed.text)
9✔
1042

1043
        mark = toyplot.mark.Text(
9✔
1044
            coordinate_axes=["x", "y"],
1045
            table=table,
1046
            coordinates=["x", "y"],
1047
            text=["text"],
1048
            angle=["angle"],
1049
            fill=["toyplot:fill"],
1050
            opacity=["opacity"],
1051
            title=["title"],
1052
            style=style,
1053
            annotation=True,
1054
            filename=None,
1055
            )
1056

1057
        self._scenegraph.add_edge(self, "render", mark)
9✔
1058

1059
        return mark
9✔
1060

1061
    def _point_scale(self, width=None, height=None, scale=None):
9✔
1062
        """Return a scale factor to convert this canvas to a target width or height in points."""
1063
        if numpy.count_nonzero(
9✔
1064
                [width is not None, height is not None, scale is not None]) > 1:
1065
            raise ValueError("Specify only one of width, height, or scale.") # pragma: no cover
1066

1067
        if width is not None:
9✔
1068
            scale = toyplot.units.convert(
9✔
1069
                width, "pt") / toyplot.units.convert((self._width, "px"), "pt")
1070
        elif height is not None:
9✔
1071
            scale = toyplot.units.convert(
9✔
1072
                height, "pt") / toyplot.units.convert((self._height, "px"), "pt")
1073
        elif scale is None:
9✔
1074
            scale = 1.0
9✔
1075
        scale *= 72.0 / 96.0
9✔
1076
        return scale
9✔
1077

1078
    @staticmethod
1079
    def _ipython_post_execute():  # pragma: no cover
1080
        try:
1081
            import IPython.display
1082
            for canvas, autoformat in Canvas._autorender:
1083
                if autoformat == "html":
1084
                    IPython.display.display_html(canvas)
1085
                elif autoformat == "png":
1086
                    IPython.display.display_png(canvas)
1087
        except:
1088
            pass
1089

1090
    @staticmethod
1091
    def _ipython_register():  # pragma: no cover
1092
        try:
1093
            import IPython
1094
            if IPython.get_ipython():
1095
                IPython.get_ipython().events.register(
1096
                    "post_execute", Canvas._ipython_post_execute)
1097
        except:
1098
            pass
1099

1100
Canvas._autorender = []
9✔
1101
Canvas._ipython_register()
9✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc