Difference between revisions of "Developing Python Modules with Pascal"
(Took out template) |
(Added "Using your module in a host application") |
||
Line 272: | Line 272: | ||
What we've done here is create a simple Python module that implements a Python function for adding two integers. Once you've imported the module into your Python script, you can use the function in the same way that you use built-in Python functions. | What we've done here is create a simple Python module that implements a Python function for adding two integers. Once you've imported the module into your Python script, you can use the function in the same way that you use built-in Python functions. | ||
+ | |||
+ | == Using your module in a host application == | ||
+ | |||
+ | If you have OpenOffice or NeoOffice installed, you can test running your module from a Python macro. | ||
+ | |||
+ | Copy and paste this script into a text editor and save it as file test_minmod.py, then place it in the folder specified in the script. You may have to create the python\Library1 folder under Scripts. | ||
+ | |||
+ | <pre> | ||
+ | # Python macro that tests Pascal module by creating new document and inserting module function result. | ||
+ | |||
+ | import sys, os | ||
+ | # Tell Python where to look for our Pascal module | ||
+ | if sys.platform == 'win32': | ||
+ | sys.path.append(os.path.expanduser('~\Application Data\OpenOffice.org2\user\Scripts\python\Library1')) | ||
+ | elif sys.platform == 'darwin': | ||
+ | sys.path.append(os.path.expanduser('~/Library/Preferences/NeoOffice-2.2/user/Scripts/python/Library1')) | ||
+ | |||
+ | # Import Pascal module that contains SumTwoIntegers function | ||
+ | import PyMinMod | ||
+ | |||
+ | import uno | ||
+ | |||
+ | def TestMinMod(): | ||
+ | ctx = uno.getComponentContext() | ||
+ | smgr = ctx.ServiceManager | ||
+ | desktop = smgr.createInstance('com.sun.star.frame.Desktop') | ||
+ | doc = desktop.loadComponentFromURL('private:factory/swriter', '_blank', 0, ()) | ||
+ | textCursor = doc.Text.createTextCursor() | ||
+ | doc.Text.insertString(textCursor, 'Sum of 1 + 2 = ' + str(PyMinMod.SumTwoIntegers(1, 2)), 0) | ||
+ | </pre> | ||
+ | |||
+ | Since OO probably includes Python 2.3, compile your module against 2.3 by adding this on the Lazarus Compiler Options dialog's Other tab: | ||
+ | |||
+ | <pre> | ||
+ | -dUSE_PYTHON23 | ||
+ | </pre> | ||
+ | |||
+ | With Delphi, enter USE_PYTHON23 on the Project Options dialog's Directories/Conditionals tab. | ||
+ | |||
+ | Rename your compiled module if necessary, then place it in the same folder as test_minmod.py. Now test running it from OO by choosing Tools | Macros | Organize Macros | Python and running the TestMinMod macro. |
Revision as of 00:24, 3 January 2009
Introduction
Python is a popular scripting language that is often used to add functionality to other applications such as OpenOffice and Quantum GIS. Your computer may already have a version of Python installed. If not, you can download Python from the official Website: http://www.python.org/.
You can extend Python by developing compiled libraries (called modules) that add functions to Python. This topic discusses how to create a library with Pascal (Delphi or Free Pascal).
Minimum Python API
Copy and paste this code into a text editor and save it as file PyAPI.pas:
unit PyAPI; { Minimum set of Python function declarations for module libraries. Author: Phil (MacPgmr at fastermac.net). To add other Python function declarations, see the Python header files (.h) included with every Python distribution. } {$IFDEF FPC} {$MODE Delphi} {$ENDIF} interface const {$IFDEF MSWINDOWS} {$IFDEF USE_PYTHON23} PythonLib = 'python23.dll'; {$ELSE} PythonLib = 'python25.dll'; {$ENDIF} {$ENDIF} {$IFDEF LINUX} {$IFDEF USE_PYTHON23} PythonLib = 'python23.so'; {$ELSE} PythonLib = 'python25.so'; {$ENDIF} {$ENDIF} {$IFDEF DARWIN} PythonLib = ''; //Link against Python.framework (-k-framework -kPython). // To link Python 2.3, add -k-F/System/Library/Frameworks {$ENDIF} type PyMethodDef = packed record name : PChar; //Python function name meth : Pointer; //Address of function that implements it flags : Integer; //METH_xxx flags; describe function's arguments doc : PChar; //Description of funtion end; PyObject = Pointer; const {$IFDEF USE_PYTHON23} PYTHON_API_VERSION = 1012; //Also used with Python 2.4 {$ELSE} PYTHON_API_VERSION = 1013; {$ENDIF} METH_VARARGS = 1; function Py_InitModule( name : PChar; var methods : PyMethodDef; doc : PChar = nil; self : PyObject = nil; apiver : LongInt = PYTHON_API_VERSION) : PyObject; cdecl; external PythonLib name 'Py_InitModule4'; function PyArg_ParseTuple(args : PyObject; format : PChar) : Integer; cdecl; varargs; external PythonLib; //Note varargs allows us to simulate C variable number of arguments (...). function PyInt_FromLong(along : LongInt) : PyObject; cdecl; external PythonLib; implementation end.
If you have a different version of Python, define USE_PYTHON23 to link against version 2.3 or just change the library name (PythonLib). If you need other Python API functions, just add them to PyAPI.pas following the example of PyInt_FromLong.
A simple module example
Here is a simple library that uses this Python API unit. Copy and paste this code into a text editor and save it as file PyMinMod.dpr:
library PyMinMod; { Minimal Python module (library) that includes a single function. Author: Phil (MacPgmr at fastermac.net). For a good explanation of modules from a C perspective, see: http://superjared.com/entry/anatomy-python-c-module/ To compile this module: - With Delphi: Open this .dpr file and compile. - With Lazarus: Open .lpi file and compile. To deploy module: - With Delphi: Rename compiled .dll to .pyd. - With Lazarus on Windows: Rename compiled .so to .pyd. - With Lazarus on OS X and Linux: .so extension is okay. } uses SysUtils, PyAPI; function SumTwoIntegers(Self : PyObject; Args : PyObject) : PyObject; cdecl; var Arg1 : Integer; Arg2 : Integer; begin PyArg_ParseTuple(Args, 'ii', @Arg1, @Arg2); //Get the two input arguments Result := PyInt_FromLong(Arg1 + Arg2); //Add them together and return sum end; var Methods : packed array [0..1] of PyMethodDef; procedure initPyMinMod; cdecl; begin Methods[0].name := 'SumTwoIntegers'; Methods[0].meth := @SumTwoIntegers; Methods[0].flags := METH_VARARGS; Methods[0].doc := 'Tests argument passing to and from module function'; Methods[1].name := nil; Methods[1].meth := nil; Methods[1].flags := 0; Methods[1].doc := nil; Py_InitModule('PyMinMod', Methods[0]); end; exports initPyMinMod; end.
You can add more functions to the module by following the example in initPyMinMod.
With Delphi, just open PyMinMod.dpr and compile; with Lazarus, you'll probably want to create a project file. You can do that yourself or just copy and paste this project file into a text editor and save it as file PyMinMod.lpi:
<?xml version="1.0"?> <CONFIG> <ProjectOptions> <PathDelim Value="/"/> <Version Value="6"/> <General> <MainUnit Value="0"/> <IconPath Value="./"/> <TargetFileExt Value=".exe"/> <UseAppBundle Value="False"/> <ActiveEditorIndexAtStart Value="0"/> </General> <PublishOptions> <Version Value="2"/> <IgnoreBinaries Value="False"/> <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/> <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/> </PublishOptions> <RunParams> <local> <FormatVersion Value="1"/> <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/> </local> </RunParams> <Units Count="1"> <Unit0> <Filename Value="PyMinMod.dpr"/> <IsPartOfProject Value="True"/> <UnitName Value="PyMinMod"/> <CursorPos X="1" Y="1"/> <TopLine Value="1"/> <EditorIndex Value="0"/> <UsageCount Value="20"/> <Loaded Value="True"/> <SyntaxHighlighter Value="Delphi"/> </Unit0> </Units> <JumpHistory Count="0" HistoryIndex="-1"/> </ProjectOptions> <CompilerOptions> <Version Value="8"/> <Target> <Filename Value="PyMinMod.so"/> </Target> <Parsing> <SyntaxOptions> <SyntaxMode Value="Delphi"/> <CStyleOperator Value="False"/> <AllowLabel Value="False"/> <CPPInline Value="False"/> </SyntaxOptions> </Parsing> <CodeGeneration> <Checks> <IOChecks Value="True"/> <RangeChecks Value="True"/> <OverflowChecks Value="True"/> <StackChecks Value="True"/> </Checks> </CodeGeneration> <Linking> <Options> <PassLinkerOptions Value="True"/> <LinkerOptions Value="-framework Python"/> <Win32> <GraphicApplication Value="True"/> </Win32> <ExecutableType Value="Library"/> </Options> </Linking> <Other> <CompilerPath Value="$(CompPath)"/> </Other> </CompilerOptions> <Debugging> <Exceptions Count="2"> <Item1> <Name Value="ECodetoolError"/> </Item1> <Item2> <Name Value="EFOpenError"/> </Item2> </Exceptions> </Debugging> </CONFIG>
Once you've compiled the module, rename it if necessary per the comments in PyMinMod.dpr. Then test the module by creating a simple test.py file that contains these two lines:
import PyMinMod print "Value returned by SumTwoIntegers: " + str(PyMinMod.SumTwoIntegers(1, 2))
Note that Python is case sensitive so if your compiled module is in lower-case, change the "PyMinMod" references accordingly.
Now open a terminal window and run the script like this:
python test.py
The script should output the following line:
Value returned by SumTwoIntegers: 3
What we've done here is create a simple Python module that implements a Python function for adding two integers. Once you've imported the module into your Python script, you can use the function in the same way that you use built-in Python functions.
Using your module in a host application
If you have OpenOffice or NeoOffice installed, you can test running your module from a Python macro.
Copy and paste this script into a text editor and save it as file test_minmod.py, then place it in the folder specified in the script. You may have to create the python\Library1 folder under Scripts.
# Python macro that tests Pascal module by creating new document and inserting module function result. import sys, os # Tell Python where to look for our Pascal module if sys.platform == 'win32': sys.path.append(os.path.expanduser('~\Application Data\OpenOffice.org2\user\Scripts\python\Library1')) elif sys.platform == 'darwin': sys.path.append(os.path.expanduser('~/Library/Preferences/NeoOffice-2.2/user/Scripts/python/Library1')) # Import Pascal module that contains SumTwoIntegers function import PyMinMod import uno def TestMinMod(): ctx = uno.getComponentContext() smgr = ctx.ServiceManager desktop = smgr.createInstance('com.sun.star.frame.Desktop') doc = desktop.loadComponentFromURL('private:factory/swriter', '_blank', 0, ()) textCursor = doc.Text.createTextCursor() doc.Text.insertString(textCursor, 'Sum of 1 + 2 = ' + str(PyMinMod.SumTwoIntegers(1, 2)), 0)
Since OO probably includes Python 2.3, compile your module against 2.3 by adding this on the Lazarus Compiler Options dialog's Other tab:
-dUSE_PYTHON23
With Delphi, enter USE_PYTHON23 on the Project Options dialog's Directories/Conditionals tab.
Rename your compiled module if necessary, then place it in the same folder as test_minmod.py. Now test running it from OO by choosing Tools | Macros | Organize Macros | Python and running the TestMinMod macro.