From: <md...@us...> - 2010-10-11 14:04:49
|
Revision: 8741 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8741&view=rev Author: mdehoon Date: 2010-10-11 14:04:43 +0000 (Mon, 11 Oct 2010) Log Message: ----------- First attempt to implement a timer in the Mac OS X backend. Blitting has not (yet?) been implemented, but otherwise it seems to work fine. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py trunk/matplotlib/src/_macosx.m Modified: trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2010-10-11 04:50:58 UTC (rev 8740) +++ trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2010-10-11 14:04:43 UTC (rev 8741) @@ -5,7 +5,7 @@ from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ - FigureManagerBase, FigureCanvasBase, NavigationToolbar2 + FigureManagerBase, FigureCanvasBase, NavigationToolbar2, TimerBase from matplotlib.backend_bases import ShowBase from matplotlib.cbook import maxdict @@ -240,6 +240,24 @@ manager = FigureManagerMac(canvas, num) return manager +class TimerMac(_macosx.Timer, TimerBase): + ''' + Subclass of :class:`backend_bases.TimerBase` that uses CoreFoundation + run loops for timer events. + + Attributes: + * interval: The time between timer events in milliseconds. Default + is 1000 ms. + * single_shot: Boolean flag indicating whether this timer should + operate as single shot (run once and then stop). Defaults to False. + * callbacks: Stores list of (func, args) tuples that will be called + upon timer events. This list can be manipulated directly, or the + functions add_callback and remove_callback can be used. + ''' + # completely implemented at the C-level (in _macosx.Timer) + + + class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase): """ The canvas the figure renders into. Calls the draw and print fig @@ -310,7 +328,22 @@ def get_default_filetype(self): return 'png' + def new_timer(self, *args, **kwargs): + """ + Creates a new backend-specific subclass of :class:`backend_bases.Timer`. + This is useful for getting periodic events through the backend's native + event loop. Implemented only for backends with GUIs. + optional arguments: + + *interval* + Timer interval in milliseconds + *callbacks* + Sequence of (func, args, kwargs) where func(*args, **kwargs) will + be executed by the timer every *interval*. + """ + return TimerMac(*args, **kwargs) + class FigureManagerMac(_macosx.FigureManager, FigureManagerBase): """ Wrap everything up into a window for the pylab interface Modified: trunk/matplotlib/src/_macosx.m =================================================================== --- trunk/matplotlib/src/_macosx.m 2010-10-11 04:50:58 UTC (rev 8740) +++ trunk/matplotlib/src/_macosx.m 2010-10-11 14:04:43 UTC (rev 8741) @@ -5494,6 +5494,195 @@ return Py_True; } +typedef struct { + PyObject_HEAD + CFRunLoopTimerRef timer; +} Timer; + +static PyObject* +Timer_new(PyTypeObject* type, PyObject *args, PyObject *kwds) +{ + Timer* self = (Timer*)type->tp_alloc(type, 0); + if (!self) return NULL; + self->timer = NULL; + return (PyObject*) self; +} + +static void +Timer_dealloc(Timer* self) +{ + if (self->timer) { + PyObject* attribute; + CFRunLoopTimerContext context; + CFRunLoopTimerGetContext(self->timer, &context); + attribute = context.info; + Py_DECREF(attribute); + CFRunLoopRef runloop = CFRunLoopGetCurrent(); + if (runloop) { + CFRunLoopRemoveTimer(runloop, self->timer, kCFRunLoopCommonModes); + } + CFRelease(self->timer); + self->timer = NULL; + } + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject* +Timer_repr(Timer* self) +{ + return PyString_FromFormat("Timer object %p wrapping CFRunLoopTimerRef %p", + (void*) self, (void*)(self->timer)); +} + +static char Timer_doc[] = +"A Timer object wraps a CFRunLoopTimerRef and can add it to the event loop.\n"; + +static void timer_callback(CFRunLoopTimerRef timer, void* info) +{ + PyObject* method = info; + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject* result = PyObject_CallFunction(method, NULL); + if (result==NULL) PyErr_Print(); + PyGILState_Release(gstate); +} + +static PyObject* +Timer__timer_start(Timer* self, PyObject* args) +{ + CFRunLoopRef runloop; + CFRunLoopTimerRef timer; + CFRunLoopTimerContext context; + double milliseconds; + CFTimeInterval interval; + PyObject* attribute; + PyObject* failure; + runloop = CFRunLoopGetCurrent(); + if (!runloop) { + PyErr_SetString(PyExc_RuntimeError, "Failed to obtain run loop"); + return NULL; + } + context.version = 0; + context.retain = 0; + context.release = 0; + context.copyDescription = 0; + attribute = PyObject_GetAttrString((PyObject*)self, "_interval"); + if (attribute==NULL) + { + PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_interval'"); + return NULL; + } + milliseconds = PyFloat_AsDouble(attribute); + failure = PyErr_Occurred(); + Py_DECREF(attribute); + if (failure) return NULL; + attribute = PyObject_GetAttrString((PyObject*)self, "_single"); + if (attribute==NULL) + { + PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_single'"); + return NULL; + } + switch (PyObject_IsTrue(attribute)) { + case 1: + interval = 0; + break; + case 0: + interval = milliseconds / 1000.0; + break; + case -1: + default: + PyErr_SetString(PyExc_ValueError, "Cannot interpret _single attribute as True of False"); + return NULL; + } + attribute = PyObject_GetAttrString((PyObject*)self, "_on_timer"); + if (attribute==NULL) + { + PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_on_timer'"); + return NULL; + } + if (!PyMethod_Check(attribute)) { + PyErr_SetString(PyExc_RuntimeError, "_on_timer should be a Python method"); + return NULL; + } + context.info = attribute; + timer = CFRunLoopTimerCreate(kCFAllocatorDefault, + 0, + interval, + 0, + 0, + timer_callback, + &context); + if (!timer) { + PyErr_SetString(PyExc_RuntimeError, "Failed to create timer"); + return NULL; + } + Py_INCREF(attribute); + if (self->timer) { + CFRunLoopTimerGetContext(self->timer, &context); + attribute = context.info; + Py_DECREF(attribute); + CFRunLoopRemoveTimer(runloop, self->timer, kCFRunLoopCommonModes); + CFRelease(self->timer); + } + CFRunLoopAddTimer(runloop, timer, kCFRunLoopCommonModes); + /* Don't release the timer here, since the run loop may be destroyed and + * the timer lost before we have a chance to decrease the reference count + * of the attribute */ + self->timer = timer; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef Timer_methods[] = { + {"_timer_start", + (PyCFunction)Timer__timer_start, + METH_VARARGS, + "Initialize and start the timer." + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject TimerType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_macosx.Timer", /*tp_name*/ + sizeof(Timer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Timer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)Timer_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Timer_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Timer_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* 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 */ + Timer_new, /* tp_new */ +}; + static struct PyMethodDef methods[] = { {"show", (PyCFunction)show, @@ -5528,6 +5717,7 @@ if (PyType_Ready(&FigureManagerType) < 0) return; if (PyType_Ready(&NavigationToolbarType) < 0) return; if (PyType_Ready(&NavigationToolbar2Type) < 0) return; + if (PyType_Ready(&TimerType) < 0) return; m = Py_InitModule4("_macosx", methods, @@ -5540,11 +5730,13 @@ Py_INCREF(&FigureManagerType); Py_INCREF(&NavigationToolbarType); Py_INCREF(&NavigationToolbar2Type); + Py_INCREF(&TimerType); PyModule_AddObject(m, "GraphicsContext", (PyObject*) &GraphicsContextType); PyModule_AddObject(m, "FigureCanvas", (PyObject*) &FigureCanvasType); PyModule_AddObject(m, "FigureManager", (PyObject*) &FigureManagerType); PyModule_AddObject(m, "NavigationToolbar", (PyObject*) &NavigationToolbarType); PyModule_AddObject(m, "NavigationToolbar2", (PyObject*) &NavigationToolbar2Type); + PyModule_AddObject(m, "Timer", (PyObject*) &TimerType); PyOS_InputHook = wait_for_stdin; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |