@@ -5494,6 +5494,195 @@ - (int)index
54945494 return Py_True;
54955495}
54965496
5497+ typedef struct {
5498+ PyObject_HEAD
5499+ CFRunLoopTimerRef timer;
5500+ } Timer;
5501+
5502+ static PyObject*
5503+ Timer_new (PyTypeObject* type, PyObject *args, PyObject *kwds)
5504+ {
5505+ Timer* self = (Timer*)type->tp_alloc (type, 0 );
5506+ if (!self) return NULL ;
5507+ self->timer = NULL ;
5508+ return (PyObject*) self;
5509+ }
5510+
5511+ static void
5512+ Timer_dealloc (Timer* self)
5513+ {
5514+ if (self->timer ) {
5515+ PyObject* attribute;
5516+ CFRunLoopTimerContext context;
5517+ CFRunLoopTimerGetContext (self->timer , &context);
5518+ attribute = context.info ;
5519+ Py_DECREF (attribute);
5520+ CFRunLoopRef runloop = CFRunLoopGetCurrent ();
5521+ if (runloop) {
5522+ CFRunLoopRemoveTimer (runloop, self->timer , kCFRunLoopCommonModes );
5523+ }
5524+ CFRelease (self->timer );
5525+ self->timer = NULL ;
5526+ }
5527+ self->ob_type ->tp_free ((PyObject*)self);
5528+ }
5529+
5530+ static PyObject*
5531+ Timer_repr (Timer* self)
5532+ {
5533+ return PyString_FromFormat (" Timer object %p wrapping CFRunLoopTimerRef %p " ,
5534+ (void *) self, (void *)(self->timer ));
5535+ }
5536+
5537+ static char Timer_doc[] =
5538+ " A Timer object wraps a CFRunLoopTimerRef and can add it to the event loop.\n " ;
5539+
5540+ static void timer_callback (CFRunLoopTimerRef timer, void * info)
5541+ {
5542+ PyObject* method = info;
5543+ PyGILState_STATE gstate = PyGILState_Ensure ();
5544+ PyObject* result = PyObject_CallFunction (method, NULL );
5545+ if (result==NULL ) PyErr_Print ();
5546+ PyGILState_Release (gstate);
5547+ }
5548+
5549+ static PyObject*
5550+ Timer__timer_start (Timer* self, PyObject* args)
5551+ {
5552+ CFRunLoopRef runloop;
5553+ CFRunLoopTimerRef timer;
5554+ CFRunLoopTimerContext context;
5555+ double milliseconds;
5556+ CFTimeInterval interval;
5557+ PyObject* attribute;
5558+ PyObject* failure;
5559+ runloop = CFRunLoopGetCurrent ();
5560+ if (!runloop) {
5561+ PyErr_SetString (PyExc_RuntimeError, " Failed to obtain run loop" );
5562+ return NULL ;
5563+ }
5564+ context.version = 0 ;
5565+ context.retain = 0 ;
5566+ context.release = 0 ;
5567+ context.copyDescription = 0 ;
5568+ attribute = PyObject_GetAttrString ((PyObject*)self, " _interval" );
5569+ if (attribute==NULL )
5570+ {
5571+ PyErr_SetString (PyExc_AttributeError, " Timer has no attribute '_interval'" );
5572+ return NULL ;
5573+ }
5574+ milliseconds = PyFloat_AsDouble (attribute);
5575+ failure = PyErr_Occurred ();
5576+ Py_DECREF (attribute);
5577+ if (failure) return NULL ;
5578+ attribute = PyObject_GetAttrString ((PyObject*)self, " _single" );
5579+ if (attribute==NULL )
5580+ {
5581+ PyErr_SetString (PyExc_AttributeError, " Timer has no attribute '_single'" );
5582+ return NULL ;
5583+ }
5584+ switch (PyObject_IsTrue (attribute)) {
5585+ case 1 :
5586+ interval = 0 ;
5587+ break ;
5588+ case 0 :
5589+ interval = milliseconds / 1000.0 ;
5590+ break ;
5591+ case -1 :
5592+ default :
5593+ PyErr_SetString (PyExc_ValueError, " Cannot interpret _single attribute as True of False" );
5594+ return NULL ;
5595+ }
5596+ attribute = PyObject_GetAttrString ((PyObject*)self, " _on_timer" );
5597+ if (attribute==NULL )
5598+ {
5599+ PyErr_SetString (PyExc_AttributeError, " Timer has no attribute '_on_timer'" );
5600+ return NULL ;
5601+ }
5602+ if (!PyMethod_Check (attribute)) {
5603+ PyErr_SetString (PyExc_RuntimeError, " _on_timer should be a Python method" );
5604+ return NULL ;
5605+ }
5606+ context.info = attribute;
5607+ timer = CFRunLoopTimerCreate (kCFAllocatorDefault ,
5608+ 0 ,
5609+ interval,
5610+ 0 ,
5611+ 0 ,
5612+ timer_callback,
5613+ &context);
5614+ if (!timer) {
5615+ PyErr_SetString (PyExc_RuntimeError, " Failed to create timer" );
5616+ return NULL ;
5617+ }
5618+ Py_INCREF (attribute);
5619+ if (self->timer ) {
5620+ CFRunLoopTimerGetContext (self->timer , &context);
5621+ attribute = context.info ;
5622+ Py_DECREF (attribute);
5623+ CFRunLoopRemoveTimer (runloop, self->timer , kCFRunLoopCommonModes );
5624+ CFRelease (self->timer );
5625+ }
5626+ CFRunLoopAddTimer (runloop, timer, kCFRunLoopCommonModes );
5627+ /* Don't release the timer here, since the run loop may be destroyed and
5628+ * the timer lost before we have a chance to decrease the reference count
5629+ * of the attribute */
5630+ self->timer = timer;
5631+ Py_INCREF (Py_None);
5632+ return Py_None;
5633+ }
5634+
5635+ static PyMethodDef Timer_methods[] = {
5636+ {" _timer_start" ,
5637+ (PyCFunction)Timer__timer_start,
5638+ METH_VARARGS,
5639+ " Initialize and start the timer."
5640+ },
5641+ {NULL } /* Sentinel */
5642+ };
5643+
5644+ static PyTypeObject TimerType = {
5645+ PyObject_HEAD_INIT (NULL )
5646+ 0 , /* ob_size*/
5647+ " _macosx.Timer" , /* tp_name*/
5648+ sizeof (Timer), /* tp_basicsize*/
5649+ 0 , /* tp_itemsize*/
5650+ (destructor)Timer_dealloc, /* tp_dealloc*/
5651+ 0 , /* tp_print*/
5652+ 0 , /* tp_getattr*/
5653+ 0 , /* tp_setattr*/
5654+ 0 , /* tp_compare*/
5655+ (reprfunc)Timer_repr, /* tp_repr*/
5656+ 0 , /* tp_as_number*/
5657+ 0 , /* tp_as_sequence*/
5658+ 0 , /* tp_as_mapping*/
5659+ 0 , /* tp_hash */
5660+ 0 , /* tp_call*/
5661+ 0 , /* tp_str*/
5662+ 0 , /* tp_getattro*/
5663+ 0 , /* tp_setattro*/
5664+ 0 , /* tp_as_buffer*/
5665+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
5666+ Timer_doc, /* tp_doc */
5667+ 0 , /* tp_traverse */
5668+ 0 , /* tp_clear */
5669+ 0 , /* tp_richcompare */
5670+ 0 , /* tp_weaklistoffset */
5671+ 0 , /* tp_iter */
5672+ 0 , /* tp_iternext */
5673+ Timer_methods, /* tp_methods */
5674+ 0 , /* tp_members */
5675+ 0 , /* tp_getset */
5676+ 0 , /* tp_base */
5677+ 0 , /* tp_dict */
5678+ 0 , /* tp_descr_get */
5679+ 0 , /* tp_descr_set */
5680+ 0 , /* tp_dictoffset */
5681+ 0 , /* tp_init */
5682+ 0 , /* tp_alloc */
5683+ Timer_new, /* tp_new */
5684+ };
5685+
54975686static struct PyMethodDef methods[] = {
54985687 {" show" ,
54995688 (PyCFunction)show,
@@ -5528,6 +5717,7 @@ void init_macosx(void)
55285717 if (PyType_Ready (&FigureManagerType) < 0 ) return ;
55295718 if (PyType_Ready (&NavigationToolbarType) < 0 ) return ;
55305719 if (PyType_Ready (&NavigationToolbar2Type) < 0 ) return ;
5720+ if (PyType_Ready (&TimerType) < 0 ) return ;
55315721
55325722 m = Py_InitModule4 (" _macosx" ,
55335723 methods,
@@ -5540,11 +5730,13 @@ void init_macosx(void)
55405730 Py_INCREF (&FigureManagerType);
55415731 Py_INCREF (&NavigationToolbarType);
55425732 Py_INCREF (&NavigationToolbar2Type);
5733+ Py_INCREF (&TimerType);
55435734 PyModule_AddObject (m, " GraphicsContext" , (PyObject*) &GraphicsContextType);
55445735 PyModule_AddObject (m, " FigureCanvas" , (PyObject*) &FigureCanvasType);
55455736 PyModule_AddObject (m, " FigureManager" , (PyObject*) &FigureManagerType);
55465737 PyModule_AddObject (m, " NavigationToolbar" , (PyObject*) &NavigationToolbarType);
55475738 PyModule_AddObject (m, " NavigationToolbar2" , (PyObject*) &NavigationToolbar2Type);
5739+ PyModule_AddObject (m, " Timer" , (PyObject*) &TimerType);
55485740
55495741 PyOS_InputHook = wait_for_stdin;
55505742}
0 commit comments