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
18 #include "python_scripter.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
)
55 // .def( self = self )
58 .def( self
*= other
<double>() )
59 .def( self
*= other
<int>() )
60 .def( self
/= other
<double>() )
61 .def( self
/ other
<double>() )
64 .def( self
* other
<double>() )
65 .def( other
<double>() * 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
)
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 )
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
;
337 class PythonScripter::Private
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
);
351 PythonScripter::PythonScripter()
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
370 s
= newstring( "import math; from math import *;" );
371 PyRun_SimpleString( s
);
373 s
= newstring( "import kig; from kig import *;" );
374 PyRun_SimpleString( s
);
376 s
= newstring( "import traceback;" );
377 PyRun_SimpleString( s
);
380 // find the main namespace..
382 s
= newstring( "__main__" );
383 handle
<> main_module( borrowed( PyImport_AddModule( s
) ) );
386 handle
<> mnh(borrowed( PyModule_GetDict(main_module
.get()) ));
387 d
->mainnamespace
= extract
<dict
>( mnh
.get() );
390 PythonScripter::~PythonScripter()
397 class CompiledPythonScript::Private
406 ObjectImp
* CompiledPythonScript::calc( const Args
& args
, const KigDocument
& )
408 return PythonScripter::instance()->calc( *this, args
);
411 CompiledPythonScript::~CompiledPythonScript()
418 CompiledPythonScript::CompiledPythonScript( Private
* ind
)
424 CompiledPythonScript
PythonScripter::compile( const char* code
)
431 (void) PyRun_String( const_cast<char*>( code
), Py_file_input
,
432 d
->mainnamespace
.ptr(), retdict
.ptr() );
438 error
|= static_cast<bool>( PyErr_Occurred() );
445 // debugging stuff, removed.
446 // std::string dictstring = extract<std::string>( str( retdict ) );
448 CompiledPythonScript::Private
* ret
= new CompiledPythonScript::Private
;
450 ret
->calcfunc
= retdict
.get( "calc" );
451 return CompiledPythonScript( ret
);
454 CompiledPythonScript::CompiledPythonScript( const CompiledPythonScript
& s
)
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
)
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
;
506 ObjectImp
& ret
= result();
514 return new InvalidImp
;
518 void PythonScripter::saveErrors()
520 erroroccurred
= true;
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
);
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( "" );
547 str s
= extract
<str
>( tracebacklist
.pop() );
556 lastexceptiontraceback
= extract
<std::string
>( tracebackstr
)();
560 void PythonScripter::clearErrors()
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
;