📄 00000005.htm
字号:
<HTML><HEAD> <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人: kafa (staring at the sun), 信区: Linux <BR>标 题: 用python扩展模块来做海龟画图 <BR>发信站: BBS 水木清华站 (Wed Jan 24 01:26:06 2001) WWW-POST <BR> <BR>
<BR> 我们常常希望把某个程序内部的功能通过脚本的形式暴露出来,使用户可以跟据自己的需 <BR>要进行二次开发,例如,我们可以用Zmud的脚本语言来编机器人自动练功完成任务,Quake里 <BR>可以用脚本描述和设计关卡,这样关卡设计就可以交给专门的人来作,减轻了程序员的工作 <BR>量.但是,要自己开发一个成熟的脚本语言本身并不是一件简单的事,所以我们可以利用已 <BR>有的各种脚本语言,例如Tcl/Tk, Perl, Python, Guile, Ruby等.它们都提供了把自己的 <BR>解释器嵌入应用程序的C语言的API,我们可以直接利用它们的解释器来作语法分析等"脏活 <BR>累活",而自己只需把需要暴露的功能作成脚本语言的扩展模块.这种方法简单灵活,正变的 <BR>越来越流行,微软也推出了script engine提供类似的功能.
<BR>
<BR> python是一种解释型的,面向对象的编程语言.由于它的C API清楚简单,所以作为嵌入式 <BR>的脚本语言非常合适.不知道大家有没有用过Logo?用过的人肯定记得那个海龟吧?呵呵,今 <BR>天我们就用python来作一个简单的海龟画图程序.为了简单,我们把它做成一个python的扩 <BR>展模块.我先解释一下,利用python的CAPI可以作两件事:扩展(extend)和嵌入(embed).扩 <BR>展是用C编写python的模块使你在python里可以调用,简单说就是把C嵌入python;而嵌入是 <BR>把python解释器植入到C写的应用程序里面,使程序可以解释执行python的脚本程序.扩展 <BR>和嵌入都是在C语言和脚本语言之间作数据转换的工作,但是把python嵌入应用程序的方式 <BR>很多,用起来非常灵活,而且往往也要涉及到扩展,所以说嵌入要比扩展复杂一些,不过原理 <BR>是一样的.
<BR>
<BR> 好,我们先写一个没有python的简单程序,实现必需的功能.程序如下:
<BR>
<BR>/*------------------------------------------------------------------------*/
<BR>
<BR>/* tortoise1.c */
<BR>
<BR>#include <unistd.h>
<BR>
<BR>#include <X11/Xlib.h>
<BR>#define WINDOW_SIZE 500
<BR>
<BR>Display *theDisplay;
<BR>Window theWindow;
<BR>Screen *theScreen;
<BR>GC theGC;
<BR>double currentX;
<BR>double currentY;
<BR>double currentDirection;
<BR>int penDown;
<BR>
<BR>#include <math.h>
<BR>#define DEGREES_TO_RADIANS (3.1415926535897932384626433832795029L/180.0)
<BR>
<BR>/* 复位. 海龟回到窗口中心,落笔,面向北方 */
<BR>void tortoise_reset()
<BR>{
<BR> currentX = currentY = WINDOW_SIZE/2;
<BR> currentDirection = 0.0;
<BR> penDown = 1;
<BR>}
<BR>
<BR>/* 落笔 */
<BR>void tortoise_pendown()
<BR>{
<BR> penDown = 1;
<BR>}
<BR>
<BR>/* 抬笔 */
<BR>void tortoise_penup()
<BR>{
<BR> penDown = 0;
<BR>}
<BR>
<BR>/* 转弯 */
<BR>void tortoise_turn(int degrees)
<BR>{
<BR> currentDirection += (double)degrees;
<BR>}
<BR>
<BR>/* 前进 */
<BR>void tortoise_move(int steps)
<BR>{
<BR> double newX, newY;
<BR> /* first work out the new endpoint */
<BR> newX = currentX + sin(currentDirection*DEGREES_TO_RADIANS)*(double)steps;
<BR> <BR> newY = currentY - cos(currentDirection*DEGREES_TO_RADIANS)*(double)steps;
<BR> <BR> /* if the pen is down, draw a line */
<BR> if (penDown) XDrawLine(theDisplay, theWindow, theGC,
<BR> (int)currentX, (int)currentY, (int)newX, <BR>(int)newY);
<BR> /* in either case, move the tortoise */
<BR> currentX = newX;
<BR> currentY = newY;
<BR>}
<BR>
<BR>int main(int argc, char *argv[])
<BR>{
<BR> theDisplay = XOpenDisplay(NULL);
<BR> XSynchronize(theDisplay, True);
<BR> theScreen = DefaultScreenOfDisplay(theDisplay);
<BR> theWindow = XCreateSimpleWindow(theDisplay, RootWindowOfScreen(theScreen), <BR>
<BR> 0, 0,
<BR> WINDOW_SIZE, WINDOW_SIZE, 0,
<BR> BlackPixelOfScreen(theScreen),
<BR> WhitePixelOfScreen(theScreen));
<BR> theGC = XCreateGC(theDisplay, theWindow, 0L, NULL);
<BR> XSetForeground(theDisplay, theGC, BlackPixelOfScreen(theScreen));
<BR> XMapWindow(theDisplay,theWindow);
<BR>
<BR> /* 画个简单的正方形测试一下 */
<BR> tortoise_reset();
<BR> {
<BR> int ii;
<BR> tortoise_pendown();
<BR> for (ii=0; ii<4; ii++) {
<BR> tortoise_move(100);
<BR> tortoise_turn(90.0);
<BR> }
<BR> /* sleep for a bit so the window stays visible */
<BR> sleep(10);
<BR> }
<BR> return 0;
<BR>}
<BR>
<BR>/*------------------------------------------------------------------------*/
<BR>
<BR> 你可以用
<BR> gcc -o t1 tortoise1.c -I/usr/X11R6/include -lX11 -L/usr/X11R6/lib -lm
<BR> 编译它,运行一下看看.
<BR> 在上面这个程序里,如果要画个什么图形就要修改程序,然后重新编译调试运行,这太麻烦 <BR>了!下面我们把它做成python的扩展模块.给python写扩展模块其实是一件很简单很模式化 <BR>的任务,所以Python提供了一个模块框架生成器modulator,可以替你生成基本的扩展模块 <BR>的程序框架,你只需在这个基础上面把自己的具体实现代码填上就可以了.它一般放在 <BR>python源程序的Tools/modulator目录下面.如果你没有找到,就去下一个python的源代码 <BR>然后编译一下就有了.
<BR>
<BR> 好,我们先运行modulator,程序的界面很简单.我们先在Module输入条里填上扩展模块的 <BR>名字--tortoise,然后在Add method输入条里加入以下几个方法名: reset, pendown, <BR>penup, turn, move. 最后按一下Generate code...按钮生成c代码,生成文件名最好叫做 <BR>tortoisemodule.c以符合python扩展模块的命名习惯.退出modulator,可以看到在当前目 <BR>录下已经多了一个叫tortoisemodule.c的文件,内容如下(中文是我加的注释):
<BR>
<BR>/*------------------------------------------------------------------------*/
<BR>
<BR>/* 所有的python CAPI需要的常量,宏和函数原型都包含在这个头文件里 */
<BR>#include "Python.h"
<BR>
<BR>/* 定义一个本模块用的例外对象 */
<BR>static PyObject *ErrorObject;
<BR>
<BR>/* reset函数的doc */
<BR>static char tortoise_reset__doc__[] =
<BR>""
<BR>;
<BR>
<BR>/* 返回PyObject类型的指针,指向具体的python返回对象 */
<BR>static PyObject *
<BR>tortoise_reset(self, args)
<BR> PyObject *self; /* 这个参数只有在做python扩展类型(extend type)
<BR> 的时候才会用到,这里我们不用 */
<BR> PyObject *args; /* 传入的是python的tuple类型的对象,里面包含需要的参数 */
<BR>{
<BR>
<BR> /* 从tuple里提取出真正的C的参数 */
<BR> if (!PyArg_ParseTuple(args, ""))
<BR> return NULL;
<BR> /* 返回Py_None类型的对象表示函数正常返回,没有错误发生 */
<BR> Py_INCREF(Py_None);
<BR> return Py_None;
<BR>}
<BR>
<BR>static char tortoise_pendown__doc__[] =
<BR>""
<BR>;
<BR>
<BR>static PyObject *
<BR>tortoise_pendown(self, args)
<BR> PyObject *self; /* Not used */
<BR> PyObject *args;
<BR>{
<BR>
<BR> if (!PyArg_ParseTuple(args, ""))
<BR> return NULL;
<BR> Py_INCREF(Py_None);
<BR> return Py_None;
<BR>}
<BR>
<BR>static char tortoise_penup__doc__[] =
<BR>""
<BR>;
<BR>
<BR>static PyObject *
<BR>tortoise_penup(self, args)
<BR> PyObject *self; /* Not used */
<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -