10000 revert to unified blit code and prevent some races · matplotlib/matplotlib@a4a920e · GitHub
[go: up one dir, main page]

Skip to content

Commit a4a920e

Browse files
revert to unified blit code and prevent some races
current status: Relies on tkinter arcana, untested
1 parent 2ad422a commit a4a920e

File tree

4 files changed

+37
-105
lines changed

4 files changed

+37
-105
lines changed

lib/matplotlib/backends/_backend_tk.py

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,25 @@ def _restore_foreground_window_at_end():
4444
_c_internal_utils.Win32_SetForegroundWindow(foreground)
4545

4646

47-
def blit(photoimage, aggimage, offsets, bbox=None):
47+
_blit_args = {}
48+
_blit_tcl_name = None
49+
50+
51+
def _blit(argsid):
52+
"""
53+
Thin wrapper to pass arguments to blit via tkapp.call
54+
"""
55+
args = _blit_args.pop(argsid)
56+
_tkagg.blit(*args)
57+
58+
59+
def blit(tk_instance, photoimage, aggimage, offsets, bbox=None):
4860
"""
4961
Blit *aggimage* to *photoimage*.
5062
63+
A *tk_instance* such as a canvas is required execute the blit.
64+
Tcl events must be dispatched to trigger a blit from a non-Tcl thread.
65+
5166
*offsets* is a tuple describing how to fill the ``offset`` field of the
5267
``Tk_PhotoImageBlock`` struct: it should be (0, 1, 2, 3) for RGBA8888 data,
5368
(2, 1, 0, 3) for little-endian ARBG32 (i.e. GBRA8888) data and (1, 2, 3, 0)
@@ -68,8 +83,22 @@ def blit(photoimage, aggimage, offsets, bbox=None):
6883
else:
6984
photoimage.blank()
7085
bboxptr = (0, width, 0, height)
71-
_tkagg.blit(
86+
87+
args = (
7288
photoimage.tk.interpaddr(), str(photoimage), dataptr, offsets, bboxptr)
89+
argsid = str(id)
90+
_blit_args[argsid] = args
91+
92+
global _blit_tcl_name
93+
# tkapp.call coerces all arguments to strings
94+
try:
95+
tk_instance.tk.call(_blit_tcl_name, argsid)
96+
except tk.TclError:
97+
# need to register with the tk root or constantly re-register
98+
# each time tk_instance is destroyed
99+
root = tk_instance._root()
100+
_blit_tcl_name = root.register(_blit)
101+
tk_instance.tk.call(_blit_tcl_name, argsid)
73102

74103

75104
class TimerTk(TimerBase):
@@ -123,9 +152,6 @@ def __init__(self, figure, master=None, resize_callback=None):
123152
width=w, height=h, borderwidth=0, highlightthickness=0)
124153
self._tkphoto = tk.PhotoImage(
125154
master=self._tkcanvas, width=w, height=h)
126-
self._findphoto_name = self._tkcanvas.register(self._findphoto)
127-
self._photoputblock_name = self._tkcanvas.register(self._photoputblock)
128-
self._tkpointers = None
129155
self._tkcanvas.create_image(w//2, h//2, image=self._tkphoto)
130156
self._resize_callback = resize_callback
131157
self._tkcanvas.bind("<Configure>", self.resize)
@@ -181,41 +207,6 @@ def resize(self, event):
181207
int(width / 2), int(height / 2), image=self._tkphoto)
182208
self.resize_event()
183209

184-
def _findphoto(self):
185-
return _tkagg.findphoto(self._tkphoto.tk.interpaddr(), str(self._tkphoto))
186-
187-
def _photoputblock(self):
188-
photoptr, dataptr, bboxptr = self._tkpointers
189-
_tkagg.photoputblock(photoptr, dataptr, (0, 1, 2, 3), bboxptr)
190-
191-
def blit(self, bbox=None):
192-
"""
193-
Blit *aggimage* to *photoimage*.
194-
195-
*offsets* is a tuple describing how to fill the ``offset`` field of the
196-
``Tk_PhotoImageBlock`` struct: it should be (0, 1, 2, 3) for RGBA8888 data,
197-
(2, 1, 0, 3) for little-endian ARBG32 (i.e. GBRA8888) data and (1, 2, 3, 0)
198-
for big-endian ARGB32 (i.e. ARGB8888) data.
199-
200-
If *bbox* is passed, it defines the region that gets blitted.
201-
"""
202-
data = np.asarray(self.renderer._renderer)
203-
height, width = data.shape[:2]
204-
dataptr = (height, width, data.ctypes.data)
205-
if bbox is not None:
206-
(x1, y1), (x2, y2) = bbox.__array__()
207-
x1 = max(math.floor(x1), 0)
208-
x2 = min(math.ceil(x2), width)
209-
y1 = max(math.floor(y1), 0)
210-
y2 = min(math.ceil(y2), height)
211-
bboxptr = (x1, x2, y1, y2)
212-
else:
213-
self._tkphoto.blank()
214-
bboxptr = (0, width, 0, height)
215-
photoptr = self._master.tk.call(self._findphoto_name)
216-
self._tkpointers = photoptr, dataptr, bboxptr
217-
self._master.tk.call(self._photoputblock_name)
218-
219210
def draw_idle(self):
220211
# docstring inherited
221212
if not self._idle:

lib/matplotlib/backends/backend_tkagg.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
from .backend_agg import FigureCanvasAgg
22
from ._backend_tk import (
3-
_BackendTk, FigureCanvasTk, FigureManagerTk, NavigationToolbar2Tk)
3+
_BackendTk, FigureCanvasTk, FigureManagerTk, NavigationToolbar2Tk, blit)
44

55

66
class FigureCanvasTkAgg(FigureCanvasAgg, FigureCanvasTk):
77
def draw(self):
88
super().draw()
99
self.blit()
1010

11+
def blit(self, bbox=None):
12+
blit(self._tkcanvas, self._tkphoto,
13+
self.renderer._renderer, (0, 1, 2, 3), bbox=bbox)
14+
1115

1216
@_BackendTk.export
1317
class _BackendTkAgg(_BackendTk):

lib/matplotlib/backends/backend_tkcairo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def draw(self):
2121
self.figure.draw(self._renderer)
2222
buf = np.reshape(surface.get_data(), (height, width, 4))
2323
_backend_tk.blit(
24-
self._tkphoto, buf,
24+
self._tkcanvas, self._tkphoto, buf,
2525
(2, 1, 0, 3) if sys.byteorder == "little" else (1, 2, 3, 0))
2626

2727

src/_tkagg.cpp

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -89,71 +89,8 @@ static PyObject *mpl_tk_blit(PyObject *self, PyObject *args)
8989
}
9090
}
9191

92-
static PyObject *mpl_tk_findphoto(PyObject *self, PyObject *args)
93-
{
94-
Tcl_Interp *interp;
95-
c 10000 har const *photo_name;
96-
Tk_PhotoHandle photo;
97-
if (!PyArg_ParseTuple(args, "O&s:findphoto",convert_voidptr, &interp, &photo_name)) {
98-
goto exit;
99-
}
100-
if (!(photo = TK_FIND_PHOTO(interp, photo_name))) {
101-
PyErr_SetString(PyExc_ValueError, "Failed to extract Tk_PhotoHandle");
102-
goto exit;
103-
}
104-
exit:
105-
if (PyErr_Occurred()) {
106-
return NULL;
107-
} else {
108-
return PyLong_FromVoidPtr((void *)photo);
109-
}
110-
}
111-
112-
static PyObject *mpl_tk_photoputblock(PyObject *self, PyObject *args)
113-
{
114-
int height, width;
115-
unsigned char *data_ptr;
116-
int o0, o1, o2, o3;
117-
int x1, x2, y1, y2;
118-
Tk_PhotoHandle photo;
119-
Tk_PhotoImageBlock block;
120-
if (!PyArg_ParseTuple(args, "O&(iiO&)(iiii)(iiii):photoputblock",
121-
convert_voidptr, &photo,
122-
&height, &width, convert_voidptr, &data_ptr,
123-
&o0, &o1, &o2, &o3,
124-
&x1, &x2, &y1, &y2)) {
125-
goto exit;
126-
}
127-
if (0 > y1 || y1 > y2 || y2 > height || 0 > x1 || x1 > x2 || x2 > width) {
128-
PyErr_SetString(PyExc_ValueError, "Attempting to draw out of bounds");
129-
goto exit;
130-
}
131-
132-
Py_BEGIN_ALLOW_THREADS
133-
block.pixelPtr = data_ptr + 4 * ((height - y2) * width + x1);
134-
block.width = x2 - x1;
135-
block.height = y2 - y1;
136-
block.pitch = 4 * width;
137-
block.pixelSize = 4;
138-
block.offset[0] = o0;
139-
block.offset[1] = o1;
140-
block.offset[2] = o2;
141-
block.offset[3] = o3;
142-
TK_PHOTO_PUT_BLOCK_NO_COMPOSITE(
143-
photo, &block, x1, height - y2, x2 - x1, y2 - y1);
144-
Py_END_ALLOW_THREADS
145-
exit:
146-
if (PyErr_Occurred()) {
147-
return NULL;
148-
} else {
149-
Py_RETURN_NONE;
150-
}
151-
}
152-
15392
static PyMethodDef functions[] = {
15493
{ "blit", (PyCFunction)mpl_tk_blit, METH_VARARGS },
155-
{ "findphoto", (PyCFunction)mpl_tk_findphoto, METH_VARARGS },
156-
{ "photoputblock", (PyCFunction)mpl_tk_photoputblock, METH_VARARGS },
15794
{ NULL, NULL } /* sentinel */
15895
};
15996

0 commit comments

Comments
 (0)
0