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

Skip to content

Commit 3bb328f

Browse files
authored
Merge pull request #9356 from mattip/tkagg-cffi
ENH: use tkagg backend on PyPy
2 parents ad27247 + 270f05d commit 3bb328f

File tree

6 files changed

+78
-68
lines changed

6 files changed

+78
-68
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Rework TkAgg backend to support PyPy
2+
------------------------------------
3+
4+
PyPy_ can plot using the TkAgg backend, supported on PyPy 5.9
5+
and greater (both PyPy for python 2.7 and PyPy for python 3.5)
6+
7+
.. _PyPy: https:/www.pypy.org

examples/user_interfaces/embedding_in_tk_sgskip.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# Implement the default Matplotlib key bindings.
1313
from matplotlib.backend_bases import key_press_handler
1414
from matplotlib.figure import Figure
15+
from six.moves import tkinter as Tk
1516

1617
import numpy as np
1718

lib/matplotlib/backends/tkagg.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,25 @@ def blit(photoimage, aggimage, bbox=None, colormode=1):
1313

1414
if bbox is not None:
1515
bbox_array = bbox.__array__()
16+
# x1, x2, y1, y2
17+
bboxptr = (bbox_array[0, 0], bbox_array[1, 0],
18+
bbox_array[0, 1], bbox_array[1, 1])
1619
else:
17-
bbox_array = None
20+
bboxptr = 0
1821
data = np.asarray(aggimage)
22+
dataptr = (data.ctypes.data, data.shape[0], data.shape[1])
1923
try:
2024
tk.call(
2125
"PyAggImagePhoto", photoimage,
22-
id(data), colormode, id(bbox_array))
26+
dataptr, colormode, bboxptr)
2327
except Tk.TclError:
24-
try:
25-
try:
26-
_tkagg.tkinit(tk.interpaddr(), 1)
27-
except AttributeError:
28-
_tkagg.tkinit(id(tk), 0)
29-
tk.call("PyAggImagePhoto", photoimage,
30-
id(data), colormode, id(bbox_array))
31-
except (ImportError, AttributeError, Tk.TclError):
32-
raise
28+
if hasattr(tk, 'interpaddr'):
29+
_tkagg.tkinit(tk.interpaddr(), 1)
30+
else:
31+
# very old python?
32+
_tkagg.tkinit(tk, 0)
33+
tk.call("PyAggImagePhoto", photoimage,
34+
dataptr, colormode, bboxptr)
3335

3436
def test(aggimage):
3537
r = Tk.Tk()

setupext.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,13 +1505,11 @@ def runtime_check(self):
15051505

15061506
def get_extension(self):
15071507
sources = [
1508-
'src/py_converters.cpp',
15091508
'src/_tkagg.cpp'
15101509
]
15111510

15121511
ext = make_extension('matplotlib.backends._tkagg', sources)
15131512
self.add_flags(ext)
1514-
Numpy().add_flags(ext)
15151513
LibAgg().add_flags(ext, add_sources=False)
15161514
return ext
15171515

src/_tkagg.cpp

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@
1313
#include <cstdio>
1414
#include <sstream>
1515

16-
#include "py_converters.h"
16+
#include <agg_basics.h> // agg:int8u
1717

1818
// Include our own excerpts from the Tcl / Tk headers
1919
#include "_tkmini.h"
2020

2121
#if defined(_MSC_VER)
22-
# define SIZE_T_FORMAT "%Iu"
22+
# define IMG_FORMAT "%Iu %d %d"
2323
#else
24-
# define SIZE_T_FORMAT "%zu"
24+
# define IMG_FORMAT "%zu %d %d"
2525
#endif
26+
#define BBOX_FORMAT "%f %f %f %f"
2627

2728
typedef struct
2829
{
@@ -44,16 +45,15 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int
4445
{
4546
Tk_PhotoHandle photo;
4647
Tk_PhotoImageBlock block;
47-
PyObject *bufferobj;
4848

4949
// vars for blitting
50-
PyObject *bboxo;
5150

52-
size_t aggl, bboxl;
51+
size_t pdata;
52+
int wdata, hdata, bbox_parse;
53+
float x1, x2, y1, y2;
5354
bool has_bbox;
54-
uint8_t *destbuffer;
55+
agg::int8u *destbuffer, *buffer;
5556
int destx, desty, destwidth, destheight, deststride;
56-
//unsigned long tmp_ptr;
5757

5858
long mode;
5959
long nval;
@@ -73,24 +73,14 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int
7373
TCL_APPEND_RESULT(interp, "destination photo must exist", (char *)NULL);
7474
return TCL_ERROR;
7575
}
76-
/* get array (or object that can be converted to array) pointer */
77-
if (sscanf(argv[2], SIZE_T_FORMAT, &aggl) != 1) {
78-
TCL_APPEND_RESULT(interp, "error casting pointer", (char *)NULL);
76+
/* get buffer from str which is "ptr height width" */
77+
if (sscanf(argv[2], IMG_FORMAT, &pdata, &hdata, &wdata) != 3) {
78+
TCL_APPEND_RESULT(interp,
79+
"error reading data, expected ptr height width",
80+
(char *)NULL);
7981
return TCL_ERROR;
8082
}
81-
bufferobj = (PyObject *)aggl;
82-
83-
numpy::array_view<uint8_t, 3> buffer;
84-
try {
85-
buffer = numpy::array_view<uint8_t, 3>(bufferobj);
86-
} catch (...) {
87-
TCL_APPEND_RESULT(interp, "buffer is of wrong type", (char *)NULL);
88-
PyErr_Clear();
89-
return TCL_ERROR;
90-
}
91-
int srcheight = buffer.dim(0);
92-
93-
/* XXX insert aggRenderer type check */
83+
buffer = (agg::int8u*)pdata;
9484

9585
/* get array mode (0=mono, 1=rgb, 2=rgba) */
9686
mode = atol(argv[3]);
@@ -100,24 +90,23 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int
10090
}
10191

10292
/* check for bbox/blitting */
103-
if (sscanf(argv[4], SIZE_T_FORMAT, &bboxl) != 1) {
104-
TCL_APPEND_RESULT(interp, "error casting pointer", (char *)NULL);
93+
bbox_parse = sscanf(argv[4], BBOX_FORMAT, &x1, &x2, &y1, &y2);
94+
if (bbox_parse == 4) {
95+
has_bbox = true;
96+
}
97+
else if ((bbox_parse == 1) && (x1 == 0)){
98+
has_bbox = false;
99+
} else {
100+
TCL_APPEND_RESULT(interp, "illegal bbox", (char *)NULL);
105101
return TCL_ERROR;
106102
}
107-
bboxo = (PyObject *)bboxl;
108-
109-
if (bboxo != NULL && bboxo != Py_None) {
110-
agg::rect_d rect;
111-
if (!convert_rect(bboxo, &rect)) {
112-
return TCL_ERROR;
113-
}
114-
115-
has_bbox = true;
116103

117-
destx = (int)rect.x1;
118-
desty = srcheight - (int)rect.y2;
119-
destwidth = (int)(rect.x2 - rect.x1);
120-
destheight = (int)(rect.y2 - rect.y1);
104+
if (has_bbox) {
105+
int srcstride = wdata * 4;
106+
destx = (int)x1;
107+
desty = (int)(hdata - y2);
108+
destwidth = (int)(x2 - x1);
109+
destheight = (int)(y2 - y1);
121110
deststride = 4 * destwidth;
122111

123112
destbuffer = new agg::int8u[deststride * destheight];
@@ -128,11 +117,10 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int
128117

129118
for (int i = 0; i < destheight; ++i) {
130119
memcpy(destbuffer + (deststride * i),
131-
&buffer(i + desty, destx, 0),
120+
&buffer[(i + desty) * srcstride + (destx * 4)],
132121
deststride);
133122
}
134123
} else {
135-
has_bbox = false;
136124
destbuffer = NULL;
137125
destx = desty = destwidth = destheight = deststride = 0;
138126
}
@@ -168,10 +156,10 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int
168156
delete[] destbuffer;
169157

170158
} else {
171-
block.width = buffer.dim(1);
172-
block.height = buffer.dim(0);
159+
block.width = wdata;
160+
block.height = hdata;
173161
block.pitch = (int)block.width * nval;
174-
block.pixelPtr = buffer.data();
162+
block.pixelPtr = buffer;
175163

176164
/* Clear current contents */
177165
TK_PHOTO_BLANK(photo);
@@ -199,7 +187,7 @@ static PyObject *_tkinit(PyObject *self, PyObject *args)
199187
} else {
200188
/* Do it the hard way. This will break if the TkappObject
201189
layout changes */
202-
app = (TkappObject *)PyLong_AsVoidPtr(arg);
190+
app = (TkappObject *)arg;
203191
interp = app->interp;
204192
}
205193

@@ -338,7 +326,7 @@ int load_tkinter_funcs(void)
338326
* tkinter uses these symbols, and the symbols are therefore visible in the
339327
* tkinter dynamic library (module).
340328
*/
341-
#if PY3K
329+
#if PY_MAJOR_VERSION >= 3
342330
#define TKINTER_PKG "tkinter"
343331
#define TKINTER_MOD "_tkinter"
344332
// From module __file__ attribute to char *string for dlopen.
@@ -432,13 +420,31 @@ int load_tkinter_funcs(void)
432420
}
433421
tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY);
434422
if (tkinter_lib == NULL) {
435-
PyErr_SetString(PyExc_RuntimeError,
436-
"Cannot dlopen tkinter module file");
437-
goto exit;
423+
/* Perhaps it is a cffi module, like in PyPy? */
424+
pString = PyObject_GetAttrString(pSubmodule, "tklib_cffi");
425+
if (pString == NULL) {
426+
goto fail;
427+
}
428+
pString = PyObject_GetAttrString(pString, "__file__");
429+
if (pString == NULL) {
430+
goto fail;
431+
}
432+
tkinter_libname = fname2char(pString);
433+
if (tkinter_libname == NULL) {
434+
goto fail;
435+
}
436+
tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY);
437+
}
438+
if (tkinter_lib == NULL) {
439+
goto fail;
438440
}
439441
ret = _func_loader(tkinter_lib);
440442
// dlclose probably safe because tkinter has been imported.
441443
dlclose(tkinter_lib);
444+
goto exit;
445+
fail:
446+
PyErr_SetString(PyExc_RuntimeError,
447+
"Cannot dlopen tkinter module file");
442448
exit:
443449
Py_XDECREF(pModule);
444450
Py_XDECREF(pSubmodule);
@@ -447,7 +453,7 @@ int load_tkinter_funcs(void)
447453
}
448454
#endif // end not Windows
449455

450-
#if PY3K
456+
#if PY_MAJOR_VERSION >= 3
451457
static PyModuleDef _tkagg_module = { PyModuleDef_HEAD_INIT, "_tkagg", "", -1, functions,
452458
NULL, NULL, NULL, NULL };
453459

@@ -457,15 +463,11 @@ PyMODINIT_FUNC PyInit__tkagg(void)
457463

458464
m = PyModule_Create(&_tkagg_module);
459465

460-
import_array();
461-
462466
return (load_tkinter_funcs() == 0) ? m : NULL;
463467
}
464468
#else
465469
PyMODINIT_FUNC init_tkagg(void)
466470
{
467-
import_array();
468-
469471
Py_InitModule("_tkagg", functions);
470472

471473
load_tkinter_funcs();

src/file_compat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ extern "C" {
4848
/*
4949
* PyFile_* compatibility
5050
*/
51-
#if PY3K
51+
#if defined(PY3K) | defined(PYPY_VERSION)
5252

5353
/*
5454
* Get a FILE* handle to the file represented by the Python object

0 commit comments

Comments
 (0)