🌐 AI搜索 & 代理 主页
Skip to content
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