Patch #518625: Return objects in Tkinter.
This commit is contained in:
parent
52ea7e9244
commit
ffad633af9
|
@ -45,6 +45,8 @@ try:
|
|||
except ImportError:
|
||||
_MacOS = None
|
||||
|
||||
want_objects = 1
|
||||
|
||||
TkVersion = float(_tkinter.TK_VERSION)
|
||||
TclVersion = float(_tkinter.TCL_VERSION)
|
||||
|
||||
|
@ -1521,6 +1523,7 @@ class Tk(Misc, Wm):
|
|||
if ext not in ('.py', '.pyc', '.pyo'):
|
||||
baseName = baseName + ext
|
||||
self.tk = _tkinter.create(screenName, baseName, className)
|
||||
self.tk.wantobjects(want_objects)
|
||||
if _MacOS and hasattr(_MacOS, 'SchedParams'):
|
||||
# Disable event scanning except for Command-Period
|
||||
_MacOS.SchedParams(1, 0)
|
||||
|
|
|
@ -312,6 +312,11 @@ Core and builtins
|
|||
Extension modules
|
||||
-----------------
|
||||
|
||||
- _tkinter now returns Tcl objects, instead of strings. Objects which
|
||||
have Python equivalents are converted to Python objects, other objects
|
||||
are wrapped. This can be configured through the wantobjects method,
|
||||
or Tkinter.want_objects.
|
||||
|
||||
- The PyBSDDB wrapper around the Sleepycat Berkely DB library has been
|
||||
added as the package bsddb. The traditional bsddb module is still
|
||||
available in source code, but not built automatically anymore, and
|
||||
|
|
|
@ -50,9 +50,11 @@ Copyright (C) 1994 Steen Lumholt.
|
|||
|
||||
#ifdef TK_FRAMEWORK
|
||||
#include <Tcl/tcl.h>
|
||||
#include <Tcl/tclInt.h>
|
||||
#include <Tk/tk.h>
|
||||
#else
|
||||
#include <tcl.h>
|
||||
#include <tclInt.h>
|
||||
#include <tk.h>
|
||||
#endif
|
||||
|
||||
|
@ -219,6 +221,7 @@ static PyTypeObject Tkapp_Type;
|
|||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Tcl_Interp *interp;
|
||||
int want_objects;
|
||||
} TkappObject;
|
||||
|
||||
#define Tkapp_Check(v) ((v)->ob_type == &Tkapp_Type)
|
||||
|
@ -424,6 +427,67 @@ Split(char *list)
|
|||
return v;
|
||||
}
|
||||
|
||||
/* In some cases, Tcl will still return strings that are supposed to be
|
||||
lists. SplitObj walks through a nested tuple, finding string objects that
|
||||
need to be split. */
|
||||
|
||||
PyObject *
|
||||
SplitObj(PyObject *arg)
|
||||
{
|
||||
if (PyTuple_Check(arg)) {
|
||||
int i, size;
|
||||
PyObject *elem, *newelem, *result;
|
||||
|
||||
size = PyTuple_Size(arg);
|
||||
result = NULL;
|
||||
/* Recursively invoke SplitObj for all tuple items.
|
||||
If this does not return a new object, no action is
|
||||
needed. */
|
||||
for(i = 0; i < size; i++) {
|
||||
elem = PyTuple_GetItem(arg, i);
|
||||
newelem = SplitObj(elem);
|
||||
if (!newelem) {
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
if (!result) {
|
||||
int k;
|
||||
if (newelem == elem) {
|
||||
Py_DECREF(newelem);
|
||||
continue;
|
||||
}
|
||||
result = PyTuple_New(size);
|
||||
if (!result)
|
||||
return NULL;
|
||||
for(k = 0; k < i; k++) {
|
||||
elem = PyTuple_GetItem(arg, k);
|
||||
Py_INCREF(elem);
|
||||
PyTuple_SetItem(result, k, elem);
|
||||
}
|
||||
}
|
||||
PyTuple_SetItem(result, i, newelem);
|
||||
}
|
||||
if (result)
|
||||
return result;
|
||||
/* Fall through, returning arg. */
|
||||
}
|
||||
else if (PyString_Check(arg)) {
|
||||
int argc;
|
||||
char **argv;
|
||||
char *list = PyString_AsString(arg);
|
||||
|
||||
if (Tcl_SplitList((Tcl_Interp *)NULL, list, &argc, &argv) != TCL_OK) {
|
||||
Py_INCREF(arg);
|
||||
return arg;
|
||||
}
|
||||
Tcl_Free(FREECAST argv);
|
||||
if (argc > 1)
|
||||
return Split(PyString_AsString(arg));
|
||||
/* Fall through, returning arg. */
|
||||
}
|
||||
Py_INCREF(arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
/**** Tkapp Object ****/
|
||||
|
@ -458,7 +522,8 @@ static void EnableEventHook(void); /* Forward */
|
|||
static void DisableEventHook(void); /* Forward */
|
||||
|
||||
static TkappObject *
|
||||
Tkapp_New(char *screenName, char *baseName, char *className, int interactive)
|
||||
Tkapp_New(char *screenName, char *baseName, char *className,
|
||||
int interactive, int want_objects)
|
||||
{
|
||||
TkappObject *v;
|
||||
char *argv0;
|
||||
|
@ -468,6 +533,7 @@ Tkapp_New(char *screenName, char *baseName, char *className, int interactive)
|
|||
return NULL;
|
||||
|
||||
v->interp = Tcl_CreateInterp();
|
||||
v->want_objects = want_objects;
|
||||
|
||||
#if defined(macintosh)
|
||||
/* This seems to be needed */
|
||||
|
@ -513,6 +579,104 @@ Tkapp_New(char *screenName, char *baseName, char *className, int interactive)
|
|||
|
||||
/** Tcl Eval **/
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Tcl_Obj *value;
|
||||
} PyTclObject;
|
||||
|
||||
staticforward PyTypeObject PyTclObject_Type;
|
||||
#define PyTclObject_Check(v) ((v)->ob_type == &PyTclObject_Type)
|
||||
|
||||
static PyObject *
|
||||
newPyTclObject(Tcl_Obj *arg)
|
||||
{
|
||||
PyTclObject *self;
|
||||
self = PyObject_New(PyTclObject, &PyTclObject_Type);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
Tcl_IncrRefCount(arg);
|
||||
self->value = arg;
|
||||
return (PyObject*)self;
|
||||
}
|
||||
|
||||
static void
|
||||
PyTclObject_dealloc(PyTclObject *self)
|
||||
{
|
||||
Tcl_DecrRefCount(self->value);
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PyTclObject_str(PyTclObject *self)
|
||||
{
|
||||
return PyString_FromString(Tcl_GetString(self->value));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PyTclObject_repr(PyTclObject *self)
|
||||
{
|
||||
char buf[50];
|
||||
PyOS_snprintf(buf, 50, "<%s object at 0x%.8x>",
|
||||
self->value->typePtr->name, (int)self->value);
|
||||
return PyString_FromString(buf);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_typename(PyTclObject* obj, void* ignored)
|
||||
{
|
||||
return PyString_FromString(obj->value->typePtr->name);
|
||||
}
|
||||
|
||||
static PyGetSetDef PyTclObject_getsetlist[] = {
|
||||
{"typename", (getter)get_typename, NULL, "name of the Tcl type"},
|
||||
{0},
|
||||
};
|
||||
|
||||
statichere PyTypeObject PyTclObject_Type = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0, /*ob_size*/
|
||||
"_tkinter.Tcl_Obj", /*tp_name*/
|
||||
sizeof(PyTclObject), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
/* methods */
|
||||
(destructor)PyTclObject_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
(reprfunc)PyTclObject_repr, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash*/
|
||||
0, /*tp_call*/
|
||||
(reprfunc)PyTclObject_str, /*tp_str*/
|
||||
PyObject_GenericGetAttr,/*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
||||
0, /*tp_doc*/
|
||||
0, /*tp_traverse*/
|
||||
0, /*tp_clear*/
|
||||
0, /*tp_richcompare*/
|
||||
0, /*tp_weaklistoffset*/
|
||||
0, /*tp_iter*/
|
||||
0, /*tp_iternext*/
|
||||
0, /*tp_methods*/
|
||||
0, /*tp_members*/
|
||||
PyTclObject_getsetlist, /*tp_getset*/
|
||||
0, /*tp_base*/
|
||||
0, /*tp_dict*/
|
||||
0, /*tp_descr_get*/
|
||||
0, /*tp_descr_set*/
|
||||
0, /*tp_dictoffset*/
|
||||
0, /*tp_init*/
|
||||
0, /*tp_alloc*/
|
||||
0, /*tp_new*/
|
||||
0, /*tp_free*/
|
||||
0, /*tp_is_gc*/
|
||||
};
|
||||
|
||||
static Tcl_Obj*
|
||||
AsObj(PyObject *value)
|
||||
{
|
||||
|
@ -570,6 +734,11 @@ AsObj(PyObject *value)
|
|||
|
||||
}
|
||||
#endif
|
||||
else if(PyTclObject_Check(value)) {
|
||||
Tcl_Obj *v = ((PyTclObject*)value)->value;
|
||||
Tcl_IncrRefCount(v);
|
||||
return v;
|
||||
}
|
||||
else {
|
||||
PyObject *v = PyObject_Str(value);
|
||||
if (!v)
|
||||
|
@ -580,6 +749,99 @@ AsObj(PyObject *value)
|
|||
}
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
FromObj(PyObject* tkapp, Tcl_Obj *value)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
|
||||
if (value->typePtr == NULL)
|
||||
return PyString_FromStringAndSize(value->bytes, value->length);
|
||||
|
||||
if (value->typePtr == &tclBooleanType) {
|
||||
result = value->internalRep.longValue ? Py_True : Py_False;
|
||||
Py_INCREF(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (value->typePtr == &tclByteArrayType) {
|
||||
int size;
|
||||
char *data = Tcl_GetByteArrayFromObj(value, &size);
|
||||
return PyString_FromStringAndSize(data, size);
|
||||
}
|
||||
|
||||
if (value->typePtr == &tclDoubleType) {
|
||||
return PyFloat_FromDouble(value->internalRep.doubleValue);
|
||||
}
|
||||
|
||||
if (value->typePtr == &tclIntType) {
|
||||
return PyInt_FromLong(value->internalRep.longValue);
|
||||
}
|
||||
|
||||
if (value->typePtr == &tclListType) {
|
||||
int size;
|
||||
int i, status;
|
||||
PyObject *elem;
|
||||
Tcl_Obj *tcl_elem;
|
||||
|
||||
status = Tcl_ListObjLength(Tkapp_Interp(tkapp), value, &size);
|
||||
if (status == TCL_ERROR)
|
||||
return Tkinter_Error(tkapp);
|
||||
result = PyTuple_New(size);
|
||||
if (!result)
|
||||
return NULL;
|
||||
for (i = 0; i < size; i++) {
|
||||
status = Tcl_ListObjIndex(Tkapp_Interp(tkapp),
|
||||
value, i, &tcl_elem);
|
||||
if (status == TCL_ERROR) {
|
||||
Py_DECREF(result);
|
||||
return Tkinter_Error(tkapp);
|
||||
}
|
||||
elem = FromObj(tkapp, tcl_elem);
|
||||
if (!elem) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SetItem(result, i, elem);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (value->typePtr == &tclProcBodyType) {
|
||||
// fall through: return tcl object
|
||||
}
|
||||
|
||||
if (value->typePtr == &tclStringType) {
|
||||
#ifdef Py_USING_UNICODE
|
||||
#ifdef Py_UNICODE_WIDE
|
||||
PyObject *result;
|
||||
int size;
|
||||
Tcl_UniChar *input;
|
||||
Py_UNICODE *output;
|
||||
|
||||
size = Tcl_GetCharLength(value);
|
||||
result = PyUnicode_FromUnicode(NULL, size);
|
||||
if (!result)
|
||||
return NULL;
|
||||
input = Tcl_GetUnicode(value);
|
||||
output = PyUnicode_AS_UNICODE(result);
|
||||
while (size--)
|
||||
*output++ = *input++;
|
||||
return result;
|
||||
#else
|
||||
return PyUnicode_FromUnicode(Tcl_GetUnicode(value),
|
||||
Tcl_GetCharLength(value));
|
||||
#endif
|
||||
#else
|
||||
int size;
|
||||
char *c;
|
||||
c = Tcl_GetStringFromObj(value, &size);
|
||||
return PyString_FromStringAndSize(c, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
return newPyTclObject(value);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Tkapp_Call(PyObject *self, PyObject *args)
|
||||
{
|
||||
|
@ -639,9 +901,15 @@ Tkapp_Call(PyObject *self, PyObject *args)
|
|||
ENTER_OVERLAP
|
||||
if (i == TCL_ERROR)
|
||||
Tkinter_Error(self);
|
||||
else {
|
||||
/* We could request the object result here, but doing
|
||||
so would confuse applications that expect a string. */
|
||||
else if(((TkappObject*)self)->want_objects) {
|
||||
Tcl_Obj *value = Tcl_GetObjResult(interp);
|
||||
/* Not sure whether the IncrRef is necessary, but something
|
||||
may overwrite the interpreter result while we are
|
||||
converting it. */
|
||||
Tcl_IncrRefCount(value);
|
||||
res = FromObj(self, value);
|
||||
Tcl_DecrRefCount(value);
|
||||
} else {
|
||||
const char *s = Tcl_GetStringResult(interp);
|
||||
const char *p = s;
|
||||
|
||||
|
@ -964,6 +1232,13 @@ Tkapp_GetInt(PyObject *self, PyObject *args)
|
|||
char *s;
|
||||
int v;
|
||||
|
||||
if (PyTuple_Size(args) == 1) {
|
||||
PyObject* o = PyTuple_GetItem(args, 0);
|
||||
if (PyInt_Check(o)) {
|
||||
Py_INCREF(o);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
if (!PyArg_ParseTuple(args, "s:getint", &s))
|
||||
return NULL;
|
||||
if (Tcl_GetInt(Tkapp_Interp(self), s, &v) == TCL_ERROR)
|
||||
|
@ -977,6 +1252,13 @@ Tkapp_GetDouble(PyObject *self, PyObject *args)
|
|||
char *s;
|
||||
double v;
|
||||
|
||||
if (PyTuple_Size(args) == 1) {
|
||||
PyObject *o = PyTuple_GetItem(args, 0);
|
||||
if (PyFloat_Check(o)) {
|
||||
Py_INCREF(o);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
if (!PyArg_ParseTuple(args, "s:getdouble", &s))
|
||||
return NULL;
|
||||
if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR)
|
||||
|
@ -990,6 +1272,13 @@ Tkapp_GetBoolean(PyObject *self, PyObject *args)
|
|||
char *s;
|
||||
int v;
|
||||
|
||||
if (PyTuple_Size(args) == 1) {
|
||||
PyObject *o = PyTuple_GetItem(args, 0);
|
||||
if (PyInt_Check(o)) {
|
||||
Py_INCREF(o);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
if (!PyArg_ParseTuple(args, "s:getboolean", &s))
|
||||
return NULL;
|
||||
if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR)
|
||||
|
@ -1093,6 +1382,13 @@ Tkapp_SplitList(PyObject *self, PyObject *args)
|
|||
PyObject *v;
|
||||
int i;
|
||||
|
||||
if (PyTuple_Size(args) == 1) {
|
||||
v = PyTuple_GetItem(args, 0);
|
||||
if (PyTuple_Check(v)) {
|
||||
Py_INCREF(v);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
if (!PyArg_ParseTuple(args, "et:splitlist", "utf-8", &list))
|
||||
return NULL;
|
||||
|
||||
|
@ -1121,6 +1417,13 @@ Tkapp_Split(PyObject *self, PyObject *args)
|
|||
{
|
||||
char *list;
|
||||
|
||||
if (PyTuple_Size(args) == 1) {
|
||||
PyObject* o = PyTuple_GetItem(args, 0);
|
||||
if (PyTuple_Check(o)) {
|
||||
o = SplitObj(o);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
if (!PyArg_ParseTuple(args, "et:split", "utf-8", &list))
|
||||
return NULL;
|
||||
return Split(list);
|
||||
|
@ -1660,11 +1963,26 @@ Tkapp_InterpAddr(PyObject *self, PyObject *args)
|
|||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
Tkapp_WantObjects(PyObject *self, PyObject *args)
|
||||
{
|
||||
|
||||
int want_objects;
|
||||
if (!PyArg_ParseTuple(args, "i:wantobjects", &want_objects))
|
||||
return NULL;
|
||||
((TkappObject*)self)->want_objects = want_objects;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**** Tkapp Method List ****/
|
||||
|
||||
static PyMethodDef Tkapp_methods[] =
|
||||
{
|
||||
{"wantobjects", Tkapp_WantObjects, METH_VARARGS},
|
||||
{"call", Tkapp_Call, METH_OLDARGS},
|
||||
{"globalcall", Tkapp_GlobalCall, METH_OLDARGS},
|
||||
{"eval", Tkapp_Eval, METH_VARARGS},
|
||||
|
@ -1861,6 +2179,7 @@ Tkinter_Create(PyObject *self, PyObject *args)
|
|||
char *baseName = NULL;
|
||||
char *className = NULL;
|
||||
int interactive = 0;
|
||||
int want_objects = 0;
|
||||
|
||||
baseName = strrchr(Py_GetProgramName(), '/');
|
||||
if (baseName != NULL)
|
||||
|
@ -1871,11 +2190,11 @@ Tkinter_Create(PyObject *self, PyObject *args)
|
|||
|
||||
if (!PyArg_ParseTuple(args, "|zssi:create",
|
||||
&screenName, &baseName, &className,
|
||||
&interactive))
|
||||
&interactive, &want_objects))
|
||||
return NULL;
|
||||
|
||||
return (PyObject *) Tkapp_New(screenName, baseName, className,
|
||||
interactive);
|
||||
interactive, want_objects);
|
||||
}
|
||||
|
||||
static PyMethodDef moduleMethods[] =
|
||||
|
@ -2045,6 +2364,8 @@ init_tkinter(void)
|
|||
Tktt_Type.ob_type = &PyType_Type;
|
||||
PyDict_SetItemString(d, "TkttType", (PyObject *)&Tktt_Type);
|
||||
|
||||
PyTclObject_Type.ob_type = &PyType_Type;
|
||||
PyDict_SetItemString(d, "Tcl_Obj", (PyObject *)&PyTclObject_Type);
|
||||
|
||||
#ifdef TK_AQUA
|
||||
/* Tk_MacOSXSetupTkNotifier must be called before Tcl's subsystems
|
||||
|
|
Loading…
Reference in New Issue