Commit 2c80c299 authored by Václav Šmilauer's avatar Václav Šmilauer
Browse files

1. Remake inheritance tree for Dispatchers, so that functors are really of the...

1. Remake inheritance tree for Dispatchers, so that functors are really of the right type. Involves some macro ugliness.
2. Add rudimentary (but working) inspection interface for engines, bodies, interactions
3. Fix compilation without qt3 (will be removed soon, completely)
parent 3fa96631
......@@ -15,39 +15,46 @@
#include<yade/core/Omega.hpp>
#include<yade/lib-multimethods/DynLibDispatcher.hpp>
class Dispatcher: public Engine
{
public :
typedef list<shared_ptr<Functor> >::iterator FunctorListIterator;
//! Should be called by the dispatcher before every loop (could be somehow optimized, since it will change very rarely)
void updateScenePtr() { FOREACH(shared_ptr<Functor> f, functors){ f->scene=scene; } }
void clear(){ functors.clear(); }
virtual ~Dispatcher();
virtual string getFunctorType() { throw; };
virtual int getDimension() { throw; };
virtual string getBaseClassType(unsigned int ) { throw; };
// emulate postProcessAttributes of real dispatchers that are never otherwise called; this hack will be removed once dispatchers are types on their own
virtual void Dispatcher_postProcessAttributes_deserializing(){ throw runtime_error("Dispatcher::Dispatcher_postProcessAttributes_deserializing should never be called (must be overridden in Dispatcher1D/Dispatcher2D)"); }
void postProcessAttributes(bool deserializing){
if(!deserializing) return;
Dispatcher_postProcessAttributes_deserializing();
}
#include<boost/preprocessor/cat.hpp>
YADE_CLASS_BASE_DOC_ATTRS(Dispatcher,Engine,"Engine dispatching control to its associated functors, based on types of argument it receives.",
((list<shared_ptr<Functor> >,functors,,"Instances of functors"))
);
// real base class for all dispatchers (the other one are templates)
class Dispatcher: public Engine{
public:
// these functions look to be completely unused...?
virtual string getFunctorType() { throw; };
virtual int getDimension() { throw; };
virtual string getBaseClassType(unsigned int ) { throw; };
//
virtual ~Dispatcher();
YADE_CLASS_BASE_DOC(Dispatcher,Engine,"Engine dispatching control to its associated functors, based on types of argument it receives. This abstract base class provides no functionality in itself.")
};
REGISTER_SERIALIZABLE(Dispatcher);
// HELPER MACROS
// supposed to be passed to YADE_CLASS_BASE_DOC_ATTRS_PY in the 5th argument; takes class name as arg
#define YADE_PY_DISPATCHER(DispatcherT) .def("__init__",python::make_constructor(Dispatcher_ctor_list<DispatcherT>),"Construct with list of associated functors.").add_property("functors",&Dispatcher_functors_get<DispatcherT>,&Dispatcher_functors_set<DispatcherT>,"Functors objects associated with this dispatcher.").def("dispMatrix",&DispatcherT::dump,python::arg("names")=true,"Return dictionary with contents of the dispatch matrix.").def("dispFunctor",&DispatcherT::getFunctor,"Return functor that would be dispatched for given argument(s); None if no dispatch; ambiguous dispatch throws.");
/* Each real dispatcher derives from Dispatcher1D or Dispatcher2D (both templates), which in turn derive from Dispatcher (an Engine) and DynLibDispatcher (the dispatch logic).
Because we need literal functor and class names for registration in python, we provide macro that creates the real dispatcher class with everything needed.
*/
#define _YADE_DISPATCHER1D_FUNCTOR_ADD(FunctorT,f) virtual void addFunctor(shared_ptr<FunctorT> f){ add1DEntry(f->get1DFunctorType1(),f); }
#define _YADE_DISPATCHER2D_FUNCTOR_ADD(FunctorT,f) virtual void addFunctor(shared_ptr<FunctorT> f){ add2DEntry(f->get2DFunctorType1(),f->get2DFunctorType2(),f); }
#define _YADE_DIM_DISPATCHER_FUNCTOR_DOC_ATTRS_CTOR_PY(Dim,DispatcherT,FunctorT,doc,attrs,ctor,py) \
typedef FunctorT FunctorType; \
void updateScenePtr(){ FOREACH(shared_ptr<FunctorT> f, functors){ f->scene=scene; }} \
virtual void postProcessAttributes(bool deserializing){ if(deserializing) { clearMatrix(); FOREACH(shared_ptr<FunctorT> f, functors) add(static_pointer_cast<FunctorT>(f)); } } \
virtual void add(FunctorT* f){ add(shared_ptr<FunctorT>(f)); } \
virtual void add(shared_ptr<FunctorT> f){ bool dupe=false; string fn=f->getClassName(); FOREACH(const shared_ptr<FunctorT>& f, functors) { if(fn==f->getClassName()) dupe=true; } if(!dupe) functors.push_back(f); addFunctor(f); } \
BOOST_PP_CAT(_YADE_DISPATCHER,BOOST_PP_CAT(Dim,D_FUNCTOR_ADD))(FunctorT,f) \
boost::python::list functors_get(void) const { boost::python::list ret; FOREACH(const shared_ptr<FunctorT>& f, functors){ ret.append(f); } return ret; } \
void functors_set(const vector<shared_ptr<FunctorT> >& ff){ functors.clear(); FOREACH(const shared_ptr<FunctorT>& f, ff) add(f); postProcessAttributes(true); } \
void pyHandleCustomCtorArgs(python::tuple& t, python::dict& d){ if(python::len(t)==0)return; if(python::len(t)!=1) throw invalid_argument("Exactly one list of " BOOST_PP_STRINGIZE(FunctorT) " must be given."); typedef std::vector<shared_ptr<FunctorT> > vecF; vecF vf=boost::python::extract<vecF>(t[0])(); functors_set(vf); t=python::tuple(); } \
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(DispatcherT,Dispatcher,"Dispatcher calling :yref:`functors<" BOOST_PP_STRINGIZE(FunctorT) ">` based on received argument type(s).\n\n" doc, \
((vector<shared_ptr<FunctorT> >,functors,,"Functors active in the dispatch mechanism [overridden below].")) /*additional attrs*/ attrs, \
/*ctor*/ ctor, /*py*/ py .add_property("functors",&DispatcherT::functors_get,&DispatcherT::functors_set,"Functors associated with this dispatcher." " :yattrtype:`vector<shared_ptr<" BOOST_PP_STRINGIZE(FunctorT) "> >` ") \
.def("dispMatrix",&DispatcherT::dump,python::arg("names")=true,"Return dictionary with contents of the dispatch matrix.").def("dispFunctor",&DispatcherT::getFunctor,"Return functor that would be dispatched for given argument(s); None if no dispatch; ambiguous dispatch throws."); \
)
#define YADE_DISPATCHER1D_FUNCTOR_DOC_ATTRS_CTOR_PY(DispatcherT,FunctorT,doc,attrs,ctor,py) _YADE_DIM_DISPATCHER_FUNCTOR_DOC_ATTRS_CTOR_PY(1,DispatcherT,FunctorT,doc,attrs,ctor,py)
#define YADE_DISPATCHER2D_FUNCTOR_DOC_ATTRS_CTOR_PY(DispatcherT,FunctorT,doc,attrs,ctor,py) _YADE_DIM_DISPATCHER_FUNCTOR_DOC_ATTRS_CTOR_PY(2,DispatcherT,FunctorT,doc,attrs,ctor,py)
// HELPER FUNCTIONS
......@@ -123,26 +130,24 @@ shared_ptr<DispatcherT> Dispatcher_ctor_list(const std::vector<shared_ptr<typena
template
<
class baseClass,
class FunctorType,
class FunctorReturnType,
class FunctorArguments,
bool autoSymmetry=true
>
class Dispatcher1D : public Dispatcher,
public DynLibDispatcher
< TYPELIST_1(baseClass) // base classes for dispatch
< TYPELIST_1(typename FunctorType::DispatchType1) // base classes for dispatch
, FunctorType // class that provides multivirtual call
, FunctorReturnType // return type
, FunctorArguments
, typename FunctorType::ReturnType // return type
, typename FunctorType::ArgumentTypes
, autoSymmetry
>
{
public :
typedef typename FunctorType::DispatchType1 baseClass;
typedef baseClass argType1;
typedef FunctorType functorType;
typedef DynLibDispatcher<TYPELIST_1(baseClass),FunctorType,FunctorReturnType,FunctorArguments,autoSymmetry> dispatcherBase;
typedef DynLibDispatcher<TYPELIST_1(baseClass),FunctorType,typename FunctorType::ReturnType,typename FunctorType::ArgumentTypes,autoSymmetry> dispatcherBase;
shared_ptr<FunctorType> getFunctor(shared_ptr<baseClass> arg){ return getExecutor(arg); }
python::dict dump(bool convertIndicesToNames){
......@@ -155,13 +160,6 @@ class Dispatcher1D : public Dispatcher,
}
return ret;
}
virtual void add(FunctorType* eu){ add(shared_ptr<FunctorType>(eu)); }
virtual void add(shared_ptr<FunctorType> eu){
bool dupe=false; string eun=eu->getClassName();
FOREACH(const shared_ptr<Functor>& f, functors) { if(eun==f->getClassName()) dupe=true; }
if(!dupe) functors.push_back(eu);
add1DEntry(eu->get1DFunctorType1(),eu);
}
int getDimension() { return 1; }
......@@ -176,13 +174,6 @@ class Dispatcher1D : public Dispatcher,
}
virtual void Dispatcher_postProcessAttributes_deserializing(){ Dispatcher1D::postProcessAttributes(true); }
// never gets called directly, but via the proxy above (hack)
void postProcessAttributes(bool deserializing){
if(!deserializing) return;
FOREACH(shared_ptr<Functor> f, functors) add(static_pointer_cast<FunctorType>(f));
}
public:
REGISTER_ATTRIBUTES(Dispatcher,);
REGISTER_CLASS_AND_BASE(Dispatcher1D,Dispatcher DynLibDispatcher);
......@@ -191,27 +182,24 @@ class Dispatcher1D : public Dispatcher,
template
<
class baseClass1,
class baseClass2,
class FunctorType,
class FunctorReturnType,
class FunctorArguments,
bool autoSymmetry=true
>
class Dispatcher2D : public Dispatcher,
public DynLibDispatcher
< TYPELIST_2(baseClass1,baseClass2) // base classes for dispatch
< TYPELIST_2(typename FunctorType::DispatchType1,typename FunctorType::DispatchType2) // base classes for dispatch
, FunctorType // class that provides multivirtual call
, FunctorReturnType // return type
, FunctorArguments // argument of engine unit
, typename FunctorType::ReturnType // return type
, typename FunctorType::ArgumentTypes // argument of engine unit
, autoSymmetry
>
{
public :
typedef typename FunctorType::DispatchType1 baseClass1; typedef typename FunctorType::DispatchType2 baseClass2;
typedef baseClass1 argType1;
typedef baseClass2 argType2;
typedef FunctorType functorType;
typedef DynLibDispatcher<TYPELIST_2(baseClass1,baseClass2),FunctorType,FunctorReturnType,FunctorArguments,autoSymmetry> dispatcherBase;
typedef DynLibDispatcher<TYPELIST_2(baseClass1,baseClass2),FunctorType,typename FunctorType::ReturnType,typename FunctorType::ArgumentTypes,autoSymmetry> dispatcherBase;
shared_ptr<FunctorType> getFunctor(shared_ptr<baseClass1> arg1, shared_ptr<baseClass2> arg2){ return getExecutor(arg1,arg2); }
python::dict dump(bool convertIndicesToNames){
python::dict ret;
......@@ -223,15 +211,6 @@ class Dispatcher2D : public Dispatcher,
}
return ret;
}
/* add functor by pointer: this is convenience for calls like foo->add(new SomeFunctor); */
virtual void add(FunctorType* eu){ add(shared_ptr<FunctorType>(eu)); }
/* add functor by shared pointer */
virtual void add(shared_ptr<FunctorType> eu){
bool dupe=false; string eun=eu->getClassName();
FOREACH(const shared_ptr<Functor>& f, functors) { if(eun==f->getClassName()) dupe=true; }
if(!dupe) functors.push_back(eu);
add2DEntry(eu->get2DFunctorType1(),eu->get2DFunctorType2(),eu);
}
virtual int getDimension() { return 2; }
......@@ -244,13 +223,6 @@ class Dispatcher2D : public Dispatcher,
else if (i==1){ shared_ptr<baseClass2> bc(new baseClass2); return bc->getClassName();}
else return "";
}
virtual void Dispatcher_postProcessAttributes_deserializing(){ Dispatcher2D::postProcessAttributes(true); }
// never gets called directly, but via the proxy above (hack)
void postProcessAttributes(bool deserializing){
if(!deserializing) return;
FOREACH(shared_ptr<Functor> f, functors) add(static_pointer_cast<FunctorType>(f));
}
public:
REGISTER_ATTRIBUTES(Dispatcher,);
REGISTER_CLASS_AND_BASE(Dispatcher2D,Dispatcher DynLibDispatcher);
......
......@@ -36,13 +36,15 @@ REGISTER_SERIALIZABLE(Functor);
template
<
class ReturnType,
class AttributesType
class _DispatchType1,
class _ReturnType,
class _ArgumentTypes
>
class Functor1D: public Functor,
public FunctorWrapper<ReturnType, AttributesType>
public FunctorWrapper<_ReturnType, _ArgumentTypes>
{
public:
typedef _DispatchType1 DispatchType1; typedef _ReturnType ReturnType; typedef _ArgumentTypes ArgumentTypes;
#define FUNCTOR1D(type1) public: std::string get1DFunctorType1(void){return string(#type1);}
virtual std::string get1DFunctorType1(void){throw runtime_error("Class "+this->getClassName()+" did not use FUNCTOR1D to declare its argument type?"); }
virtual vector<string> getFunctorTypes(void){vector<string> ret; ret.push_back(get1DFunctorType1()); return ret;};
......@@ -53,13 +55,16 @@ class Functor1D: public Functor,
template
<
class ReturnType,
class AttributesType
class _DispatchType1,
class _DispatchType2,
class _ReturnType,
class _ArgumentTypes
>
class Functor2D: public Functor,
public FunctorWrapper<ReturnType, AttributesType>
public FunctorWrapper<_ReturnType, _ArgumentTypes>
{
public:
typedef _DispatchType1 DispatchType1; typedef _DispatchType2 DispatchType2; typedef _ReturnType ReturnType; typedef _ArgumentTypes ArgumentTypes;
#define FUNCTOR2D(type1,type2) public: std::string get2DFunctorType1(void){return string(#type1);}; std::string get2DFunctorType2(void){return string(#type2);};
virtual std::string get2DFunctorType1(void){throw logic_error("Class "+this->getClassName()+" did not use FUNCTOR2D to declare its argument types?");}
virtual std::string get2DFunctorType2(void){throw logic_error("Class "+this->getClassName()+" did not use FUNCTOR2D to declare its argument types?");}
......
......@@ -66,9 +66,6 @@ if yade.config.debug and opts.noGdb:
if 'log4cxx' in yade.config.features and opts.verbosity:
yade.log.setLevel('',[yade.log.INFO,yade.log.DEBUG,yade.log.TRACE][min(opts.verbosity,2)])
# run servers
yade.system.runServers()
# modify sys.argv in-place so that it can be handled by userSession
sys.argv=yade.runtime.argv=args
yade.runtime.opts=opts
......@@ -80,7 +77,6 @@ def userSession(qt3=False,qt4=False):
# prepare nice namespace for users
import yade
import sys
# something to run?
if len(sys.argv)>0:
arg0=sys.argv[0]
if qt3: yade.qt.Controller();
......@@ -90,15 +86,25 @@ def userSession(qt3=False,qt4=False):
sys.stderr.write("Running simulation "+arg0+'\n')
O=yade.wrapper.Omega(); O.load(arg0); O.run()
if arg0.endswith('.py'):
sys.stderr.write("Running script "+arg0+'\n')
try:
execfile(arg0,globals())
except SystemExit: raise
except: # all other exceptions
import traceback
traceback.print_exc()
if yade.runtime.opts.exitAfter: sys.exit(1)
if yade.runtime.opts.exitAfter: sys.exit(0)
def runScript(script):
sys.stderr.write("Running script "+arg0+'\n')
try:
execfile(script,globals())
except SystemExit: raise
except: # all other exceptions
import traceback
traceback.print_exc()
if yade.runtime.opts.exitAfter: sys.exit(1)
if yade.runtime.opts.exitAfter: sys.exit(0)
#if qt4:
# class RunScriptThread(QThread):
# def __init__(self,script): QThread.__init__(self); self.script=script
# def run(self): runScript(self.script)
# worker=RunScriptThread(arg0)
# worker.start()
# print 'worker started'
#else:
runScript(arg0)
# show python console
if 1:
from IPython.Shell import IPShellEmbed
......@@ -129,12 +135,12 @@ def userSession(qt3=False,qt4=False):
ipshell()
# save history -- a workaround for atexit handlers not being run (why?)
# http://lists.ipython.scipy.org/pipermail/ipython-user/2008-September/005839.html
#import IPython.ipapi
#IPython.ipapi.get().IP.atexit_operations()
import IPython.ipapi
IPython.ipapi.get().IP.atexit_operations()
## run userSession in a way corresponding to the features we use:
gui=None
if 'opengl' in features and not 'noqt3' in features: gui='qt3'
if 'qt3' in features: gui='qt3'
if 'qt4' in features: gui='qt4'
if opts.nogui: gui=None
if gui:
......@@ -146,6 +152,12 @@ if gui:
Xlib.display._BaseDisplay(); # contrary to display.Display, _BaseDisplay does not check for extensions and that avoids spurious message "Xlib.protocol.request.QueryExtension" (bug?)
except Xlib.error.DisplayError:
gui=None
# run remote access things, before actually starting the user session
from yade import remote
yade.remote.useQThread=(gui=='qt4')
yade.remote.runServers()
if gui==None:
userSession()
elif gui=='qt3':
......
......@@ -8,6 +8,7 @@ if 'qt4' in env['features']:
env.File('qt4/ui_controller.py'),
env.File('qt4/ui_SeqSerializable.py'),
env.File('qt4/SerializableEditor.py'),
env.File('qt4/Inspector.py'),
env.File('qt4/__init__.py'),
env.SharedLibrary('_GLViewer',['qt4/_GLViewer.cpp','qt4/OpenGLManager.cpp'],SHLIBPREFIX='',LIBS=env['LIBS']+['GLViewer'],RPATH=env['RPATH']+[env.Literal('\\$$ORIGIN/../../../gui')])
])
......
......@@ -11,7 +11,7 @@
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
......@@ -45,7 +45,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
<height>10</height>
</size>
</property>
<property name="maximumSize">
......@@ -54,6 +54,12 @@
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
......@@ -81,7 +87,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
<height>10</height>
</size>
</property>
<property name="maximumSize">
......@@ -90,6 +96,12 @@
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
......@@ -114,7 +126,7 @@
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>10</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
......@@ -125,6 +137,12 @@
<height>10</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
......@@ -162,7 +180,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
<height>10</height>
</size>
</property>
<property name="maximumSize">
......@@ -180,7 +198,7 @@
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
<height>15</height>
</size>
</property>
<property name="font">
......@@ -210,7 +228,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
<height>10</height>
</size>
</property>
<property name="maximumSize">
......@@ -219,6 +237,12 @@
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
......
# encoding: utf-8
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtGui
......@@ -30,6 +31,9 @@ class AttrEditor():
self.repaint()
#print self.attr,('hot' if hot else 'cold')
def sizeHint(self): return QSize(150,12)
def setAttribute(self,ser,attr,val):
try: setattr(ser,attr,val)
except AttributeError: self.setEnabled(False) # read-only attribute
class AttrEditor_Bool(AttrEditor,QCheckBox):
def __init__(self,parent,ser,attr):
......@@ -38,7 +42,7 @@ class AttrEditor_Bool(AttrEditor,QCheckBox):
self.clicked.connect(self.update)
def refresh(self):
self.setChecked(getattr(self.ser,self.attr))
def update(self): setattr(self.ser,self.attr,self.isChecked())
def update(self): self.setAttribute(self.ser,self.attr,self.isChecked())
class AttrEditor_Int(AttrEditor,QSpinBox):
def __init__(self,parent,ser,attr):
......@@ -46,8 +50,13 @@ class AttrEditor_Int(AttrEditor,QSpinBox):
QSpinBox.__init__(self,parent)
self.setRange(int(-1e10),int(1e10)); self.setSingleStep(1);
self.valueChanged.connect(self.update)
def refresh(self): self.setValue(getattr(self.ser,self.attr))
def update(self): setattr(self.ser,self.attr,self.value()); self.isHot(False)
def refresh(self):
val=getattr(self.ser,self.attr)
#print 'INT',self.ser,self.attr,val
self.setValue(val)
def update(self):
sim,ui=getattr(self.ser,self.attr),self.value()
if sim!=ui: self.setAttribute(self.ser,self.attr,ui)
class AttrEditor_Str(AttrEditor,QLineEdit):
def __init__(self,parent,ser,attr):
......@@ -56,7 +65,7 @@ class AttrEditor_Str(AttrEditor,QLineEdit):
self.textEdited.connect(self.isHot)
self.editingFinished.connect(self.update)
def refresh(self): self.setText(getattr(self.ser,self.attr))
def update(self): setattr(self.ser,self.attr,str(self.text())); self.isHot(False)
def update(self): self.setAttribute(self.ser,self.attr,str(self.text())); self.isHot(False)
class AttrEditor_Float(AttrEditor,QLineEdit):
def __init__(self,parent,ser,attr):
......@@ -66,7 +75,7 @@ class AttrEditor_Float(AttrEditor,QLineEdit):
self.editingFinished.connect(self.update)
def refresh(self): self.setText(str(getattr(self.ser,self.attr)))
def update(self):
try: setattr(self.ser,self.attr,float(self.text()))
try: self.setAttribute(self.ser,self.attr,float(self.text()))
except ValueError: self.refresh()
self.isHot(False)
......@@ -95,7 +104,7 @@ class AttrEditor_MatrixX(AttrEditor,QFrame):
w=self.grid.itemAtPosition(row,col).widget()
if w.isModified(): val[self.idxConverter(row,col)]=float(w.text())
logging.debug('setting'+str(val))
setattr(self.ser,self.attr,val)
self.setAttribute(self.ser,self.attr,val)
except ValueError: self.refresh()
self.isHot(False)
......@@ -108,6 +117,9 @@ class AttrEditor_Matrix3(AttrEditor_MatrixX):
class AttrEditor_Quaternion(AttrEditor_MatrixX):
def __init__(self,parent,ser,attr):
AttrEditor_MatrixX.__init__(self,parent,ser,attr,1,4,lambda r,c:c)
class AttrEditor_Se3(AttrEditor_MatrixX):
def __init__(self,parent,ser,attr):
AttrEditor_MatrixX.__init__(self,parent,ser,attr,1,4,lambda r,c:c)
class AttrEditor_ListStr(AttrEditor,QPlainTextEdit):
def __init__(self,parent,ser,attr):
......@@ -122,10 +134,10 @@ class AttrEditor_ListStr(AttrEditor,QPlainTextEdit):
def update(self):
if self.hasFocus(): self.isHot()
t=self.toPlainText()
setattr(self.ser,self.attr,str(t).strip().split('\n'))
self.setAttribute(self.ser,self.attr,str(t).strip().split('\n'))
if not self.hasFocus(): self.isHot(False)
class SerializableEditor(QWidget):
class SerializableEditor(QFrame):
"Class displaying and modifying serializable attributes of a yade object."
import collections
import logging
......@@ -134,17 +146,18 @@ class SerializableEditor(QWidget):
def __init__(self,name,T):
self.name,self.T=name,T
self.lineNo,self.widget=None,None
def __init__(self,ser,parent=None,ignoredAttrs=set()):
def __init__(self,ser,parent=None,ignoredAttrs=set(),showType=False):
"Construct window, *ser* is the object we want to show."
QtGui.QWidget.__init__(self,parent)
QtGui.QFrame.__init__(self,parent)
self.ser=ser
self.showType=showType
self.hot=False
self.entries=[]
self.ignoredAttrs=ignoredAttrs
logging.debug('New Serializable of type %s'%ser.__class__.__name__)
self.setWindowTitle(str(ser))
self.mkWidgets()
self.refreshTimer=QTimer()
self.refreshTimer=QTimer(self)
self.refreshTimer.timeout.connect(self.refreshEvent)
self.refreshTimer.start(500)
def getListTypeFromDocstring(self,attr):
......@@ -169,7 +182,7 @@ class SerializableEditor(QWidget):
'Real':float,'float':float,'double':float,
'Vector3r':Vector3,'Matrix3r':Matrix3,
'string':str,
'BodyCallback':BodyCallback,'IntrCallback':IntrCallback,
'BodyCallback':BodyCallback,'IntrCallback':IntrCallback,'BoundFunctor':BoundFunctor,'InteractionGeometryFunctor':InteractionGeometryFunctor,'InteractionPhysicsFunctor':InteractionPhysicsFunctor,'LawFunctor':LawFunctor
}
for T,ret in vecMap.items():
if vecTest(T,cxxT):
......@@ -184,17 +197,21 @@ class SerializableEditor(QWidget):
logging.error('TypeError when getting attributes of '+str(self.ser)+',skipping. ')
import traceback
traceback.print_exc()
for attr,val in self.ser.dict().items():
if attr in self.ignoredAttrs:
for attr in self.ser.dict():
val=getattr(self.ser,attr) # get the value using serattr, as it might be different from what the dictionary provides (e.g. Body.blockedDOFs)
t=None
if attr in self.ignoredAttrs or attr=='blockedDOFs': # HACK here
continue
if isinstance(val,list):
if len(val)==0: t=self.getListTypeFromDocstring(attr)
else: t=(val[0].__class__,) # 1-tuple is list of the contained type
t=self.getListTypeFromDocstring(attr)
if not t and len(val)==0: t=(val[0].__class__,) # 1-tuple is list of the contained type
#if not t: raise RuntimeError('Unable to guess type of '+str(self.ser)+'.'+attr)
else: t=val.__class__
#logging.debug('Attr %s is of type %s'%(attr,((t[0].__name__,) if isinstance(t,tuple) else t.__name__)))
self.entries.append(self.EntryData(name=attr,T=t))
def mkWidget(self,entry):
typeMap={bool:AttrEditor_Bool,str:AttrEditor_Str,int:AttrEditor_Int,float:AttrEditor_Float,Vector3:AttrEditor_Vector3,Matrix3:AttrEditor_Matrix3,(str,):AttrEditor_ListStr}
if not entry.T: return None
typeMap={bool:AttrEditor_Bool,str:AttrEditor_Str,int:AttrEditor_Int,float:AttrEditor_Float,Quaternion:AttrEditor_Quaternion,Vector3:AttrEditor_Vector3,Matrix3:AttrEditor_Matrix3,(str,):AttrEditor_ListStr}
Klass=typeMap.get(entry.T,None)
if Klass:
widget=Klass(self,self.ser,entry.name)
......@@ -206,7 +223,8 @@ class SerializableEditor(QWidget):
return widget
return None
if issubclass(entry.T,Serializable) or entry.T==Serializable:
widget=SerializableEditor(getattr(self.ser,entry.name),parent=self)
widget=SerializableEditor(getattr(self.ser,entry.name),parent=self,showType=self.showType)
widget.setFrameShape(QFrame.Box); widget.setFrameShadow(QFrame.Raised); widget.setLineWidth(1)
return widget
return None
def mkWidgets(self):
......@@ -215,7 +233,11 @@ class SerializableEditor(QWidget):
grid.setContentsMargins(2,2,2,2)
grid.setVerticalSpacing(0)
grid.setLabelAlignment(Qt.AlignRight)
for lineNo,entry in enumerate(self.entries):
if self.showType:
lab=QLabel(u'<b>→ '+self.ser.__class__.__name__+u' ←</b>')
lab.setFrameShape(QFrame.Box); lab.setFrameShadow(QFrame.Sunken); lab.setLineWidth(2); lab.setAlignment(Qt.AlignHCenter)
grid.setWidget(0,QFormLayout.SpanningRole,lab)
for entry in self.entries:
entry.widget=self.mkWidget(entry)