@@ -44,6 +44,38 @@ typedef struct
44
44
Tcl_Interp *interp;
45
45
} TkappObject;
46
46
47
+ #define DYNAMIC_TKINTER
48
+
49
+ #ifdef DYNAMIC_TKINTER
50
+ // Load TCL / Tk symbols from tkinter extension module at run-time.
51
+ // Typedefs, global vars for TCL / Tk library functions.
52
+ typedef Tcl_Command (*tcl_cc)(Tcl_Interp *interp,
53
+ const char *cmdName, Tcl_CmdProc *proc,
54
+ ClientData clientData,
55
+ Tcl_CmdDeleteProc *deleteProc);
56
+ static tcl_cc TCL_CREATE_COMMAND;
57
+ typedef void (*tcl_app_res) (Tcl_Interp *interp, ...);
58
+ static tcl_app_res TCL_APPEND_RESULT;
59
+ typedef Tk_Window (*tk_mw) (Tcl_Interp *interp);
60
+ static tk_mw TK_MAIN_WINDOW;
61
+ typedef Tk_PhotoHandle (*tk_fp) (Tcl_Interp *interp, const char *imageName);
62
+ static tk_fp TK_FIND_PHOTO;
63
+ typedef void (*tk_ppb_nc) (Tk_PhotoHandle handle,
64
+ Tk_PhotoImageBlock *blockPtr, int x, int y,
65
+ int width, int height);
66
+ static tk_ppb_nc TK_PHOTO_PUTBLOCK;
67
+ typedef void (*tk_pb) (Tk_PhotoHandle handle);
68
+ static tk_pb TK_PHOTO_BLANK;
69
+ #else
70
+ // Build-time linking against system TCL / Tk functions.
71
+ #define TCL_CREATE_COMMAND Tcl_CreateCommand
72
+ #define TCL_APPEND_RESULT Tcl_AppendResult
73
+ #define TK_MAIN_WINDOW Tk_MainWindow
74
+ #define TK_FIND_PHOTO Tk_FindPhoto
75
+ #define TK_PHOTO_PUTBLOCK Tk_PhotoPutBlock
76
+ #define TK_PHOTO_BLANK Tk_PhotoBlank
77
+ #endif
78
+
47
79
static int PyAggImagePhoto (ClientData clientdata, Tcl_Interp *interp, int argc, char **argv)
48
80
{
49
81
Tk_PhotoHandle photo;
@@ -61,25 +93,25 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int argc,
61
93
62
94
long mode;
63
95
long nval;
64
- if (Tk_MainWindow (interp) == NULL ) {
96
+ if (TK_MAIN_WINDOW (interp) == NULL ) {
65
97
// Will throw a _tkinter.TclError with "this isn't a Tk application"
66
98
return TCL_ERROR;
67
99
}
68
100
69
101
if (argc != 5 ) {
70
- Tcl_AppendResult (interp, " usage: " , argv[0 ], " destPhoto srcImage" , (char *)NULL );
102
+ TCL_APPEND_RESULT (interp, " usage: " , argv[0 ], " destPhoto srcImage" , (char *)NULL );
71
103
return TCL_ERROR;
72
104
}
73
105
74
106
/* get Tcl PhotoImage handle */
75
- photo = Tk_FindPhoto (interp, argv[1 ]);
107
+ photo = TK_FIND_PHOTO (interp, argv[1 ]);
76
108
if (photo == NULL ) {
77
- Tcl_AppendResult (interp, " destination photo must exist" , (char *)NULL );
109
+ TCL_APPEND_RESULT (interp, " destination photo must exist" , (char *)NULL );
78
110
return TCL_ERROR;
79
111
}
80
112
/* get array (or object that can be converted to array) pointer */
81
113
if (sscanf (argv[2 ], SIZE_T_FORMAT, &aggl) != 1 ) {
82
- Tcl_AppendResult (interp, " error casting pointer" , (char *)NULL );
114
+ TCL_APPEND_RESULT (interp, " error casting pointer" , (char *)NULL );
83
115
return TCL_ERROR;
84
116
}
85
117
bufferobj = (PyObject *)aggl;
@@ -88,7 +120,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int argc,
88
120
try {
89
121
buffer = numpy::array_view<uint8_t , 3 >(bufferobj);
90
122
} catch (...) {
91
- Tcl_AppendResult (interp, " buffer is of wrong type" , (char *)NULL );
123
+ TCL_APPEND_RESULT (interp, " buffer is of wrong type" , (char *)NULL );
92
124
PyErr_Clear ();
93
125
return TCL_ERROR;
94
126
}
@@ -99,13 +131,13 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int argc,
99
131
/* get array mode (0=mono, 1=rgb, 2=rgba) */
100
132
mode = atol (argv[3 ]);
101
133
if ((mode != 0 ) && (mode != 1 ) && (mode != 2 )) {
102
- Tcl_AppendResult (interp, " illegal image mode" , (char *)NULL );
134
+ TCL_APPEND_RESULT (interp, " illegal image mode" , (char *)NULL );
103
135
return TCL_ERROR;
104
136
}
105
137
106
138
/* check for bbox/blitting */
107
139
if (sscanf (argv[4 ], SIZE_T_FORMAT, &bboxl) != 1 ) {
108
- Tcl_AppendResult (interp, " error casting pointer" , (char *)NULL );
140
+ TCL_APPEND_RESULT (interp, " error casting pointer" , (char *)NULL );
109
141
return TCL_ERROR;
110
142
}
111
143
bboxo = (PyObject *)bboxl;
@@ -126,7 +158,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int argc,
126
158
127
159
destbuffer = new agg::int8u[deststride * destheight];
128
160
if (destbuffer == NULL ) {
129
- Tcl_AppendResult (interp, " could not allocate memory" , (char *)NULL );
161
+ TCL_APPEND_RESULT (interp, " could not allocate memory" , (char *)NULL );
130
162
return TCL_ERROR;
131
163
}
132
164
@@ -167,7 +199,7 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int argc,
167
199
block.pitch = deststride;
168
200
block.pixelPtr = destbuffer;
169
201
170
- Tk_PhotoPutBlock (photo, &block, destx, desty, destwidth, destheight);
202
+ TK_PHOTO_PUTBLOCK (photo, &block, destx, desty, destwidth, destheight);
171
203
delete[] destbuffer;
172
204
173
205
} else {
@@ -177,9 +209,9 @@ static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int argc,
177
209
block.pixelPtr = buffer.data ();
178
210
179
211
/* Clear current contents */
180
- Tk_PhotoBlank (photo);
212
+ TK_PHOTO_BLANK (photo);
181
213
/* Copy opaque block to photo image, and leave the rest to TK */
182
- Tk_PhotoPutBlock (photo, &block, 0 , 0 , block.width , block.height );
214
+ TK_PHOTO_PUTBLOCK (photo, &block, 0 , 0 , block.width , block.height );
183
215
}
184
216
185
217
return TCL_OK;
@@ -216,11 +248,11 @@ static PyObject *_tkinit(PyObject *self, PyObject *args)
216
248
217
249
/* This will bomb if interp is invalid... */
218
250
219
- Tcl_CreateCommand (interp,
220
- " PyAggImagePhoto" ,
221
- (Tcl_CmdProc *)PyAggImagePhoto,
222
- (ClientData)0 ,
223
- (Tcl_CmdDeleteProc *)NULL );
251
+ TCL_CREATE_COMMAND (interp,
252
+ " PyAggImagePhoto" ,
253
+ (Tcl_CmdProc *)PyAggImagePhoto,
254
+ (ClientData)0 ,
255
+ (Tcl_CmdDeleteProc *)NULL );
224
256
225
257
Py_INCREF (Py_None);
226
258
return Py_None;
@@ -232,6 +264,90 @@ static PyMethodDef functions[] = {
232
264
{ NULL , NULL } /* sentinel */
233
265
};
234
266
267
+ #ifdef DYNAMIC_TKINTER
268
+ // Functions to fill global TCL / Tk function pointers from tkinter module.
269
+
270
+ #include < dlfcn.h>
271
+
272
+ #if PY3K
273
+ #define TKINTER_PKG " tkinter"
274
+ #define TKINTER_MOD " _tkinter"
275
+ // From module __file__ attribute to char *string for dlopen.
276
+ #define FNAME2CHAR (s ) (PyBytes_AsString(PyUnicode_EncodeFSDefault(s)))
277
+ #else
278
+ #define TKINTER_PKG " Tkinter"
279
+ #define TKINTER_MOD " tkinter"
280
+ // From module __file__ attribute to char *string for dlopen
281
+ #define FNAME2CHAR (s ) (PyString_AsString(s))
282
+ #endif
283
+
284
+ void *_dfunc (void *lib_handle, const char *func_name)
285
+ {
286
+ // Load function, unless there has been a previous error. If so, then
287
+ // return NULL. If there is an error loading the function, return NULL
288
+ // and set error flag.
289
+ static int have_error = 0 ;
290
+ void *func = NULL ;
291
+ if (have_error == 0 ) {
292
+ // reset errors
293
+ dlerror ();
294
+ func = dlsym (lib_handle, func_name);
295
+ const char *error = dlerror ();
296
+ if (error != NULL ) {
297
+ PyErr_SetString (PyExc_RuntimeError, error);
298
+ have_error = 1 ;
299
+ }
300
+ }
301
+ return func;
302
+ }
303
+
304
+ int _func_loader (void *tkinter_lib)
305
+ {
306
+ // Fill global function pointers from dynamic lib.
307
+ // Return 0 fur success; 1 otherwise.
308
+ TCL_CREATE_COMMAND = (tcl_cc) _dfunc (tkinter_lib, " Tcl_CreateCommand" );
309
+ TCL_APPEND_RESULT = (tcl_app_res) _dfunc (tkinter_lib, " Tcl_AppendResult" );
310
+ TK_MAIN_WINDOW = (tk_mw) _dfunc (tkinter_lib, " Tk_MainWindow" );
311
+ TK_FIND_PHOTO = (tk_fp) _dfunc (tkinter_lib, " Tk_FindPhoto" );
312
+ TK_PHOTO_PUTBLOCK = (tk_ppb_nc) _dfunc (tkinter_lib, " Tk_PhotoPutBlock_NoComposite" );
313
+ TK_PHOTO_BLANK = (tk_pb) _dfunc (tkinter_lib, " Tk_PhotoBlank" );
314
+ return (TK_PHOTO_BLANK == NULL );
315
+ }
316
+
317
+ int load_tkinter_funcs (void )
318
+ {
319
+ // Load tkinter global funcs from tkinter compiled module.
320
+ // Return 0 for success, non-zero for failure.
321
+ int ret = -1 ;
322
+ PyObject *pModule, *pSubmodule, *pString;
323
+
324
+ pModule = PyImport_ImportModule (TKINTER_PKG);
325
+ if (pModule != NULL ) {
326
+ pSubmodule = PyObject_GetAttrString (pModule, TKINTER_MOD);
327
+ if (pSubmodule != NULL ) {
328
+ pString = PyObject_GetAttrString (pSubmodule, " __file__" );
329
+ if (pString != NULL ) {
330
+ char *tkinter_libname = FNAME2CHAR (pString);
331
+ void *tkinter_lib = dlopen (tkinter_libname, RTLD_LAZY);
332
+ if (tkinter_lib == NULL ) {
333
+ PyErr_SetString (PyExc_RuntimeError,
334
+ " Cannot dlopen tkinter module file" );
335
+ } else {
336
+ ret = _func_loader (tkinter_lib);
337
+ // dlclose probably safe because tkinter has been
338
+ // imported.
339
+ dlclose (tkinter_lib);
340
+ }
341
+ Py_DECREF (pString);
342
+ }
343
+ Py_DECREF (pSubmodule);
344
+ }
345
+ Py_DECREF (pModule);
346
+ }
347
+ return ret;
348
+ }
349
+ #endif
350
+
235
351
#if PY3K
236
352
static PyModuleDef _tkagg_module = { PyModuleDef_HEAD_INIT, " _tkagg" , " " , -1 , functions,
237
353
NULL , NULL , NULL , NULL };
@@ -244,13 +360,20 @@ PyMODINIT_FUNC PyInit__tkagg(void)
244
360
245
361
import_array ();
246
362
247
- return m;
363
+ #ifdef DYNAMIC_TKINTER
364
+ return (load_tkinter_funcs () == 0 ) ? m : NULL ;
365
+ #else
366
+ return m
367
+ #endif
248
368
}
249
369
#else
250
370
PyMODINIT_FUNC init_tkagg (void )
251
371
{
252
372
import_array ();
253
373
254
374
Py_InitModule (" _tkagg" , functions);
375
+ #ifdef DYNAMIC_TKINTER
376
+ load_tkinter_funcs ();
377
+ #endif
255
378
}
256
379
#endif
0 commit comments