moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kig / scripting / python_scripter.cc
blob523067f501d4663340d2d92987b2891cbe4bdd1e
1 // Copyright (C) 2003 Dominique Devriese <devriese@kde.org>
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License
5 // as published by the Free Software Foundation; either version 2
6 // of the License, or (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16 // 02111-1307, USA.
18 #include "python_scripter.h"
20 #include <iostream>
21 #include <string>
22 #include <Python.h>
23 #include <boost/python.hpp>
24 #include <boost/mpl/bool.hpp>
26 #include "../misc/common.h"
27 #include "../misc/coordinate.h"
28 #include "../misc/cubic-common.h"
29 #include "../misc/kigtransform.h"
30 #include "../objects/bogus_imp.h"
31 #include "../objects/common.h"
32 #include "../objects/circle_imp.h"
33 #include "../objects/cubic_imp.h"
34 #include "../objects/line_imp.h"
35 #include "../objects/other_imp.h"
36 #include "../objects/point_imp.h"
38 using namespace boost::python;
40 BOOST_PYTHON_MODULE_INIT( kig )
42 class_<Coordinate>( "Coordinate" )
43 .def( init<double, double>() )
44 .def( init<const Coordinate&>() )
45 .def( "invalidCoord", &Coordinate::invalidCoord )
46 .staticmethod( "invalidCoord" )
47 .def( "valid", &Coordinate::valid )
48 .def( "distance", &Coordinate::distance )
49 .def( "length", &Coordinate::length )
50 .def( "squareLength", &Coordinate::squareLength )
51 .def( "orthogonal", &Coordinate::orthogonal )
52 .def( "round", &Coordinate::round )
53 .def( "normalize", &Coordinate::normalize )
54 .def( -self )
55 // .def( self = self )
56 .def( self += self )
57 .def( self -= self )
58 .def( self *= other<double>() )
59 .def( self *= other<int>() )
60 .def( self /= other<double>() )
61 .def( self / other<double>() )
62 .def( self + self )
63 .def( self - self )
64 .def( self * other<double>() )
65 .def( other<double>() * self )
66 .def( self * self )
67 .def_readwrite( "x", &Coordinate::x )
68 .def_readwrite( "y", &Coordinate::y )
71 class_<LineData>( "LineData" )
72 .def( init<Coordinate, Coordinate>() )
73 .def( "dir", &LineData::dir )
74 .def( "length", &LineData::length )
75 .def( "isParallelTo", &LineData::isParallelTo )
76 .def_readwrite( "a", &LineData::a )
77 .def_readwrite( "b", &LineData::b )
80 // we need this cause Transformation::apply is overloaded and
81 // otherwise using Transformation::apply would be ambiguous..
82 const Coordinate (Transformation::*transformapplyfunc)( const Coordinate& ) const = &Transformation::apply;
83 class_<Transformation>( "Transformation", no_init )
84 .def( "apply", transformapplyfunc )
85 .def( "isHomothetic", &Transformation::isHomothetic )
86 .def( "inverse", &Transformation::inverse )
87 .def( "identity", &Transformation::identity )
88 .def( "translation", &Transformation::translation )
89 .def( "rotation", &Transformation::rotation )
90 .def( "pointReflection", &Transformation::pointReflection )
91 .def( "lineReflection", &Transformation::lineReflection )
92 .def( "castShadow", &Transformation::castShadow )
93 .def( "projectiveRotation", &Transformation::projectiveRotation )
94 .def( "scalingOverPoint", &Transformation::scalingOverPoint )
95 .def( "scalingOverLine", &Transformation::scalingOverLine )
96 .def( self * self )
97 .def( self == self )
98 .staticmethod( "identity" )
99 .staticmethod( "translation" )
100 .staticmethod( "rotation" )
101 .staticmethod( "pointReflection" )
102 .staticmethod( "lineReflection" )
103 .staticmethod( "castShadow" )
104 .staticmethod( "projectiveRotation" )
105 .staticmethod( "scalingOverPoint" )
106 .staticmethod( "scalingOverLine" )
109 class_<ObjectImpType, boost::noncopyable>( "ObjectType", no_init )
110 .def( "fromInternalName", &ObjectImpType::typeFromInternalName,
111 return_value_policy<reference_existing_object>() )
112 .staticmethod( "fromInternalName" )
113 .def( "inherits", &ObjectImpType::inherits )
114 .def( "internalName", &ObjectImpType::internalName )
115 .def( "translatedName", &ObjectImpType::translatedName )
116 .def( "selectStatement", &ObjectImpType::selectStatement )
117 .def( "removeAStatement", &ObjectImpType::removeAStatement )
118 .def( "addAStatement", &ObjectImpType::addAStatement )
119 .def( "moveAStatement", &ObjectImpType::moveAStatement )
120 .def( "attachToThisStatement", &ObjectImpType::attachToThisStatement )
123 class_<ObjectImp, boost::noncopyable>( "Object", no_init )
124 .def( "stype", &ObjectImp::stype,
125 return_value_policy<reference_existing_object>() )
126 .staticmethod( "stype" )
127 .def( "inherits", &ObjectImp::inherits )
128 .def( "transform", &ObjectImp::transform,
129 return_value_policy<manage_new_object>() )
130 .def( "valid", &ObjectImp::valid )
131 .def( "copy", &ObjectImp::copy,
132 return_value_policy<manage_new_object>() )
133 .def( "equals", &ObjectImp::equals )
136 class_<CurveImp, bases<ObjectImp>, boost::noncopyable>( "Curve", no_init )
137 .def( "stype", &CurveImp::stype,
138 return_value_policy<reference_existing_object>() )
139 .staticmethod( "stype" )
140 // .def( "getParam", &CurveImp::getParam )
141 // .def( "getPoint", &CurveImp::getPoint );
143 class_<PointImp, bases<ObjectImp> >( "Point", init<Coordinate>() )
144 .def( "stype", &PointImp::stype,
145 return_value_policy<reference_existing_object>() )
146 .staticmethod( "stype" )
147 .def( "coordinate", &PointImp::coordinate,
148 return_internal_reference<1>() )
149 .def( "setCoordinate", &PointImp::setCoordinate )
152 class_<AbstractLineImp, bases<CurveImp>, boost::noncopyable >( "AbstractLine", no_init )
153 .def( "stype", &AbstractLineImp::stype,
154 return_value_policy<reference_existing_object>() )
155 .staticmethod( "stype" )
156 .def( "slope", &AbstractLineImp::slope )
157 .def( "equationString", &AbstractLineImp::equationString )
158 .def( "data", &AbstractLineImp::data )
161 class_<SegmentImp, bases<AbstractLineImp> >( "Segment", init<Coordinate, Coordinate>() )
162 .def( "stype", &SegmentImp::stype,
163 return_value_policy<reference_existing_object>() )
164 .staticmethod( "stype" )
165 .def( init<LineData>() )
166 .def( "length", &SegmentImp::length )
169 class_<RayImp, bases<AbstractLineImp> >( "Ray", init<Coordinate, Coordinate>() )
170 .def( "stype", &RayImp::stype,
171 return_value_policy<reference_existing_object>() )
172 .staticmethod( "stype" )
173 .def( init<LineData>() )
176 class_<LineImp, bases<AbstractLineImp> >( "Line", init<Coordinate, Coordinate>() )
177 .def( "stype", &LineImp::stype,
178 return_value_policy<reference_existing_object>() )
179 .staticmethod( "stype" )
180 .def( init<LineData>() )
183 class_<ConicCartesianData>( "ConicCartesianData", init<double,double,double,double,double,double>() )
184 .def( init<ConicPolarData>() )
185 .def( "invalidData", &ConicCartesianData::invalidData )
186 .staticmethod( "invalidData" )
187 .def( "valid", &ConicCartesianData::valid )
188 // .def( init<double[6]>() )
189 // .def_readwrite( "coeffs", &ConicCartesianData::coeffs )
192 class_<ConicPolarData>( "ConicPolarData", init<Coordinate, double, double, double>() )
193 .def( init<ConicCartesianData>() )
194 .def_readwrite( "focus1", &ConicPolarData::focus1 )
195 .def_readwrite( "pdimen", &ConicPolarData::pdimen )
196 .def_readwrite( "ecostheta0", &ConicPolarData::ecostheta0 )
197 .def_readwrite( "esintheta0", &ConicPolarData::esintheta0 )
200 class_<ConicImp, bases<CurveImp>, boost::noncopyable >( "Conic", no_init )
201 .def( "stype", &ConicImp::stype,
202 return_value_policy<reference_existing_object>() )
203 .staticmethod( "stype" )
204 .def( "conicType", &ConicImp::conicType )
205 // .def( "conicTypeString", &ConicImp::conicTypeString )
206 // .def( "cartesianEquationString", &ConicImp::cartesianEquationString )
207 // .def( "polarEquationString", &ConicImp::polarEquationString )
208 .def( "cartesianData", &ConicImp::cartesianData )
209 .def( "polarData", &ConicImp::polarData )
210 .def( "focus1", &ConicImp::focus1 )
211 .def( "focus2", &ConicImp::focus2 )
214 class_<ConicImpCart, bases<ConicImp> >( "CartesianConic", init<ConicCartesianData>() )
216 class_<ConicImpPolar, bases<ConicImp> >( "PolarConic", init<ConicPolarData>() )
219 class_<CircleImp, bases<ConicImp> >( "Circle", init<Coordinate, double>() )
220 .def( "stype", &CircleImp::stype,
221 return_value_policy<reference_existing_object>() )
222 .staticmethod( "stype" )
223 .def( "center", &CircleImp::center )
224 .def( "radius", &CircleImp::radius )
225 .def( "squareRadius", &CircleImp::squareRadius )
226 .def( "surface", &CircleImp::surface )
227 .def( "circumference", &CircleImp::circumference )
230 class_<VectorImp, bases<CurveImp> >( "Vector", init<Coordinate, Coordinate>() )
231 .def( "stype", &VectorImp::stype,
232 return_value_policy<reference_existing_object>() )
233 .staticmethod( "stype" )
234 .def( "length", &VectorImp::length )
235 .def( "dir", &VectorImp::dir )
236 .def( "data", &VectorImp::data )
239 class_<AngleImp, bases<ObjectImp> >( "Angle", init<Coordinate, double, double>() )
240 .def( "stype", &AngleImp::stype,
241 return_value_policy<reference_existing_object>() )
242 .staticmethod( "stype" )
243 .def( "size", &AngleImp::size )
244 .def( "point", &AngleImp::point )
245 .def( "startAngle", &AngleImp::startAngle )
246 .def( "angle", &AngleImp::angle )
249 class_<ArcImp, bases<ObjectImp> >( "Arc", init<Coordinate, double, double, double>() )
250 .def( "stype", &ArcImp::stype,
251 return_value_policy<reference_existing_object>() )
252 .staticmethod( "stype" )
253 .def( "startAngle", &ArcImp::startAngle )
254 .def( "angle", &ArcImp::angle )
255 .def( "radius", &ArcImp::radius )
256 .def( "center", &ArcImp::center )
257 .def( "firstEndPoint", &ArcImp::firstEndPoint )
258 .def( "secondEndPoint", &ArcImp::secondEndPoint )
259 .def( "sectorSurface", &ArcImp::sectorSurface )
262 class_<BogusImp, bases<ObjectImp>, boost::noncopyable >( "BogusObject", no_init )
263 .def( "stype", &BogusImp::stype,
264 return_value_policy<reference_existing_object>() )
265 .staticmethod( "stype" )
268 class_<InvalidImp, bases<BogusImp> >( "InvalidObject", init<>() )
269 .def( "stype", &InvalidImp::stype,
270 return_value_policy<reference_existing_object>() )
271 .staticmethod( "stype" )
274 class_<DoubleImp, bases<BogusImp> >( "DoubleObject", init<double>() )
275 .def( "stype", &DoubleImp::stype,
276 return_value_policy<reference_existing_object>() )
277 .staticmethod( "stype" )
278 .def( "data", &DoubleImp::data )
279 .def( "setData", &DoubleImp::setData )
282 class_<IntImp, bases<BogusImp> >( "IntObject", init<int>() )
283 .def( "stype", &IntImp::stype,
284 return_value_policy<reference_existing_object>() )
285 .staticmethod( "stype" )
286 .def( "data", &IntImp::data )
287 .def( "setData", &IntImp::setData )
290 class_<StringImp, bases<BogusImp> >( "StringObject", no_init )
291 .def( "stype", &StringImp::stype,
292 return_value_policy<reference_existing_object>() )
293 .staticmethod( "stype" )
294 // .def( "data", &StringImp::data )
295 // .def( "setData", &StringImp::setData )
298 class_<TestResultImp, bases<BogusImp> >( "TestResultObject", no_init )
299 .def( "stype", &TestResultImp::stype,
300 return_value_policy<reference_existing_object>() )
301 .staticmethod( "stype" )
302 // .def( "data", &TestResultImp::data )
305 // class_<TextImp, bases<ObjectImp> >( "Text", init<string, Coordinate, bool>() )
306 // .def( "stype", &TextImp::stype,
307 // return_value_policy<reference_existing_object>() )
308 // .staticmethod( "stype" )
309 // .def( "text", &TextImp::text )
310 // .def( "coordinate", &TextImp::coordinate )
311 // .def( "hasFrame", &TextImp::hasFrame )
312 // ;
314 class_<CubicCartesianData>( "CubicCartesianData", init<double,double,double,double,double,double,double,double,double,double>() )
315 .def( "invalidData", &CubicCartesianData::invalidData )
316 .staticmethod( "invalidData" )
317 .def( "valid", &CubicCartesianData::valid )
318 // .def( init<double[10]>() )
319 // .def_readwrite( "coeffs", &CubicCartesianData::coeffs )
322 class_<CubicImp, bases<CurveImp> >( "Cubic", init<CubicCartesianData>() )
323 .def( "stype", &CubicImp::stype,
324 return_value_policy<reference_existing_object>() )
325 .staticmethod( "stype" )
326 .def( "data", &CubicImp::data )
331 PythonScripter* PythonScripter::instance()
333 static PythonScripter t;
334 return &t;
337 class PythonScripter::Private
339 public:
340 dict mainnamespace;
343 // allocates a new string using new [], and copies contents into it..
344 static char* newstring( const char* contents )
346 char* ret = new char[strlen( contents ) + 1];
347 strcpy( ret, contents );
348 return ret;
351 PythonScripter::PythonScripter()
353 d = new Private;
355 // tell the python interpreter about our API..
357 // the newstring stuff is to prevent warnings about conversion from
358 // const char* to char*..
359 char* s = newstring( "kig" );
360 PyImport_AppendInittab( s, initkig );
361 // we can't delete this yet, since python keeps a pointer to it..
362 // This means we have a small but harmless memory leak here, but it
363 // doesn't hurt at all, since it could only be freed at the end of
364 // the program, at which time it is freed by the system anyway if we
365 // don't do it..
366 //delete [] s;
368 Py_Initialize();
370 s = newstring( "import math; from math import *;" );
371 PyRun_SimpleString( s );
372 delete [] s;
373 s = newstring( "import kig; from kig import *;" );
374 PyRun_SimpleString( s );
375 delete [] s;
376 s = newstring( "import traceback;" );
377 PyRun_SimpleString( s );
378 delete [] s;
380 // find the main namespace..
382 s = newstring( "__main__" );
383 handle<> main_module( borrowed( PyImport_AddModule( s ) ) );
384 delete [] s;
386 handle<> mnh(borrowed( PyModule_GetDict(main_module.get()) ));
387 d->mainnamespace = extract<dict>( mnh.get() );
390 PythonScripter::~PythonScripter()
392 PyErr_Clear();
393 Py_Finalize();
394 delete d;
397 class CompiledPythonScript::Private
399 public:
400 int ref;
401 object calcfunc;
402 // TODO
403 // object movefunc;
406 ObjectImp* CompiledPythonScript::calc( const Args& args, const KigDocument& )
408 return PythonScripter::instance()->calc( *this, args );
411 CompiledPythonScript::~CompiledPythonScript()
413 --d->ref;
414 if ( d->ref == 0 )
415 delete d;
418 CompiledPythonScript::CompiledPythonScript( Private* ind )
419 : d( ind )
421 ++d->ref;
424 CompiledPythonScript PythonScripter::compile( const char* code )
426 clearErrors();
427 dict retdict;
428 bool error = false;
431 (void) PyRun_String( const_cast<char*>( code ), Py_file_input,
432 d->mainnamespace.ptr(), retdict.ptr() );
434 catch( ... )
436 error = true;
438 error |= static_cast<bool>( PyErr_Occurred() );
439 if ( error )
441 saveErrors();
442 retdict.clear();
445 // debugging stuff, removed.
446 // std::string dictstring = extract<std::string>( str( retdict ) );
448 CompiledPythonScript::Private* ret = new CompiledPythonScript::Private;
449 ret->ref = 0;
450 ret->calcfunc = retdict.get( "calc" );
451 return CompiledPythonScript( ret );
454 CompiledPythonScript::CompiledPythonScript( const CompiledPythonScript& s )
455 : d( s.d )
457 ++d->ref;
460 std::string PythonScripter::lastErrorExceptionType() const
462 return lastexceptiontype;
465 std::string PythonScripter::lastErrorExceptionValue() const
467 return lastexceptionvalue;
470 std::string PythonScripter::lastErrorExceptionTraceback() const
472 return lastexceptiontraceback;
475 ObjectImp* PythonScripter::calc( CompiledPythonScript& script, const Args& args )
477 clearErrors();
478 object calcfunc = script.d->calcfunc;
481 std::vector<object> objectvect;
482 objectvect.reserve( args.size() );
484 for ( int i = 0; i < (int) args.size(); ++i )
486 object o( boost::ref( *args[i] ) );
487 objectvect.push_back( o );
490 handle<> argstuph( PyTuple_New( args.size() ) );
491 for ( int i = 0; i < (int) objectvect.size(); ++i )
493 PyTuple_SetItem( argstuph.get(), i, (objectvect.begin() +i)->ptr() );
495 tuple argstup( argstuph );
497 handle<> reth( PyEval_CallObject( calcfunc.ptr(), argstup.ptr() ) );
498 // object resulto = calcfunc( argstup );
499 // handle<> reth( PyEval_CallObject( calcfunc.ptr(), args ) );
500 object resulto( reth );
502 extract<ObjectImp&> result( resulto );
503 if( ! result.check() ) return new InvalidImp;
504 else
506 ObjectImp& ret = result();
507 return ret.copy();
510 catch( ... )
512 saveErrors();
514 return new InvalidImp;
518 void PythonScripter::saveErrors()
520 erroroccurred = true;
521 PyObject* poexctype;
522 PyObject* poexcvalue;
523 PyObject* poexctraceback;
524 PyErr_Fetch( &poexctype, &poexcvalue, &poexctraceback );
525 handle<> exctypeh( poexctype );
526 handle<> excvalueh( poexcvalue );
528 object exctype( exctypeh );
529 object excvalue( excvalueh );
530 object exctraceback;
531 if ( poexctraceback )
533 handle<> exctracebackh( poexctraceback );
534 exctraceback = object( exctracebackh );
537 lastexceptiontype = extract<std::string>( str( exctype ) )();
538 lastexceptionvalue = extract<std::string>( str( excvalue ) )();
540 object printexcfunc = d->mainnamespace[ "traceback" ].attr( "format_exception" );
542 list tracebacklist = extract<list>( printexcfunc( exctype, excvalue, exctraceback ) )();
543 str tracebackstr( "" );
544 while ( true )
546 try {
547 str s = extract<str>( tracebacklist.pop() );
548 tracebackstr += s;
550 catch( ... )
552 break;
556 lastexceptiontraceback = extract<std::string>( tracebackstr )();
557 PyErr_Clear();
560 void PythonScripter::clearErrors()
562 PyErr_Clear();
563 lastexceptiontype.clear();
564 lastexceptionvalue.clear();
565 lastexceptiontraceback.clear();
566 erroroccurred = false;
569 bool CompiledPythonScript::valid()
571 return !!d->calcfunc;
574 bool PythonScripter::errorOccurred() const
576 return erroroccurred;