From b4e2b8aacb626986a1f0f1bf887e1b615a84f204 Mon Sep 17 00:00:00 2001 From: mattip Date: Tue, 10 Oct 2017 14:50:24 +1100 Subject: [PATCH 01/10] simple plot works --- lib/matplotlib/backends/_tkagg.py | 61 ++++++++++++++++++++++++++++++ lib/matplotlib/backends/tkagg.py | 62 +++++++++++++++++++++---------- 2 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 lib/matplotlib/backends/_tkagg.py diff --git a/lib/matplotlib/backends/_tkagg.py b/lib/matplotlib/backends/_tkagg.py new file mode 100644 index 000000000000..64a66df4ec78 --- /dev/null +++ b/lib/matplotlib/backends/_tkagg.py @@ -0,0 +1,61 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) +from _tkinter.tklib_cffi import ffi as tkffi, lib as tklib +from _tkinter.tclobj import FromTclStringNDArray +import numpy as np + +app = None + +def PyAggImagePhoto(photoimage, data, mode): + interp = PyAggImagePhoto.interp + if not tklib.Tk_MainWindow(interp): + raise _tkinter.TclError("this isn't a Tk application") + photo = tklib.Tk_FindPhoto(interp, photoimage) + if not photo: + tklib.Tcl_AppendResult(interp, "destination photo must exist", 0) + return tklib.TCL_ERROR + img = FromTclStringNDArray(data) + has_bbox = False + pBlock = tkffi.new('Tk_PhotoImageBlock[1]') + block = pBlock[0] + block.pixelSize = 1 + if mode == 0: + block.offset[0] = block.offset[1] = block.offset[2] = 0 + nval = 1 + else: + block.offset[0] = 0 + block.offset[1] = 1 + block.offset[2] = 2 + if mode == 1: + block.offset[3] = 0 + block.pixelSize = 3 + nval = 3 + else: + block.offset[3] = 3 + block.pixelSize = 4 + if has_bbox: + block.width = destwidth + block.height = destheight + block.pitch = deststride + block.pixelPtr = destbuffer + + tklib.Tk_PhotoPutBlock_NoComposite(photo, pBlock, destx, desty, + destwidth, destheight); + + else: + block.width = shape[1] + block.height = shape[0] + block.pitch = img.strides[0] + block.pixelPtr = tkffi.from_buffer(img) + + #/* Clear current contents */ + tklib.Tk_PhotoBlank(photo); + #/* Copy opaque block to photo image, and leave the rest to TK */ + tklib.Tk_PhotoPutBlock_NoComposite(photo, pBlock, 0, 0, + block.width, block.height); + return tklib.TCL_OK + +def tkinit(tk): + tk.createcommand(b"PyAggImagePhoto", PyAggImagePhoto) + PyAggImagePhoto.interp = tk.interp + diff --git a/lib/matplotlib/backends/tkagg.py b/lib/matplotlib/backends/tkagg.py index 979bc3cc24fa..7cc301666142 100644 --- a/lib/matplotlib/backends/tkagg.py +++ b/lib/matplotlib/backends/tkagg.py @@ -7,29 +7,51 @@ import numpy as np from matplotlib.backends import _tkagg +from platform import python_implementation -def blit(photoimage, aggimage, bbox=None, colormode=1): - tk = photoimage.tk - - if bbox is not None: - bbox_array = bbox.__array__() - else: - bbox_array = None - data = np.asarray(aggimage) - try: - tk.call( - "PyAggImagePhoto", photoimage, - id(data), colormode, id(bbox_array)) - except Tk.TclError: +if python_implementation() == 'PyPy': + def blit(photoimage, aggimage, bbox=None, colormode=1): + tk = photoimage.tk + + if bbox is not None: + bbox_array = bbox.__array__() + else: + bbox_array = None + data = np.asarray(aggimage) + try: + tk.call( + "PyAggImagePhoto", photoimage, + data, colormode, bbox_array) + except Tk.TclError: + _tkagg.tkinit(tk) + tk.call("PyAggImagePhoto", photoimage, + data, colormode, bbox_array) + +else: + def blit(photoimage, aggimage, bbox=None, colormode=1): + tk = photoimage.tk + + if bbox is not None: + bbox_array = bbox.__array__() + else: + bbox_array = None + data = np.asarray(aggimage) try: + import pdb;pdb.set_trace() + tk.call( + "PyAggImagePhoto", photoimage, + id(data), colormode, id(bbox_array)) + except Tk.TclError: try: - _tkagg.tkinit(tk.interpaddr(), 1) - except AttributeError: - _tkagg.tkinit(id(tk), 0) - tk.call("PyAggImagePhoto", photoimage, - id(data), colormode, id(bbox_array)) - except (ImportError, AttributeError, Tk.TclError): - raise + import pdb;pdb.set_trace() + try: + _tkagg.tkinit(tk.interpaddr(), 1) + except AttributeError: + _tkagg.tkinit(id(tk), 0) + tk.call("PyAggImagePhoto", photoimage, + id(data), colormode, id(bbox_array)) + except (ImportError, AttributeError, Tk.TclError): + raise def test(aggimage): import time From 68492fbd6926cbb55dae4dc12519a16382eaa8d7 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 16 Oct 2017 21:25:58 +0300 Subject: [PATCH 02/10] flesh out bbox handling, fix mpl_PyFile_Dup for PyPy which is like python3 --- lib/matplotlib/backends/_tkagg.py | 41 ++++++++++++++++++++++--------- lib/matplotlib/backends/tkagg.py | 2 -- src/file_compat.h | 2 +- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/backends/_tkagg.py b/lib/matplotlib/backends/_tkagg.py index 64a66df4ec78..3c87f35f902e 100644 --- a/lib/matplotlib/backends/_tkagg.py +++ b/lib/matplotlib/backends/_tkagg.py @@ -6,23 +6,40 @@ app = None -def PyAggImagePhoto(photoimage, data, mode): +def PyAggImagePhoto(photoimage, data_as_str, mode, bbox_as_str=None): interp = PyAggImagePhoto.interp if not tklib.Tk_MainWindow(interp): raise _tkinter.TclError("this isn't a Tk application") photo = tklib.Tk_FindPhoto(interp, photoimage) if not photo: - tklib.Tcl_AppendResult(interp, "destination photo must exist", 0) + tklib.Tcl_AppendResult(interp, "destination photo must exist", 0) return tklib.TCL_ERROR - img = FromTclStringNDArray(data) - has_bbox = False + data = FromTclStringNDArray(data_as_str) + if bbox_as_str: + try: + bbox = FromTclStringNDArray(bbox_as_str) + except: + tklib.Tcl_AppendResult(interp, "bbox not valid", 0) + return tklib.TCL_ERROR + destx = int(bbox[0, 0]) + desty = data.shape[0] - int(bbox[1, 1]) + destwidth = int(bbox[1, 0] - bbox[0, 0]) + destheight = int(bbox[1, 1] - bbox[0, 1]) + deststride = 4 * destwidth; + destbuffer = np.empty([destheight, destwidth, 4], dtype='uint8') + has_bbox = True + destbuffer[:,:,:] = data[desty:desty+destheight, destx:destx+destwidth,:] + else: + has_bbox = False + destbuffer = None + destx = desty = destwidth = destheight = deststride = 0; pBlock = tkffi.new('Tk_PhotoImageBlock[1]') block = pBlock[0] block.pixelSize = 1 if mode == 0: block.offset[0] = block.offset[1] = block.offset[2] = 0 nval = 1 - else: + else: block.offset[0] = 0 block.offset[1] = 1 block.offset[2] = 2 @@ -37,25 +54,25 @@ def PyAggImagePhoto(photoimage, data, mode): block.width = destwidth block.height = destheight block.pitch = deststride - block.pixelPtr = destbuffer + block.pixelPtr = tkffi.from_buffer(destbuffer) tklib.Tk_PhotoPutBlock_NoComposite(photo, pBlock, destx, desty, destwidth, destheight); else: - block.width = shape[1] - block.height = shape[0] - block.pitch = img.strides[0] - block.pixelPtr = tkffi.from_buffer(img) + block.width = data.shape[1] + block.height = data.shape[0] + block.pitch = data.strides[0] + block.pixelPtr = tkffi.from_buffer(data) #/* Clear current contents */ tklib.Tk_PhotoBlank(photo); #/* Copy opaque block to photo image, and leave the rest to TK */ tklib.Tk_PhotoPutBlock_NoComposite(photo, pBlock, 0, 0, block.width, block.height); - return tklib.TCL_OK + return tklib.TCL_OK def tkinit(tk): tk.createcommand(b"PyAggImagePhoto", PyAggImagePhoto) PyAggImagePhoto.interp = tk.interp - + diff --git a/lib/matplotlib/backends/tkagg.py b/lib/matplotlib/backends/tkagg.py index 7cc301666142..12fb17b14189 100644 --- a/lib/matplotlib/backends/tkagg.py +++ b/lib/matplotlib/backends/tkagg.py @@ -37,13 +37,11 @@ def blit(photoimage, aggimage, bbox=None, colormode=1): bbox_array = None data = np.asarray(aggimage) try: - import pdb;pdb.set_trace() tk.call( "PyAggImagePhoto", photoimage, id(data), colormode, id(bbox_array)) except Tk.TclError: try: - import pdb;pdb.set_trace() try: _tkagg.tkinit(tk.interpaddr(), 1) except AttributeError: diff --git a/src/file_compat.h b/src/file_compat.h index a8f04eaeddca..42fdd162288c 100644 --- a/src/file_compat.h +++ b/src/file_compat.h @@ -48,7 +48,7 @@ extern "C" { /* * PyFile_* compatibility */ -#if PY3K +#if defined(PY3K) | defined(PYPY_VERSION) /* * Get a FILE* handle to the file represented by the Python object From 8e80a20381139d4d84d112e30056751fadb49d48 Mon Sep 17 00:00:00 2001 From: mattip Date: Fri, 3 Nov 2017 16:51:21 +0200 Subject: [PATCH 03/10] change interface in PyAggImagePhoto to avoid id() --- lib/matplotlib/backends/tkagg.py | 63 +++++++++---------------- setupext.py | 2 - src/_tkagg.cpp | 80 ++++++++++++-------------------- 3 files changed, 52 insertions(+), 93 deletions(-) diff --git a/lib/matplotlib/backends/tkagg.py b/lib/matplotlib/backends/tkagg.py index 12fb17b14189..f563c5877404 100644 --- a/lib/matplotlib/backends/tkagg.py +++ b/lib/matplotlib/backends/tkagg.py @@ -7,49 +7,32 @@ import numpy as np from matplotlib.backends import _tkagg -from platform import python_implementation -if python_implementation() == 'PyPy': - def blit(photoimage, aggimage, bbox=None, colormode=1): - tk = photoimage.tk - - if bbox is not None: - bbox_array = bbox.__array__() - else: - bbox_array = None - data = np.asarray(aggimage) - try: - tk.call( - "PyAggImagePhoto", photoimage, - data, colormode, bbox_array) - except Tk.TclError: - _tkagg.tkinit(tk) - tk.call("PyAggImagePhoto", photoimage, - data, colormode, bbox_array) - -else: - def blit(photoimage, aggimage, bbox=None, colormode=1): - tk = photoimage.tk - - if bbox is not None: - bbox_array = bbox.__array__() - else: - bbox_array = None - data = np.asarray(aggimage) +def blit(photoimage, aggimage, bbox=None, colormode=1): + tk = photoimage.tk + + if bbox is not None: + bbox_array = bbox.__array__() + # x1, x2, y1, y2 + bboxptr = (bbox_array[0, 0], bbox_array[1, 0], bbox_array[0, 1], bbox_array[1, 1]) + else: + bboxptr = 0 + data = np.asarray(aggimage) + dataptr = (data.ctypes.data, data.shape[0], data.shape[1]) + try: + tk.call( + "PyAggImagePhoto", photoimage, + dataptr, colormode, bboxptr) + except Tk.TclError: try: - tk.call( - "PyAggImagePhoto", photoimage, - id(data), colormode, id(bbox_array)) - except Tk.TclError: try: - try: - _tkagg.tkinit(tk.interpaddr(), 1) - except AttributeError: - _tkagg.tkinit(id(tk), 0) - tk.call("PyAggImagePhoto", photoimage, - id(data), colormode, id(bbox_array)) - except (ImportError, AttributeError, Tk.TclError): - raise + _tkagg.tkinit(tk.interpaddr(), 1) + except AttributeError: + _tkagg.tkinit(tk, 0) + tk.call("PyAggImagePhoto", photoimage, + dataptr, colormode, bboxptr) + except (ImportError, AttributeError, Tk.TclError): + raise def test(aggimage): import time diff --git a/setupext.py b/setupext.py index 2856545fbc59..72996259e368 100644 --- a/setupext.py +++ b/setupext.py @@ -1513,13 +1513,11 @@ def runtime_check(self): def get_extension(self): sources = [ - 'src/py_converters.cpp', 'src/_tkagg.cpp' ] ext = make_extension('matplotlib.backends._tkagg', sources) self.add_flags(ext) - Numpy().add_flags(ext) LibAgg().add_flags(ext, add_sources=False) return ext diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 830831400d50..67eb8744142d 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -13,16 +13,15 @@ #include #include -#include "py_converters.h" - // Include our own excerpts from the Tcl / Tk headers #include "_tkmini.h" #if defined(_MSC_VER) -# define SIZE_T_FORMAT "%Iu" +# define IMG_FORMAT "%Iu %d %d" #else -# define SIZE_T_FORMAT "%zu" +# define IMG_FORMAT "%zu %d %d" #endif +# define BBOX_FORMAT "%f %f %f %f" typedef struct { @@ -44,16 +43,15 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int { Tk_PhotoHandle photo; Tk_PhotoImageBlock block; - PyObject *bufferobj; // vars for blitting - PyObject *bboxo; - size_t aggl, bboxl; + size_t pdata; + int wdata, hdata; + float x1, x2, y1, y2; bool has_bbox; - uint8_t *destbuffer; + uint8_t *destbuffer, *buffer; int destx, desty, destwidth, destheight, deststride; - //unsigned long tmp_ptr; long mode; long nval; @@ -73,24 +71,14 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int TCL_APPEND_RESULT(interp, "destination photo must exist", (char *)NULL); return TCL_ERROR; } - /* get array (or object that can be converted to array) pointer */ - if (sscanf(argv[2], SIZE_T_FORMAT, &aggl) != 1) { - TCL_APPEND_RESULT(interp, "error casting pointer", (char *)NULL); - return TCL_ERROR; - } - bufferobj = (PyObject *)aggl; - - numpy::array_view buffer; - try { - buffer = numpy::array_view(bufferobj); - } catch (...) { - TCL_APPEND_RESULT(interp, "buffer is of wrong type", (char *)NULL); - PyErr_Clear(); + /* get buffer from str which is "ptr height width" */ + if (sscanf(argv[2], IMG_FORMAT, &pdata, &hdata, &wdata) != 3) { + TCL_APPEND_RESULT(interp, + "error reading data, expected ptr height width", + (char *)NULL); return TCL_ERROR; } - int srcheight = buffer.dim(0); - - /* XXX insert aggRenderer type check */ + buffer = (uint8_t*)pdata; /* get array mode (0=mono, 1=rgb, 2=rgba) */ mode = atol(argv[3]); @@ -100,27 +88,22 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int } /* check for bbox/blitting */ - if (sscanf(argv[4], SIZE_T_FORMAT, &bboxl) != 1) { - TCL_APPEND_RESULT(interp, "error casting pointer", (char *)NULL); - return TCL_ERROR; - } - bboxo = (PyObject *)bboxl; - - if (bboxo != NULL && bboxo != Py_None) { - agg::rect_d rect; - if (!convert_rect(bboxo, &rect)) { - return TCL_ERROR; - } - + if (sscanf(argv[4], BBOX_FORMAT, &x1, &x2, &y1, &y2) == 4) { has_bbox = true; + } + else { + has_bbox = false; + } - destx = (int)rect.x1; - desty = srcheight - (int)rect.y2; - destwidth = (int)(rect.x2 - rect.x1); - destheight = (int)(rect.y2 - rect.y1); + if (has_bbox) { + int srcstride = wdata * 4; + destx = x1; + desty = hdata - y2; + destwidth = x2 - x1; + destheight = y2 - y1; deststride = 4 * destwidth; - destbuffer = new agg::int8u[deststride * destheight]; + destbuffer = new uint8_t[deststride * destheight]; if (destbuffer == NULL) { TCL_APPEND_RESULT(interp, "could not allocate memory", (char *)NULL); return TCL_ERROR; @@ -128,11 +111,10 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int for (int i = 0; i < destheight; ++i) { memcpy(destbuffer + (deststride * i), - &buffer(i + desty, destx, 0), + &buffer[(i + desty) * srcstride + (destx * 4)], deststride); } } else { - has_bbox = false; destbuffer = NULL; destx = desty = destwidth = destheight = deststride = 0; } @@ -168,10 +150,10 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int delete[] destbuffer; } else { - block.width = buffer.dim(1); - block.height = buffer.dim(0); + block.width = wdata; + block.height = hdata; block.pitch = (int)block.width * nval; - block.pixelPtr = buffer.data(); + block.pixelPtr = buffer; /* Clear current contents */ TK_PHOTO_BLANK(photo); @@ -457,15 +439,11 @@ PyMODINIT_FUNC PyInit__tkagg(void) m = PyModule_Create(&_tkagg_module); - import_array(); - return (load_tkinter_funcs() == 0) ? m : NULL; } #else PyMODINIT_FUNC init_tkagg(void) { - import_array(); - Py_InitModule("_tkagg", functions); load_tkinter_funcs(); From 512e4d86ec5083ed74c750e2310abda72b8f9bbb Mon Sep 17 00:00:00 2001 From: mattip Date: Sun, 5 Nov 2017 21:00:31 +0200 Subject: [PATCH 04/10] search for cffi c-extension module to find c-functions --- lib/matplotlib/backends/_tkagg.py | 78 ------------------------------- lib/matplotlib/backends/tkagg.py | 16 +++---- src/_tkagg.cpp | 30 +++++++++--- 3 files changed, 31 insertions(+), 93 deletions(-) delete mode 100644 lib/matplotlib/backends/_tkagg.py diff --git a/lib/matplotlib/backends/_tkagg.py b/lib/matplotlib/backends/_tkagg.py deleted file mode 100644 index 3c87f35f902e..000000000000 --- a/lib/matplotlib/backends/_tkagg.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) -from _tkinter.tklib_cffi import ffi as tkffi, lib as tklib -from _tkinter.tclobj import FromTclStringNDArray -import numpy as np - -app = None - -def PyAggImagePhoto(photoimage, data_as_str, mode, bbox_as_str=None): - interp = PyAggImagePhoto.interp - if not tklib.Tk_MainWindow(interp): - raise _tkinter.TclError("this isn't a Tk application") - photo = tklib.Tk_FindPhoto(interp, photoimage) - if not photo: - tklib.Tcl_AppendResult(interp, "destination photo must exist", 0) - return tklib.TCL_ERROR - data = FromTclStringNDArray(data_as_str) - if bbox_as_str: - try: - bbox = FromTclStringNDArray(bbox_as_str) - except: - tklib.Tcl_AppendResult(interp, "bbox not valid", 0) - return tklib.TCL_ERROR - destx = int(bbox[0, 0]) - desty = data.shape[0] - int(bbox[1, 1]) - destwidth = int(bbox[1, 0] - bbox[0, 0]) - destheight = int(bbox[1, 1] - bbox[0, 1]) - deststride = 4 * destwidth; - destbuffer = np.empty([destheight, destwidth, 4], dtype='uint8') - has_bbox = True - destbuffer[:,:,:] = data[desty:desty+destheight, destx:destx+destwidth,:] - else: - has_bbox = False - destbuffer = None - destx = desty = destwidth = destheight = deststride = 0; - pBlock = tkffi.new('Tk_PhotoImageBlock[1]') - block = pBlock[0] - block.pixelSize = 1 - if mode == 0: - block.offset[0] = block.offset[1] = block.offset[2] = 0 - nval = 1 - else: - block.offset[0] = 0 - block.offset[1] = 1 - block.offset[2] = 2 - if mode == 1: - block.offset[3] = 0 - block.pixelSize = 3 - nval = 3 - else: - block.offset[3] = 3 - block.pixelSize = 4 - if has_bbox: - block.width = destwidth - block.height = destheight - block.pitch = deststride - block.pixelPtr = tkffi.from_buffer(destbuffer) - - tklib.Tk_PhotoPutBlock_NoComposite(photo, pBlock, destx, desty, - destwidth, destheight); - - else: - block.width = data.shape[1] - block.height = data.shape[0] - block.pitch = data.strides[0] - block.pixelPtr = tkffi.from_buffer(data) - - #/* Clear current contents */ - tklib.Tk_PhotoBlank(photo); - #/* Copy opaque block to photo image, and leave the rest to TK */ - tklib.Tk_PhotoPutBlock_NoComposite(photo, pBlock, 0, 0, - block.width, block.height); - return tklib.TCL_OK - -def tkinit(tk): - tk.createcommand(b"PyAggImagePhoto", PyAggImagePhoto) - PyAggImagePhoto.interp = tk.interp - diff --git a/lib/matplotlib/backends/tkagg.py b/lib/matplotlib/backends/tkagg.py index f563c5877404..c0e457375718 100644 --- a/lib/matplotlib/backends/tkagg.py +++ b/lib/matplotlib/backends/tkagg.py @@ -24,15 +24,13 @@ def blit(photoimage, aggimage, bbox=None, colormode=1): "PyAggImagePhoto", photoimage, dataptr, colormode, bboxptr) except Tk.TclError: - try: - try: - _tkagg.tkinit(tk.interpaddr(), 1) - except AttributeError: - _tkagg.tkinit(tk, 0) - tk.call("PyAggImagePhoto", photoimage, - dataptr, colormode, bboxptr) - except (ImportError, AttributeError, Tk.TclError): - raise + if hasattr(tk, 'interpaddr'): + _tkagg.tkinit(tk.interpaddr(), 1) + else: + # very old python? + _tkagg.tkinit(tk, 0) + tk.call("PyAggImagePhoto", photoimage, + dataptr, colormode, bboxptr) def test(aggimage): import time diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 67eb8744142d..52129abbf169 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -181,7 +181,7 @@ static PyObject *_tkinit(PyObject *self, PyObject *args) } else { /* Do it the hard way. This will break if the TkappObject layout changes */ - app = (TkappObject *)PyLong_AsVoidPtr(arg); + app = (TkappObject *)arg; interp = app->interp; } @@ -320,7 +320,7 @@ int load_tkinter_funcs(void) * tkinter uses these symbols, and the symbols are therefore visible in the * tkinter dynamic library (module). */ -#if PY3K +#if PY_MAJOR_VERSION >= 3 #define TKINTER_PKG "tkinter" #define TKINTER_MOD "_tkinter" // From module __file__ attribute to char *string for dlopen. @@ -414,13 +414,31 @@ int load_tkinter_funcs(void) } tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY); if (tkinter_lib == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "Cannot dlopen tkinter module file"); - goto exit; + /* Perhaps it is a cffi module, like in PyPy? */ + pString = PyObject_GetAttrString(pSubmodule, "tklib_cffi"); + if (pString == NULL) { + goto fail; + } + pString = PyObject_GetAttrString(pString, "__file__"); + if (pString == NULL) { + goto fail; + } + tkinter_libname = fname2char(pString); + if (tkinter_libname == NULL) { + goto fail; + } + tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY); + } + if (tkinter_lib == NULL) { + goto fail; } ret = _func_loader(tkinter_lib); // dlclose probably safe because tkinter has been imported. dlclose(tkinter_lib); + goto exit; +fail: + PyErr_SetString(PyExc_RuntimeError, + "Cannot dlopen tkinter module file"); exit: Py_XDECREF(pModule); Py_XDECREF(pSubmodule); @@ -429,7 +447,7 @@ int load_tkinter_funcs(void) } #endif // end not Windows -#if PY3K +#if PY_MAJOR_VERSION >= 3 static PyModuleDef _tkagg_module = { PyModuleDef_HEAD_INIT, "_tkagg", "", -1, functions, NULL, NULL, NULL, NULL }; From 4c5dfaa300700fc1766841e97490fdd13f1afd32 Mon Sep 17 00:00:00 2001 From: mattip Date: Sun, 5 Nov 2017 22:02:26 +0200 Subject: [PATCH 05/10] pep8 --- lib/matplotlib/backends/tkagg.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/tkagg.py b/lib/matplotlib/backends/tkagg.py index c0e457375718..ee4fcedd2e04 100644 --- a/lib/matplotlib/backends/tkagg.py +++ b/lib/matplotlib/backends/tkagg.py @@ -14,7 +14,8 @@ def blit(photoimage, aggimage, bbox=None, colormode=1): if bbox is not None: bbox_array = bbox.__array__() # x1, x2, y1, y2 - bboxptr = (bbox_array[0, 0], bbox_array[1, 0], bbox_array[0, 1], bbox_array[1, 1]) + bboxptr = (bbox_array[0, 0], bbox_array[1, 0], + bbox_array[0, 1], bbox_array[1, 1]) else: bboxptr = 0 data = np.asarray(aggimage) From c2a7143e2d8b251e2f61ab2a325bed3dbfa1db0b Mon Sep 17 00:00:00 2001 From: mattip Date: Fri, 24 Nov 2017 00:21:32 +0200 Subject: [PATCH 06/10] fixes from review --- src/_tkagg.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 52129abbf169..3c0ad3e85233 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -21,7 +21,7 @@ #else # define IMG_FORMAT "%zu %d %d" #endif -# define BBOX_FORMAT "%f %f %f %f" +#define BBOX_FORMAT "%f %f %f %f" typedef struct { @@ -47,7 +47,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int // vars for blitting size_t pdata; - int wdata, hdata; + int wdata, hdata, bbox_parse; float x1, x2, y1, y2; bool has_bbox; uint8_t *destbuffer, *buffer; @@ -88,11 +88,15 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int } /* check for bbox/blitting */ - if (sscanf(argv[4], BBOX_FORMAT, &x1, &x2, &y1, &y2) == 4) { + bbox_parse = sscanf(argv[4], BBOX_FORMAT, &x1, &x2, &y1, &y2); + if (bbox_parse == 4) { has_bbox = true; } - else { + else if ((bbox_parse == 1) && (x1 == 0)){ has_bbox = false; + } else { + TCL_APPEND_RESULT(interp, "illegal bbox", (char *)NULL); + return TCL_ERROR; } if (has_bbox) { From 09576fa7cebbc335a3e810b7d8d1820a5dda3293 Mon Sep 17 00:00:00 2001 From: mattip Date: Fri, 24 Nov 2017 09:32:22 +0200 Subject: [PATCH 07/10] silence warnings, fix build on old MSC compilers --- src/_tkagg.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 3c0ad3e85233..1afc09d396ce 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -13,6 +13,8 @@ #include #include +#include // uint8_t on old MSC_VER + // Include our own excerpts from the Tcl / Tk headers #include "_tkmini.h" @@ -101,10 +103,10 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int if (has_bbox) { int srcstride = wdata * 4; - destx = x1; - desty = hdata - y2; - destwidth = x2 - x1; - destheight = y2 - y1; + destx = (int)x1; + desty = (int)(hdata - y2); + destwidth = (int)(x2 - x1); + destheight = (int)(y2 - y1); deststride = 4 * destwidth; destbuffer = new uint8_t[deststride * destheight]; From 596345e6ec8a33759abe21563902e0d7e9b357e7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 3 Oct 2017 10:43:15 -0700 Subject: [PATCH 08/10] Cleanup embedding_in_tk, remove embedding_in_tk2. embedding_in_tk2_sgskip is essentially a slightly simplified duplicate of embedding_in_tk_sgskip. --- .../embedding_in_tk2_sgskip.py | 49 ------------------- .../user_interfaces/embedding_in_tk_sgskip.py | 47 +++++++----------- 2 files changed, 17 insertions(+), 79 deletions(-) delete mode 100644 examples/user_interfaces/embedding_in_tk2_sgskip.py diff --git a/examples/user_interfaces/embedding_in_tk2_sgskip.py b/examples/user_interfaces/embedding_in_tk2_sgskip.py deleted file mode 100644 index ed3c5f1357a5..000000000000 --- a/examples/user_interfaces/embedding_in_tk2_sgskip.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -================ -Embedding In Tk2 -================ - -""" -import matplotlib -matplotlib.use('TkAgg') - -from numpy import arange, sin, pi -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from matplotlib.figure import Figure - -import sys -if sys.version_info[0] < 3: - import Tkinter as Tk -else: - import tkinter as Tk - - -def destroy(e): - sys.exit() - -root = Tk.Tk() -root.wm_title("Embedding in TK") - - -f = Figure(figsize=(5, 4), dpi=100) -a = f.add_subplot(111) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) - -a.plot(t, s) -a.set_title('Tk embedding') -a.set_xlabel('X axis label') -a.set_ylabel('Y label') - - -# a tk.DrawingArea -canvas = FigureCanvasTkAgg(f, master=root) -canvas.draw() -canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) - -canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) - -button = Tk.Button(master=root, text='Quit', command=sys.exit) -button.pack(side=Tk.BOTTOM) - -Tk.mainloop() diff --git a/examples/user_interfaces/embedding_in_tk_sgskip.py b/examples/user_interfaces/embedding_in_tk_sgskip.py index b4ea1669c356..c54a033893e5 100644 --- a/examples/user_interfaces/embedding_in_tk_sgskip.py +++ b/examples/user_interfaces/embedding_in_tk_sgskip.py @@ -7,34 +7,23 @@ import matplotlib matplotlib.use('TkAgg') -from numpy import arange, sin, pi -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg -# implement the default mpl key bindings +import numpy as np +from matplotlib.backends.backend_tkagg import ( + FigureCanvasTkAgg, NavigationToolbar2TkAgg) +# Implement the default Matplotlib key bindings. from matplotlib.backend_bases import key_press_handler - - from matplotlib.figure import Figure +from six.moves import tkinter as Tk -import sys -if sys.version_info[0] < 3: - import Tkinter as Tk -else: - import tkinter as Tk root = Tk.Tk() -root.wm_title("Embedding in TK") - - -f = Figure(figsize=(5, 4), dpi=100) -a = f.add_subplot(111) -t = arange(0.0, 3.0, 0.01) -s = sin(2*pi*t) +root.wm_title("Embedding in Tk") -a.plot(t, s) +fig = Figure(figsize=(5, 4), dpi=100) +t = np.arange(0, 3, .01) +fig.add_subplot(111).plot(t, 2 * np.sin(2 * np.pi * t)) - -# a tk.DrawingArea -canvas = FigureCanvasTkAgg(f, master=root) +canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.draw() canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) @@ -42,12 +31,10 @@ toolbar.update() canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) - -def on_key_event(event): - print('you pressed %s' % event.key) - key_press_handler(event, canvas, toolbar) - -canvas.mpl_connect('key_press_event', on_key_event) +canvas.mpl_connect( + "key_press_event", lambda event: print("you pressed {}".format(event.key))) +canvas.mpl_connect( + "key_press_event", lambda event: key_press_handler(event, canvas, toolbar)) def _quit(): @@ -55,9 +42,9 @@ def _quit(): root.destroy() # this is necessary on Windows to prevent # Fatal Python Error: PyEval_RestoreThread: NULL tstate -button = Tk.Button(master=root, text='Quit', command=_quit) +button = Tk.Button(master=root, text="Quit", command=_quit) button.pack(side=Tk.BOTTOM) Tk.mainloop() -# If you put root.destroy() here, it will cause an error if -# the window is closed with the window manager. +# If you put root.destroy() here, it will cause an error if the window is +# closed with the window manager. From b40923d0f3638841a098129d1ef0897a2eda0ff8 Mon Sep 17 00:00:00 2001 From: mattip Date: Tue, 12 Dec 2017 20:45:36 +0200 Subject: [PATCH 09/10] use agg:int8u not uint8_t --- src/_tkagg.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 1afc09d396ce..d92d8e7ffc9d 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -13,7 +13,7 @@ #include #include -#include // uint8_t on old MSC_VER +#include // agg:int8u // Include our own excerpts from the Tcl / Tk headers #include "_tkmini.h" @@ -52,7 +52,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int int wdata, hdata, bbox_parse; float x1, x2, y1, y2; bool has_bbox; - uint8_t *destbuffer, *buffer; + agg::int8u *destbuffer, *buffer; int destx, desty, destwidth, destheight, deststride; long mode; @@ -80,7 +80,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int (char *)NULL); return TCL_ERROR; } - buffer = (uint8_t*)pdata; + buffer = (agg::int8u*)pdata; /* get array mode (0=mono, 1=rgb, 2=rgba) */ mode = atol(argv[3]); @@ -109,7 +109,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int destheight = (int)(y2 - y1); deststride = 4 * destwidth; - destbuffer = new uint8_t[deststride * destheight]; + destbuffer = new agg::int8u[deststride * destheight]; if (destbuffer == NULL) { TCL_APPEND_RESULT(interp, "could not allocate memory", (char *)NULL); return TCL_ERROR; From 270f05db4c1727000aa6e590c0f5fc8392458d56 Mon Sep 17 00:00:00 2001 From: mattip Date: Fri, 12 Jan 2018 13:19:29 +0200 Subject: [PATCH 10/10] add whatsnew entry --- doc/users/next_whats_new/2018_01_10_pypy_tkagg_support.rst | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 doc/users/next_whats_new/2018_01_10_pypy_tkagg_support.rst diff --git a/doc/users/next_whats_new/2018_01_10_pypy_tkagg_support.rst b/doc/users/next_whats_new/2018_01_10_pypy_tkagg_support.rst new file mode 100644 index 000000000000..cd4fb18f06c1 --- /dev/null +++ b/doc/users/next_whats_new/2018_01_10_pypy_tkagg_support.rst @@ -0,0 +1,7 @@ +Rework TkAgg backend to support PyPy +------------------------------------ + +PyPy_ can plot using the TkAgg backend, supported on PyPy 5.9 +and greater (both PyPy for python 2.7 and PyPy for python 3.5) + +.. _PyPy: https:/www.pypy.org