Commit 43e910f0 authored by Michael Droettboom's avatar Michael Droettboom

Support downloading of plot files

parent 77266efd
......@@ -191,7 +191,6 @@ root/.built: \
( \
cd root/lib/python$(PYMINOR); \
rm -fr `cat ../../../remove_modules.txt`; \
rm encodings/cp*.py; \
rm encodings/mac_*.py; \
find . -name "*.wasm.pre" -type f -delete ; \
find -type d -name __pycache__ -prune -exec rm -rf {} \; \
......
diff --git a/src/_path_wrapper.cpp b/src/_path_wrapper.cpp
index 354097557..2fd90adb9 100644
--- a/src/_path_wrapper.cpp
+++ b/src/_path_wrapper.cpp
@@ -437,23 +437,25 @@ static PyObject *Py_affine_transform(PyObject *self, PyObject *args, PyObject *k
return NULL;
}
- try {
- numpy::array_view<double, 2> vertices(vertices_obj);
+ PyArrayObject* vertices_arr = (PyArrayObject *)PyArray_ContiguousFromAny(vertices_obj, NPY_DOUBLE, 1, 2);
+ if (vertices_arr == NULL) {
+ return NULL;
+ }
+
+ if (PyArray_NDIM(vertices_arr) == 2) {
+ numpy::array_view<double, 2> vertices(vertices_arr);
npy_intp dims[] = { (npy_intp)vertices.size(), 2 };
numpy::array_view<double, 2> result(dims);
CALL_CPP("affine_transform", (affine_transform_2d(vertices, trans, result)));
+ Py_DECREF(vertices_arr);
+ return result.pyobj();
+ } else { // PyArray_NDIM(vertices_arr) == 1
+ numpy::array_view<double, 1> vertices(vertices_arr);
+ npy_intp dims[] = { (npy_intp)vertices.size() };
+ numpy::array_view<double, 1> result(dims);
+ CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result)));
+ Py_DECREF(vertices_arr);
return result.pyobj();
- } catch (py::exception &) {
- PyErr_Clear();
- try {
- numpy::array_view<double, 1> vertices(vertices_obj);
- npy_intp dims[] = { (npy_intp)vertices.size() };
- numpy::array_view<double, 1> result(dims);
- CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result)));
- return result.pyobj();
- } catch (py::exception &) {
- return NULL;
- }
}
}
diff --git a/src/numpy_cpp.h b/src/numpy_cpp.h
index 2218078ae..cddbcb659 100644
--- a/src/numpy_cpp.h
+++ b/src/numpy_cpp.h
@@ -398,6 +398,15 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
m_strides = strides;
}
+ array_view(PyArrayObject *arr)
+ {
+ m_arr = arr;
+ Py_XINCREF(arr);
+ m_shape = PyArray_DIMS(m_arr);
+ m_strides = PyArray_STRIDES(m_arr);
+ m_data = (char *)PyArray_BYTES(m_arr);
+ }
+
array_view(npy_intp shape[ND]) : m_arr(NULL), m_shape(NULL), m_strides(NULL), m_data(NULL)
{
PyObject *arr = PyArray_SimpleNew(ND, shape, type_num_of<T>::value);
@@ -456,18 +465,18 @@ class array_view : public detail::array_view_accessors<array_view, T, ND>
m_data = NULL;
m_shape = zeros;
m_strides = zeros;
- if (PyArray_NDIM(tmp) == 0 && ND == 0) {
- m_arr = tmp;
- return 1;
- }
+ if (PyArray_NDIM(tmp) == 0 && ND == 0) {
+ m_arr = tmp;
+ return 1;
+ }
}
- if (PyArray_NDIM(tmp) != ND) {
- PyErr_Format(PyExc_ValueError,
- "Expected %d-dimensional array, got %d",
- ND,
- PyArray_NDIM(tmp));
- Py_DECREF(tmp);
- return 0;
+ if (PyArray_NDIM(tmp) != ND) {
+ PyErr_Format(PyExc_ValueError,
+ "Expected %d-dimensional array, got %d",
+ ND,
+ PyArray_NDIM(tmp));
+ Py_DECREF(tmp);
+ return 0;
}
/* Copy some of the data to the view object for faster access */
diff --git a/src/py_converters.cpp b/src/py_converters.cpp
index cc235df2e..9119df6b6 100644
--- a/src/py_converters.cpp
+++ b/src/py_converters.cpp
@@ -158,45 +158,36 @@ int convert_rect(PyObject *rectobj, void *rectp)
rect->x2 = 0.0;
rect->y2 = 0.0;
} else {
- try
- {
- numpy::array_view<const double, 2> rect_arr(rectobj);
+ PyArrayObject *rect_arr = (PyArrayObject *)PyArray_ContiguousFromAny(
+ rectobj, NPY_DOUBLE, 1, 2);
+ if (rect_arr == NULL) {
+ return 0;
+ }
- if (rect_arr.dim(0) != 2 || rect_arr.dim(1) != 2) {
+ if (PyArray_NDIM(rect_arr) == 2) {
+ if (PyArray_DIM(rect_arr, 0) != 2 ||
+ PyArray_DIM(rect_arr, 1) != 2) {
PyErr_SetString(PyExc_ValueError, "Invalid bounding box");
+ Py_DECREF(rect_arr);
return 0;
}
- rect->x1 = rect_arr(0, 0);
- rect->y1 = rect_arr(0, 1);
- rect->x2 = rect_arr(1, 0);
- rect->y2 = rect_arr(1, 1);
- }
- catch (py::exception &)
- {
- PyErr_Clear();
-
- try
- {
- numpy::array_view<const double, 1> rect_arr(rectobj);
-
- if (rect_arr.dim(0) != 4) {
- PyErr_SetString(PyExc_ValueError, "Invalid bounding box");
- return 0;
- }
-
- rect->x1 = rect_arr(0);
- rect->y1 = rect_arr(1);
- rect->x2 = rect_arr(2);
- rect->y2 = rect_arr(3);
- }
- catch (py::exception &)
- {
+ } else { // PyArray_NDIM(rect_arr) == 1
+ if (PyArray_DIM(rect_arr, 0) != 4) {
+ PyErr_SetString(PyExc_ValueError, "Invalid bounding box");
+ Py_DECREF(rect_arr);
return 0;
}
}
- }
+ double *buff = (double *)PyArray_DATA(rect_arr);
+ rect->x1 = buff[0];
+ rect->y1 = buff[1];
+ rect->x2 = buff[2];
+ rect->y2 = buff[3];
+
+ Py_DECREF(rect_arr);
+ }
return 1;
}
@@ -336,27 +327,26 @@ int convert_trans_affine(PyObject *obj, void *transp)
return 1;
}
- try
- {
- numpy::array_view<const double, 2> matrix(obj);
+ PyArrayObject *array = (PyArrayObject *)PyArray_ContiguousFromAny(obj, NPY_DOUBLE, 2, 2);
+ if (array == NULL) {
+ return 0;
+ }
- if (matrix.dim(0) == 3 && matrix.dim(1) == 3) {
- trans->sx = matrix(0, 0);
- trans->shx = matrix(0, 1);
- trans->tx = matrix(0, 2);
+ if (PyArray_DIM(array, 0) == 3 && PyArray_DIM(array, 1) == 3) {
+ double *buffer = (double *)PyArray_DATA(array);
+ trans->sx = buffer[0];
+ trans->shx = buffer[1];
+ trans->tx = buffer[2];
- trans->shy = matrix(1, 0);
- trans->sy = matrix(1, 1);
- trans->ty = matrix(1, 2);
+ trans->shy = buffer[3];
+ trans->sy = buffer[4];
+ trans->ty = buffer[5];
- return 1;
- }
- }
- catch (py::exception &)
- {
- return 0;
+ Py_DECREF(array);
+ return 1;
}
+ Py_DECREF(array);
PyErr_SetString(PyExc_ValueError, "Invalid affine transformation matrix");
return 0;
}
import base64
import io
from matplotlib.backends import backend_agg
from matplotlib.backend_bases import _Backend
from matplotlib import backend_bases, _png
......@@ -6,7 +9,6 @@ from js import iodide
from js import document
from js import window
from js import ImageData
from js import Uint8ClampedArray
class FigureCanvasWasm(backend_agg.FigureCanvasAgg):
......@@ -167,7 +169,7 @@ _FONTAWESOME_ICONS = {
'forward': 'fa-arrow-right',
'zoom_to_rect': 'fa-search-plus',
'move': 'fa-arrows',
'download': 'download',
'download': 'fa-download',
None: None,
}
......@@ -178,21 +180,57 @@ class NavigationToolbar2Wasm(backend_bases.NavigationToolbar2):
def get_element(self):
div = document.createElement('span')
def add_spacer():
span = document.createElement('span')
span.style.minWidth = '16px'
span.textContent = '\u00a0'
div.appendChild(span)
for text, tooltip_text, image_file, name_of_method in self.toolitems:
if image_file in _FONTAWESOME_ICONS:
if image_file is None:
span = document.createElement('span')
span.style.minWidth = 16
span.style.textContent = ' '
div.appendChild(span)
add_spacer()
else:
button = document.createElement('button')
button.classList.add('fa')
button.classList.add(_FONTAWESOME_ICONS[image_file])
button.addEventListener('click', getattr(self, name_of_method))
div.appendChild(button)
for filetype in ('png', 'svg', 'pdf'):
button = document.createElement('button')
button.classList.add('fa')
button.textContent = filetype
button.addEventListener('click', getattr(self, 'download_' + filetype))
div.appendChild(button)
return div
def download_png(self, event):
self.download('png', 'image/png')
def download_svg(self, event):
self.download('svg', 'image/svg+xml')
def download_pdf(self, event):
self.download('pdf', 'application/pdf')
def download(self, format, mimetype):
element = document.createElement('a')
data = io.BytesIO()
try:
self.canvas.figure.savefig(data, format=format)
except Exception as e:
raise
element.setAttribute('href', 'data:{};base64,{}'.format(
mimetype, base64.b64encode(data.getvalue()).decode('ascii')))
element.setAttribute('download', 'plot.{}'.format(format))
element.style.display = 'none'
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
def set_message(self, message):
self.canvas.set_message(message)
......
......@@ -7,3 +7,15 @@ def test_matplotlib(selenium):
)
assert img.startswith('data:image/png;base64,')
assert len(img) == 26766
def test_svg(selenium):
selenium.load_package("matplotlib")
selenium.run("from matplotlib import pyplot as plt")
selenium.run("x = plt.plot([1,2,3])")
selenium.run("import io")
selenium.run("fd = io.BytesIO()")
selenium.run("plt.savefig(fd, format='svg')")
content = selenium.run("fd.getvalue().decode('utf8')")
assert len(content) == 15752
assert content.startswith("<?xml")
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment