Thanks to visit codestin.com
Credit goes to github.com

Skip to content

COMPAT: use tkagg backend on PyPy #9356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 12, 2018
7 changes: 7 additions & 0 deletions doc/users/next_whats_new/2018_01_10_pypy_tkagg_support.rst
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions examples/user_interfaces/embedding_in_tk_sgskip.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# 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 numpy as np

Expand Down
24 changes: 13 additions & 11 deletions lib/matplotlib/backends/tkagg.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,25 @@ 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])
else:
bbox_array = None
bboxptr = 0
data = np.asarray(aggimage)
dataptr = (data.ctypes.data, data.shape[0], data.shape[1])
try:
tk.call(
"PyAggImagePhoto", photoimage,
id(data), colormode, id(bbox_array))
dataptr, colormode, bboxptr)
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
if hasattr(tk, 'interpaddr'):
_tkagg.tkinit(tk.interpaddr(), 1)
else:
# very old python?
_tkagg.tkinit(tk, 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interpaddr was added in 1998(! python/cpython@9d1b7ae) so I cannot actually imagine a case where this is triggered today (but that can go to another PR of course)

tk.call("PyAggImagePhoto", photoimage,
dataptr, colormode, bboxptr)

def test(aggimage):
r = Tk.Tk()
Expand Down
2 changes: 0 additions & 2 deletions setupext.py
Original file line number Diff line number Diff line change
Expand Up @@ -1505,13 +1505,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

Expand Down
110 changes: 56 additions & 54 deletions src/_tkagg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@
#include <cstdio>
#include <sstream>

#include "py_converters.h"
#include <agg_basics.h> // agg:int8u

// 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
{
Expand All @@ -44,16 +45,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, bbox_parse;
float x1, x2, y1, y2;
bool has_bbox;
uint8_t *destbuffer;
agg::int8u *destbuffer, *buffer;
int destx, desty, destwidth, destheight, deststride;
//unsigned long tmp_ptr;

long mode;
long nval;
Expand All @@ -73,24 +73,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);
/* 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;
}
bufferobj = (PyObject *)aggl;

numpy::array_view<uint8_t, 3> buffer;
try {
buffer = numpy::array_view<uint8_t, 3>(bufferobj);
} catch (...) {
TCL_APPEND_RESULT(interp, "buffer is of wrong type", (char *)NULL);
PyErr_Clear();
return TCL_ERROR;
}
int srcheight = buffer.dim(0);

/* XXX insert aggRenderer type check */
buffer = (agg::int8u*)pdata;

/* get array mode (0=mono, 1=rgb, 2=rgba) */
mode = atol(argv[3]);
Expand All @@ -100,24 +90,23 @@ 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);
bbox_parse = sscanf(argv[4], BBOX_FORMAT, &x1, &x2, &y1, &y2);
if (bbox_parse == 4) {
has_bbox = true;
}
else if ((bbox_parse == 1) && (x1 == 0)){
has_bbox = false;
} else {
TCL_APPEND_RESULT(interp, "illegal bbox", (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;
}

has_bbox = true;

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 = (int)x1;
desty = (int)(hdata - y2);
destwidth = (int)(x2 - x1);
destheight = (int)(y2 - y1);
deststride = 4 * destwidth;

destbuffer = new agg::int8u[deststride * destheight];
Expand All @@ -128,11 +117,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;
}
Expand Down Expand Up @@ -168,10 +156,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);
Expand Down Expand Up @@ -199,7 +187,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;
}

Expand Down Expand Up @@ -338,7 +326,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.
Expand Down Expand Up @@ -432,13 +420,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);
Expand All @@ -447,7 +453,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 };

Expand All @@ -457,15 +463,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();
Expand Down
2 changes: 1 addition & 1 deletion src/file_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down