在windows上扩展python(2.1)---一个实例
在win32编程中,我们经常使用timer来定时的出发一些事件。 SetTimer来创建一个定时器, 他的原型是:
UINT_PTR SetTimer(
HWND hWnd, // handle to window
UINT_PTR nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // timer procedure
);
lpTimerFunc就是定时器的回调函数。在第二篇的时候,我们也可以在python中设置回调函数。python提供这个方法
主要是用来扩展c中的一些实现。在python win32的扩展模块中。我们可以看到他的具体使用。他对timer的几个win32 API的包装
都写在timermodule.cpp(请看附件)
在这个文件中,他的整个做法和我们上两节讲过的是一样的。
extern"C" __declspec(dllexport) void
inittimer(void)
{
PyObject *dict, *module;
module = Py_InitModule("timer", timer_functions);
if (!module) /* Eeek - some serious error! */
return;
dict = PyModule_GetDict(module);
if (!dict) return; /* Another serious error!*/
timer_module_error = PyString_FromString("timer error");
PyDict_SetItemString(dict, "error", timer_module_error);
PyDict_SetItemString(dict, "__version__", PyString_FromString("0.2"));
timer_id_callback_map = PyDict_New();
}
这个是他的初始化函数。他的这个module的名字是timer。这个函数也是别导出的,所以需要__declspec(dllexport)。
在看这个映射表:
// List of functions exported by this module
static struct PyMethodDef timer_functions[] = {
{"set_timer", py_timer_set_timer, 1},
{"kill_timer", py_timer_kill_timer, 1},
#ifdef _DEBUG
{"_id_timer_map", py_timer_timer_map, 1},
#endif
{NULL, NULL}
};
我们可以看到,一共映射了3个函数,_id_timer_map是为了调试的。我们暂且不管。 其他两个是set_timer和kill_timer。
我们可以在python的timer模块里看到这两个函数。过会我们会来显示他们。
set_timer对应的c函数是py_timer_set_timer:
static PyObject *
py_timer_set_timer (PyObject * self, PyObject * args)
{
PyObject *callback;
PyObject * py_timer_id;
int elapse;
UINT timer_id;
if (!PyArg_ParseTuple (args, "iO", &elapse, &callback)) {
return NULL;
}
// make sure the callback is a valid callable object
if (!PyCallable_Check (callback)) {
PyErr_SetString (timer_module_error, "argument must be a callable object");
return NULL;
}
// create the win32 timer
Py_BEGIN_ALLOW_THREADS;
timer_id = ::SetTimer (NULL, 0, (UINT) elapse, (TIMERPROC) py_win32_timer_callback);
Py_END_ALLOW_THREADS;
if (!timer_id) {
PyErr_SetString (timer_module_error, "win32 SetTimer() failed");
return NULL;
}
py_timer_id = PyInt_FromLong((long) timer_id);
if (!py_timer_id)
return NULL;
// associate the timer id with the given callback function
if (PyObject_SetItem (timer_id_callback_map,
py_timer_id,
callback) == -1) {
::KillTimer (NULL, timer_id);
Py_DECREF(py_timer_id);
PyErr_SetString (timer_module_error,
"internal error, couldn't set timer id callback item");
return NULL;
}
// everything went ok.
return py_timer_id;
}
大部分的结构我们都已经讲过了,看这行:
PyObject_SetItem (timer_id_callback_map, py_timer_id, callback)
他将一个timerid和一个python用户设置的函数关联起来。并且把他存储到一个map中去, 这个map就是python 中的dict。
比如, map[timerid] = callback
另外, 我们需要注意这行代码:
timer_id = ::SetTimer (NULL, 0, (UINT) elapse, (TIMERPROC) py_win32_timer_callback);
这个是win32 API的调用,py_win32_timer_callback是定时器的回调函数。他的定义是这样的:
VOID CALLBACK
py_win32_timer_callback (HWND hwnd, UINT msg, UINT event, DWORD time)
{
// do we have a valid callback dictionary?
if (timer_id_callback_map) {
CEnterLeavePython _celp;
PyObject * py_event = Py_BuildValue ("i", (int) event);
// is this timer id recognized?
PyObject * callback_function =
PyDict_GetItem (timer_id_callback_map, py_event);
// call the user's function
if (callback_function) {
PyObject * callback_args = Py_BuildValue ("(il)", (int) event, (long) time);
PyObject * result =
PyEval_CallObject (callback_function, callback_args);
if (!result) {
// Is this necessary, or will python already have flagged
// an exception? Can we even catch exceptions here?
PyErr_Print();
}
// everything's ok, return
Py_XDECREF(callback_args);
Py_XDECREF(result);
Py_DECREF (py_event);
return;
}
// invalid key or callback: remove the key and kill the timer.
PyDict_DelItem(timer_id_callback_map, py_event);
Py_DECREF (py_event);
::KillTimer (NULL, event);
return;
} else {
// the id/callback map is NULL
::KillTimer (NULL, event);
}
}
他在一个callback map中根据timer id找到对应的回调函数, PyObject * py_event = Py_BuildValue ("i", (int) event);这句话
就是得到timer id。 他得到的就是在py_timer_set_timer函数里python传递过来的callback。 我们已经把他存储在map中了。
然后调用这个函数,PyEval_CallObject (callback_function, callback_args);在这里, 参数, callback_function就是我们找到的那个
回调函数。PyEval_CallObject我们已经讲过了。
py_timer_kill_timer 很简单:
static PyObject *
py_timer_kill_timer (PyObject * self, PyObject * args)
{
PyObject * py_timer_id;
if (!PyArg_ParseTuple (args, "O", &py_timer_id)) {
return NULL;
} else if (timer_id_callback_map) {
if (0 != PyDict_DelItem (timer_id_callback_map, py_timer_id)) {
return NULL;
}
}
int rc;
Py_BEGIN_ALLOW_THREADS;
rc = ::KillTimer (NULL, (int) PyInt_AsLong (py_timer_id));
Py_END_ALLOW_THREADS;
return Py_BuildValue ("i", rc);
}
他先得到这个timer id,然后从map里把他对应的这一项删除。PyDict_DelItem (timer_id_callback_map, py_timer_id)。
然后真正调用win32 API KillTimer.
在python中做测试:
>>> import timer
>>> dir(timer)
['__doc__', '__file__', '__name__', '__version__', 'error', 'kill_timer', 'set_timer']
>>> import time
>>> def testtimer(id, timeinterval):
... print time.localtime()
...
>>> def starttimer():
... start_time = time.time()
... timerid = timer.set_timer(5000, testtimer)
... print timerid
...
我们设置一个时间间隔5秒的定时器, 他的定时器回调函数是testtimer。 在c中,他将把testtimer, 和timerid
放入到一个map中去。
>>> starttimer()
23557
23557就是timerid
>>> (2004, 4, 13, 20, 42, 8, 1, 104, 0)
(2004, 4, 13, 20, 42, 13, 1, 104, 0)
>>> (2004, 4, 13, 20, 42, 18, 1, 104, 0)
(2004, 4, 13, 20, 42, 23, 1, 104, 0)
(2004, 4, 13, 20, 42, 28, 1, 104, 0)
(2004, 4, 13, 20, 42, 33, 1, 104, 0)
>>> timer.kill_timer(23557)
1
返回1表示成功kill掉timer了。
|
|