Merge branch 'master' of ssh://git.code.sf.net/p/free-cad/code

This commit is contained in:
Yorik van Havre
2015-03-07 14:32:08 -03:00
23 changed files with 500 additions and 217 deletions

View File

@@ -138,11 +138,11 @@ void TaskFemConstraintForce::updateUI()
std::string ref = ui->listReferences->item(0)->text().toStdString();
int pos = ref.find_last_of(":");
if (ref.substr(pos+1, 6) == "Vertex")
ui->labelForce->setText(tr("Point load [N]"));
ui->labelForce->setText(tr("Point load"));
else if (ref.substr(pos+1, 4) == "Edge")
ui->labelForce->setText(tr("Line load [N/mm]"));
ui->labelForce->setText(tr("Line load"));
else if (ref.substr(pos+1, 4) == "Face")
ui->labelForce->setText(trUtf8("Area load [N/mm\xc2\xb2]"));
ui->labelForce->setText(tr("Area load"));
}
void TaskFemConstraintForce::onSelectionChanged(const Gui::SelectionChanges& msg)

View File

@@ -372,37 +372,58 @@ class _JobControlTaskPanel:
self.OutStr = self.OutStr + '<font color="#0000FF">{0:4.1f}:</font> '.format(time.time() - self.Start) + 'Write mesh...<br>'
self.form.textEdit_Output.setText(self.OutStr)
# write mesh
MeshObject.FemMesh.writeABAQUS(filename)
# reopen file with "append" and add the analysis definition
inpfile = open(filename,'a')
inpfile.write('\n\n')
self.OutStr = self.OutStr + '<font color="#0000FF">{0:4.1f}:</font> '.format(time.time() - self.Start) + 'Write loads & Co...<br>'
self.form.textEdit_Output.setText(self.OutStr)
# write the fixed node set
# write fixed node set
NodeSetName = FixedObject.Name
inpfile.write('\n\n\n\n***********************************************************\n')
inpfile.write('** node set for fixed constraint\n')
inpfile.write('*NSET,NSET=' + NodeSetName + '\n')
for o,f in FixedObject.References:
fo = o.Shape.getElement(f)
n = MeshObject.FemMesh.getNodesByFace(fo)
for i in n:
inpfile.write( str(i)+',\n')
inpfile.write('\n\n')
if fo.ShapeType == 'Face':
FixedObjectType = 'AreaSupport'
n = MeshObject.FemMesh.getNodesByFace(fo)
for i in n:
inpfile.write( str(i)+',\n')
elif fo.ShapeType == 'Edge':
FixedObjectType = 'LineSupport'
print 'Line Supports are not yet implemented to export to CalculiX'
# getNodesByEdge(fo) # not implemented yet
elif fo.ShapeType == 'Vertex':
FixedObjectType = 'PointSupport'
print 'Point Supports are not yet implemented to export to CalculiX'
# write the load node set
# write load node set
NodeSetNameForce = ForceObject.Name
inpfile.write('\n\n\n\n***********************************************************\n')
inpfile.write('** node set for load\n')
inpfile.write('*NSET,NSET=' + NodeSetNameForce + '\n')
NbrForceNods = 0
for o,f in ForceObject.References:
fo = o.Shape.getElement(f)
n = MeshObject.FemMesh.getNodesByFace(fo)
for i in n:
inpfile.write( str(i)+',\n')
NbrForceNods = NbrForceNods + 1
inpfile.write('\n\n')
if fo.ShapeType == 'Face':
ForceObjectType = 'AreaLoad'
n = MeshObject.FemMesh.getNodesByFace(fo)
for i in n:
inpfile.write( str(i)+',\n')
NbrForceNods = NbrForceNods + 1
elif fo.ShapeType == 'Edge':
ForceObjectType = 'LineLoad'
print 'Line Loads are not yet implemented to export to CalculiX'
# getNodesByEdge(fo) # not implemented yet
elif fo.ShapeType == 'Vertex':
ForceObjectType = 'PointLoad'
print 'Point Loads are not yet implemented to export to CalculiX'
# get the material properties
# get material properties
YM = FreeCAD.Units.Quantity(MathObject.Material['Mechanical_youngsmodulus'])
if YM.Unit.Type == '':
print 'Material "Mechanical_youngsmodulus" has no Unit, asuming kPa!'
@@ -412,40 +433,83 @@ class _JobControlTaskPanel:
print 'YM = ', YM
PR = float( MathObject.Material['FEM_poissonratio'] )
print 'PR= ', PR
print 'PR = ', PR
# now open again and write the setup:
# write material properties
inpfile.write('\n\n\n\n***********************************************************\n')
inpfile.write('** material\n')
inpfile.write('** unit is kPa = mN/mm2 = (kg*mm/s^2) * 1/mm^2 = kg/mm*s^2 = 10e-3 N/mm2 = 10e-3 MPa\n')
inpfile.write('*MATERIAL, Name='+matmap['General_name'] + '\n')
inpfile.write('*ELASTIC \n')
inpfile.write('{0:.3f}, '.format(YM.Value) )
inpfile.write('{0:.3f}\n'.format(PR) )
inpfile.write('*SOLID SECTION, Elset=Eall, Material='+matmap['General_name'] + '\n')
# write step beginn
inpfile.write('\n\n\n\n***********************************************************\n')
inpfile.write('** one step is needed to calculate the mechanical analysis of FreeCAD\n')
inpfile.write('** loads are applied quasi-static, means without involving the time dimension\n')
inpfile.write('*STEP\n')
inpfile.write('*STATIC\n')
inpfile.write('*BOUNDARY\n')
inpfile.write(NodeSetName + ',1,3,0.0\n')
# write constaints
inpfile.write('\n\n** constaints\n')
if FixedObjectType == 'AreaSupport':
inpfile.write('*BOUNDARY\n')
inpfile.write(NodeSetName + ',1,3,0.0\n')
elif FixedObjectType == 'LineSupport':
pass # ToDo
elif FixedObjectType == 'PointSupport':
pass # ToDo
# write loads
#inpfile.write('*DLOAD\n')
#inpfile.write('Eall,NEWTON\n')
Force = (ForceObject.Force * 1000.0) / NbrForceNods
vec = ForceObject.DirectionVector
inpfile.write('*CLOAD\n')
inpfile.write(NodeSetNameForce + ',1,' + `vec.x * Force` + '\n')
inpfile.write(NodeSetNameForce + ',2,' + `vec.y * Force` + '\n')
inpfile.write(NodeSetNameForce + ',3,' + `vec.z * Force` + '\n')
inpfile.write('\n\n** loads\n')
if ForceObjectType == 'AreaLoad':
Force = (ForceObject.Force * 1000.0) / NbrForceNods
vec = ForceObject.DirectionVector
inpfile.write('** direction: ' + str(vec) + '\n')
inpfile.write('** concentrated load [N] distributed on the area of the given faces.\n')
inpfile.write('** ' + str(ForceObject.Force) + ' N * 1000 / ' + str(NbrForceNods) + ' Nodes = ' + str(Force) + ' mN on each node\n')
inpfile.write('*CLOAD\n')
inpfile.write(NodeSetNameForce + ',1,' + `vec.x * Force` + '\n')
inpfile.write(NodeSetNameForce + ',2,' + `vec.y * Force` + '\n')
inpfile.write(NodeSetNameForce + ',3,' + `vec.z * Force` + '\n')
elif ForceObjectType == 'LineLoad':
pass # ToDo
elif ForceObjectType == 'PointLoad':
pass # ToDo
# write outputs, both are needed by FreeCAD
inpfile.write('\n\n** outputs --> frd file\n')
inpfile.write('*NODE FILE\n')
inpfile.write('U\n')
inpfile.write('*EL FILE\n')
inpfile.write('S, E\n')
inpfile.write('** outputs --> dat file\n')
inpfile.write('*NODE PRINT , NSET=Nall \n')
inpfile.write('U \n')
inpfile.write('*EL PRINT , ELSET=Eall \n')
inpfile.write('S \n')
inpfile.write('*END STEP \n')
inpfile.write('\n\n')
#do not run Calculix
# write step end
inpfile.write('*END STEP \n')
# write some informations
FcVersionInfo = FreeCAD.Version()
inpfile.write('\n\n\n\n***********************************************************\n')
inpfile.write('**\n')
inpfile.write('** CalculiX Inputfile\n')
inpfile.write('**\n')
inpfile.write('** written by: FreeCAD ' + FcVersionInfo[0] + '.' + FcVersionInfo[1] + '.' + FcVersionInfo[2] + '\n')
inpfile.write('** written on: ' + time.ctime() + '\n')
inpfile.write('** file name: ' + os.path.basename(FreeCAD.ActiveDocument.FileName) + '\n')
inpfile.write('** analysis name: ' + FemGui.getActiveAnalysis().Name + '\n')
inpfile.write('**\n')
inpfile.close()
QApplication.restoreOverrideCursor()
@@ -543,11 +607,11 @@ class _ResultControlTaskPanel:
self.form.spinBox_DisplacementFactor.setValue(value)
def sliderMaxValue(self,value):
print 'sliderMaxValue()'
#print 'sliderMaxValue()'
self.form.verticalScrollBar_Factor.setMaximum(value)
def displacementFactorValue(self,value):
print 'displacementFactorValue()'
#print 'displacementFactorValue()'
self.form.verticalScrollBar_Factor.setValue(value)
def setDisplacement(self):
@@ -564,7 +628,7 @@ class _ResultControlTaskPanel:
def update(self):
'fills the widgets'
print "Update-------------------------------"
#print "Update-------------------------------"
self.MeshObject = None
if FemGui.getActiveAnalysis():
for i in FemGui.getActiveAnalysis().Member:

View File

@@ -145,9 +145,16 @@ class _MechanicalMaterialTaskPanel:
matmap['Mechanical_youngsmodulus'] = self.form.spinBox_young_modulus.text()
matmap['FEM_poissonratio'] = str(self.form.spinBox_poisson_ratio.value())
print self.form.comboBox_MaterialsInDir.currentText()
self.obj.Material = matmap
print matmap
print 'material data:'
if matmap.has_key('General_name'):
print ' Name = ', matmap['General_name']
if matmap.has_key('Mechanical_youngsmodulus'):
print ' YM = ', matmap['Mechanical_youngsmodulus']
if matmap.has_key('FEM_poissonratio'):
print ' PR = ', matmap['FEM_poissonratio']
def transferFrom(self):
@@ -155,7 +162,7 @@ class _MechanicalMaterialTaskPanel:
matmap = self.obj.Material
if matmap.has_key('Mechanical_youngsmodulus'):
print matmap['Mechanical_youngsmodulus']
#print matmap['Mechanical_youngsmodulus']
self.form.spinBox_young_modulus.setText(matmap['Mechanical_youngsmodulus'])
if matmap.has_key('FEM_poissonratio'):
#print float(matmap['FEM_poissonratio'])
@@ -181,12 +188,20 @@ class _MechanicalMaterialTaskPanel:
return
def accept(self):
print 'accept(self)'
#print 'accept(self)'
self.transferTo()
FreeCADGui.ActiveDocument.resetEdit()
def reject(self):
print 'reject(self)'
#print 'reject(self)'
matmap = self.obj.Material
print 'material data:'
if matmap.has_key('General_name'):
print ' Name = ', matmap['General_name']
if matmap.has_key('Mechanical_youngsmodulus'):
print ' YM = ', matmap['Mechanical_youngsmodulus']
if matmap.has_key('FEM_poissonratio'):
print ' PR = ', matmap['FEM_poissonratio']
FreeCADGui.ActiveDocument.resetEdit()
def saveMat(self):

View File

@@ -31,6 +31,7 @@ void SpreadsheetExport initSpreadsheet() {
(void) Py_InitModule("Spreadsheet", Spreadsheet_methods); /* mod name, table ptr */
Base::Console().Log("Loading Spreadsheet module... done\n");
Spreadsheet::PropertySpreadsheetQuantity::init();
Spreadsheet::PropertyColumnWidths::init();
Spreadsheet::PropertyRowHeights::init();
Spreadsheet::PropertySheet::init();

View File

@@ -395,8 +395,20 @@ void Cell::setAlias(const std::string &n)
if (alias != n) {
PropertySheet::Signaller signaller(*owner);
owner->revAliasProp.erase(alias);
alias = n;
// Update owner
if (alias != "") {
owner->aliasProp[address] = n;
owner->revAliasProp[n] = address;
}
else
owner->aliasProp.erase(address);
setUsed(ALIAS_SET, !alias.empty());
}
}
@@ -590,7 +602,7 @@ void Cell::save(Base::Writer &writer) const
writer.Stream() << writer.ind() << "<Cell ";
writer.Stream() << "address=\"" << addressToString(address) << "\" ";
writer.Stream() << "address=\"" << address.toString() << "\" ";
if (isUsed(EXPRESSION_SET)) {
std::string content;
@@ -663,7 +675,7 @@ bool Cell::isUsed() const
void Cell::visit(ExpressionVisitor &v)
{
if (expression)
v.visit(expression);
expression->visit(v);
}
/**

View File

@@ -116,6 +116,8 @@ public:
void visit(ExpressionVisitor & v);
CellAddress getAddress() const { return address; }
/* Alignment */
static const int ALIGNMENT_LEFT;
static const int ALIGNMENT_HCENTER;

View File

@@ -176,7 +176,7 @@ std::string Path::getPythonAccessor() const
const Property * prop = getProperty();
if (!prop)
throw Exception("Property not found");
throw Exception(std::string("Property '") + getPropertyName() + std::string("' not found."));
const DocumentObject * docObj = freecad_dynamic_cast<DocumentObject>(prop->getContainer());
@@ -1309,6 +1309,16 @@ Document * Path::getDocument() const
return doc;
}
const DocumentObject *Path::getDocumentObject() const
{
const App::Document * doc = getDocument();
if (!doc)
return 0;
return getDocumentObject(doc, documentObjectName);
}
const Property *Path::getProperty() const
{
const App::Document * doc = getDocument();
@@ -1346,7 +1356,7 @@ const Property * VariableExpression::getProperty() const
if (prop)
return prop;
else
throw Base::Exception("Property not found.");
throw Base::Exception(std::string("Property '") + var.getPropertyName() + std::string("' not found."));
}
/**
@@ -1785,6 +1795,11 @@ Expression *RangeExpression::simplify() const
return copy();
}
void RangeExpression::setRange(const Range &r)
{
range = r;
}
}
/**

View File

@@ -158,6 +158,8 @@ public:
App::Document *getDocument() const;
const App::DocumentObject *getDocumentObject() const;
protected:
const App::DocumentObject *getDocumentObject(const App::Document *doc, const std::string &name) const;
@@ -367,6 +369,8 @@ public:
Range getRange() const { return range; }
void setRange(const Range & r);
protected:
Range range;
};

View File

@@ -155,3 +155,14 @@ PyObject *PropertyColumnWidths::getPyObject()
}
return Py::new_reference_to(PythonObject);
}
void PropertyColumnWidths::clear()
{
std::map<int, int>::const_iterator i = begin();
while (i != end()) {
dirty.insert(i->first);
++i;
}
std::map<int,int>::clear();
}

View File

@@ -35,8 +35,6 @@ class SpreadsheetExport PropertyColumnWidths : public App::Property, std::map<in
public:
PropertyColumnWidths();
PropertyColumnWidths(const PropertyColumnWidths & other);
void setValue() { }
void setValue(int col, int width);
@@ -66,11 +64,15 @@ public:
PyObject *getPyObject(void);
void clear();
static const int defaultWidth;
static const int defaultHeaderWidth;
private:
PropertyColumnWidths(const PropertyColumnWidths & other);
std::set<int> dirty;
Py::Object PythonObject;

View File

@@ -148,3 +148,14 @@ PyObject *PropertyRowHeights::getPyObject()
}
return Py::new_reference_to(PythonObject);
}
void PropertyRowHeights::clear()
{
std::map<int, int>::const_iterator i = begin();
while (i != end()) {
dirty.insert(i->first);
++i;
}
std::map<int,int>::clear();
}

View File

@@ -35,8 +35,6 @@ class SpreadsheetExport PropertyRowHeights : public App::Property, std::map<int,
public:
PropertyRowHeights();
PropertyRowHeights(const PropertyRowHeights & other);
void setValue() { }
void setValue(int row, int height);
@@ -68,8 +66,12 @@ public:
static const int defaultHeight;
void clear();
private:
PropertyRowHeights(const PropertyRowHeights & other);
std::set<int> dirty;
Py::Object PythonObject;

View File

@@ -149,6 +149,8 @@ void PropertySheet::clear()
propertyNameToCellMap.clear();
documentObjectToCellMap.clear();
docDeps.clear();
aliasProp.clear();
revAliasProp.clear();
}
Cell *PropertySheet::getValue(CellAddress key)
@@ -429,8 +431,30 @@ void PropertySheet::setDisplayUnit(CellAddress address, const std::string &unit)
void PropertySheet::setAlias(CellAddress address, const std::string &alias)
{
assert(nonNullCellAt(address) != 0);
nonNullCellAt(address)->setAlias(alias);
Cell * cell = nonNullCellAt(address);
assert(cell != 0);
/* Mark cells depending on this cell dirty; they need to be resolved when an alias changes or disappears */
const char * docName = owner->getDocument()->Label.getValue();
const char * docObjName = owner->getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
std::map<std::string, std::set< CellAddress > >::const_iterator j = propertyNameToCellMap.find(fullName);
if (j != propertyNameToCellMap.end()) {
std::set< CellAddress >::const_iterator k = j->second.begin();
while (k != j->second.end()) {
setDirty(*k);
++k;
}
}
std::string oldAlias;
if (cell->getAlias(oldAlias))
owner->aliasRemoved(address, oldAlias);
cell->setAlias(alias);
}
void PropertySheet::setComputedUnit(CellAddress address, const Base::Unit &unit)
@@ -464,6 +488,13 @@ void PropertySheet::clear(CellAddress address)
// Mark as dirty
dirty.insert(i->first);
// Remove alias if it exists
std::map<CellAddress, std::string>::iterator j = aliasProp.find(address);
if (j != aliasProp.end()) {
revAliasProp.erase(j->second);
aliasProp.erase(j);
}
// Erase from internal struct
data.erase(i);
@@ -524,13 +555,15 @@ public:
bool changed() const { return mChanged; }
void visit(Expression * node) {
VariableExpression *expr = freecad_dynamic_cast<VariableExpression>(node);
VariableExpression *varExpr = freecad_dynamic_cast<VariableExpression>(node);
RangeExpression *rangeExpr = freecad_dynamic_cast<RangeExpression>(node);
if (expr) {
if (varExpr) {
static const boost::regex e("(\\${0,1})([A-Za-z]+)(\\${0,1})([0-9]+)");
boost::cmatch cm;
if (boost::regex_match(expr->name().c_str(), cm, e)) {
if (boost::regex_match(varExpr->name().c_str(), cm, e)) {
const boost::sub_match<const char *> colstr = cm[2];
const boost::sub_match<const char *> rowstr = cm[4];
int thisRow, thisCol;
@@ -541,11 +574,26 @@ public:
if (thisRow >= mRow || thisCol >= mCol) {
thisRow += mRowCount;
thisCol += mColCount;
expr->setName(columnName(thisCol) + rowName(thisRow));
varExpr->setName(columnName(thisCol) + rowName(thisRow));
mChanged = true;
}
}
}
else if (rangeExpr) {
Range r = rangeExpr->getRange();
CellAddress from(r.from());
CellAddress to(r.to());
if (from.row() >= mRow || from.col() >= mCol) {
from = CellAddress(from.row() + mRowCount, from.col() + mColCount);
mChanged = true;
}
if (to.row() >= mRow || to.col() >= mCol) {
to = CellAddress(to.row() + mRowCount, to.col() + mColCount);
mChanged = true;
}
rangeExpr->setRange(Range(from, to));
}
}
private:
int mRow;
@@ -832,13 +880,13 @@ void PropertySheet::addDependencies(CellAddress key)
std::set<Path>::const_iterator i = expressionDeps.begin();
while (i != expressionDeps.end()) {
const Property * prop = (*i).getProperty();
DocumentObject * docObj = prop ? freecad_dynamic_cast<DocumentObject>(prop->getContainer()) : 0;
Document * doc = docObj ? docObj->getDocument() : 0;
const Property * prop = i->getProperty();
const DocumentObject * docObj = i->getDocumentObject();
Document * doc = i->getDocument();
std::string docName = doc ? doc->Label.getValue() : (*i).getDocumentName().getString();
std::string docObjName = docName + "#" + (docObj ? docObj->getNameInDocument() : (*i).getDocumentObjectName().getString());
std::string propName = docObjName + "." + (*i).getPropertyName();
std::string docName = doc ? doc->Label.getValue() : i->getDocumentName().getString();
std::string docObjName = docName + "#" + (docObj ? docObj->getNameInDocument() : i->getDocumentObjectName().getString());
std::string propName = docObjName + "." + i->getPropertyName();
if (!prop)
cell->setResolveException("Unresolved dependency");
@@ -857,6 +905,19 @@ void PropertySheet::addDependencies(CellAddress key)
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
// Also an alias?
if (docObj == owner) {
std::map<std::string, CellAddress>::const_iterator j = revAliasProp.find(i->getPropertyName());
if (j != revAliasProp.end()) {
propName = docObjName + "." + j->second.toString();
// Insert into maps
propertyNameToCellMap[propName].insert(key);
cellToPropertyNameMap[key].insert(propName);
}
}
documentObjectToCellMap[docObjName].insert(key);
cellToDocumentObjectMap[key].insert(docObjName);
@@ -874,50 +935,47 @@ void PropertySheet::addDependencies(CellAddress key)
void PropertySheet::removeDependencies(CellAddress key)
{
std::map<CellAddress, std::set< std::string > >::iterator i1 = cellToPropertyNameMap.find(key);
std::set< std::string >::iterator j;
/* Remove from Property <-> Key maps */
if (i1 == cellToPropertyNameMap.end())
return;
std::map<CellAddress, std::set< std::string > >::iterator i1 = cellToPropertyNameMap.find(key);
j = i1->second.begin();
if (i1 != cellToPropertyNameMap.end()) {
std::set< std::string >::const_iterator j = i1->second.begin();
while (j != i1->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = propertyNameToCellMap.find(*j);
while (j != i1->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = propertyNameToCellMap.find(*j);
assert(k != propertyNameToCellMap.end());
assert(k != propertyNameToCellMap.end());
k->second.erase(key);
++j;
k->second.erase(key);
++j;
}
cellToPropertyNameMap.erase(i1);
}
cellToPropertyNameMap.erase(i1);
/* Remove from DocumentObject <-> Key maps */
std::map<CellAddress, std::set< std::string > >::iterator i2 = cellToDocumentObjectMap.find(key);
if (i2 == cellToDocumentObjectMap.end())
return;
if (i2 != cellToDocumentObjectMap.end()) {
std::set< std::string >::const_iterator j = i2->second.begin();
j = i2->second.begin();
while (j != i2->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = documentObjectToCellMap.find(*j);
while (j != i2->second.end()) {
std::map<std::string, std::set< CellAddress > >::iterator k = documentObjectToCellMap.find(*j);
assert(k != documentObjectToCellMap.end());
assert(k != documentObjectToCellMap.end());
k->second.erase(key);
k->second.erase(key);
if (k->second.size() == 0)
documentObjectToCellMap.erase(*j);
if (k->second.size() == 0)
documentObjectToCellMap.erase(*j);
++j;
}
++j;
cellToDocumentObjectMap.erase(i2);
}
cellToDocumentObjectMap.erase(i2);
}
/**
@@ -1028,6 +1086,11 @@ void PropertySheet::renamedDocument(const Document * doc)
}
}
void PropertySheet::documentSet()
{
documentName[owner->getDocument()] = owner->getDocument()->Label.getValue();
}
void PropertySheet::recomputeDependants(const DocumentObject *docObj)
{
const char * docName = docObj->getDocument()->Label.getValue();

View File

@@ -43,8 +43,6 @@ public:
PropertySheet(Sheet * _owner = 0);
PropertySheet(const PropertySheet & other);
~PropertySheet();
virtual Property *Copy(void) const;
@@ -145,8 +143,12 @@ public:
void renamedDocument(const App::Document *doc);
void documentSet();
private:
PropertySheet(const PropertySheet & other);
friend class Signaller;
friend class SheetObserver;
@@ -211,6 +213,12 @@ private:
/* Name of documents, used for renaming */
std::map<const App::Document*, std::string> documentName;
/* Mapping of cell position to alias property */
std::map<CellAddress, std::string> aliasProp;
/* Mapping of alias property to cell position */
std::map<std::string, CellAddress> revAliasProp;
int signalCounter;
Py::Object PythonObject;

View File

@@ -66,6 +66,16 @@ Range::Range(int _row_begin, int _col_begin, int _row_end, int _col_end)
{
}
Range::Range(const CellAddress &from, const CellAddress &to)
: row_curr(from.row())
, col_curr(from.col())
, row_begin(from.row())
, col_begin(from.col())
, row_end(to.row())
, col_end(to.col())
{
}
bool Range::next()
{
if (row_curr < row_end) {

View File

@@ -45,6 +45,8 @@ public:
Range(int _row_begin, int _col_begin, int _row_end, int _col_end);
Range(const CellAddress & from, const CellAddress & to);
bool next();
/** Current row */
@@ -60,17 +62,17 @@ public:
inline CellAddress to() const { return CellAddress(row_end, col_end); }
/** Start of range as a string */
inline std::string fromCellString() const { return addressToString(CellAddress(row_begin, col_begin)); }
inline std::string fromCellString() const { return CellAddress(row_begin, col_begin).toString(); }
/** End of range as a string */
inline std::string toCellString() const { return addressToString(CellAddress(row_end, col_end)); }
inline std::string toCellString() const { return CellAddress(row_end, col_end).toString(); }
/** Current cell as a string */
inline std::string address() const { return addressToString(CellAddress(row_curr, col_curr)); }
inline std::string address() const { return CellAddress(row_curr, col_curr).toString(); }
/** The raneg as a string */
inline std::string rangeString() const {
return addressToString(CellAddress(row_begin, col_begin)) + ":" + addressToString(CellAddress(row_end, col_end));
return CellAddress(row_begin, col_begin).toString() + ":" + CellAddress(row_end, col_end).toString();
}
CellAddress operator*() const { return CellAddress(row_curr, col_curr); }

View File

@@ -110,9 +110,15 @@ void Sheet::clearAll()
props.removeDynamicProperty((*i).c_str());
propAddress.clear();
cellErrors.clear();
columnWidths.clear();
rowHeights.clear();
removedAliases.clear();
docDeps.setValues(std::vector<DocumentObject*>());
for (ObserverMap::iterator i = observers.begin(); i != observers.end(); ++i)
delete i->second;
observers.clear();
}
/**
@@ -315,19 +321,6 @@ Cell *Sheet::getNewCell(CellAddress address)
return cell;
}
/**
* Convert given \a key address to string.
*
* @param key Address of cell.
*
* @returns Address given as a string.
*/
std::string Sheet::toAddress(CellAddress key)
{
return Spreadsheet::addressToString(key);
}
/**
* Set cell given by \a address to \a contents.
*
@@ -400,7 +393,7 @@ PyObject *Sheet::getPyObject(void)
Property * Sheet::getProperty(CellAddress key) const
{
return props.getDynamicPropertyByName(toAddress(key).c_str());
return props.getDynamicPropertyByName(key.toString().c_str());
}
Property * Sheet::getProperty(const char * addr) const
@@ -442,6 +435,22 @@ void Sheet::setPosition(CellAddress address)
currColumn.purgeTouched();
}
void Sheet::removeAliases()
{
std::map<CellAddress, std::string>::iterator i = removedAliases.begin();
while (i != removedAliases.end()) {
props.removeDynamicProperty(i->second.c_str());
++i;
}
removedAliases.clear();
}
void Sheet::onSettingDocument()
{
cells.documentSet();
}
/**
* Set the property for cell \key to a PropertyFloat with the value \a value.
* If the Property exists, but of wrong type, the previous Property is destroyed and recreated as the correct type.
@@ -453,21 +462,15 @@ void Sheet::setPosition(CellAddress address)
Property * Sheet::setFloatProperty(CellAddress key, double value)
{
Property * prop = props.getPropertyByName(toAddress(key).c_str());
Property * prop = props.getPropertyByName(key.toString().c_str());
PropertyFloat * floatProp;
if (!prop || prop->getTypeId() != PropertyFloat::getClassTypeId()) {
if (prop) {
props.removeDynamicProperty(toAddress(key).c_str());
props.removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
floatProp = freecad_dynamic_cast<PropertyFloat>(props.addDynamicProperty("App::PropertyFloat", toAddress(key).c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
floatProp = freecad_dynamic_cast<PropertyFloat>(props.addDynamicProperty("App::PropertyFloat", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
floatProp->StatusBits.set(3);
}
else
@@ -491,26 +494,20 @@ Property * Sheet::setFloatProperty(CellAddress key, double value)
Property * Sheet::setQuantityProperty(CellAddress key, double value, const Base::Unit & unit)
{
Property * prop = props.getPropertyByName(toAddress(key).c_str());
PropertyQuantity * quantityProp;
Property * prop = props.getPropertyByName(key.toString().c_str());
PropertySpreadsheetQuantity * quantityProp;
if (!prop || prop->getTypeId() != PropertyQuantity::getClassTypeId()) {
if (!prop || prop->getTypeId() != PropertySpreadsheetQuantity::getClassTypeId()) {
if (prop) {
props.removeDynamicProperty(toAddress(key).c_str());
props.removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
Property * p = props.addDynamicProperty("App::PropertyQuantity", toAddress(key).c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
quantityProp = freecad_dynamic_cast<PropertyQuantity>(p);
Property * p = props.addDynamicProperty("Spreadsheet::PropertySpreadsheetQuantity", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
quantityProp = freecad_dynamic_cast<PropertySpreadsheetQuantity>(p);
quantityProp->StatusBits.set(3);
}
else
quantityProp = static_cast<PropertyQuantity*>(prop);
quantityProp = static_cast<PropertySpreadsheetQuantity*>(prop);
propAddress[quantityProp] = key;
quantityProp->setValue(value);
@@ -532,21 +529,15 @@ Property * Sheet::setQuantityProperty(CellAddress key, double value, const Base:
Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
{
Property * prop = props.getPropertyByName(toAddress(key).c_str());
Property * prop = props.getPropertyByName(key.toString().c_str());
PropertyString * stringProp = freecad_dynamic_cast<PropertyString>(prop);
if (!stringProp) {
if (prop) {
props.removeDynamicProperty(toAddress(key).c_str());
props.removeDynamicProperty(key.toString().c_str());
propAddress.erase(prop);
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
stringProp = freecad_dynamic_cast<PropertyString>(props.addDynamicProperty("App::PropertyString", toAddress(key).c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
stringProp = freecad_dynamic_cast<PropertyString>(props.addDynamicProperty("App::PropertyString", key.toString().c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true));
stringProp->StatusBits.set(3);
}
@@ -556,6 +547,35 @@ Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
return stringProp;
}
void Sheet::updateAlias(CellAddress key)
{
std::string alias;
Property * prop = props.getDynamicPropertyByName(key.toString().c_str());
if (!prop)
return;
Cell * cell = getCell(key);
if (cell && cell->getAlias(alias)) {
App::Property * aliasProp = props.getDynamicPropertyByName(alias.c_str());
/* Update or create alias? */
if (aliasProp) {
// Type of alias and property must always be the same
if (aliasProp->getTypeId() != prop->getTypeId()) {
props.removeDynamicProperty(alias.c_str());
aliasProp = 0;
}
}
if (!aliasProp)
aliasProp = props.addDynamicProperty(prop->getTypeId().getName(), alias.c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
aliasProp->Paste(*prop);
}
}
/**
* Update the Propery given by \a key. This will also eventually trigger recomputations of cells depending on \a key.
*
@@ -566,10 +586,8 @@ Property * Sheet::setStringProperty(CellAddress key, const std::string & value)
void Sheet::updateProperty(CellAddress key)
{
const Property * prop;
const char * aliasType = 0;
Cell * cell = getCell(key);
std::string alias;
if (cell != 0) {
Expression * output;
@@ -590,40 +608,19 @@ void Sheet::updateProperty(CellAddress key)
/* Eval returns either NumberExpression or StringExpression objects */
if (freecad_dynamic_cast<NumberExpression>(output)) {
NumberExpression * number = static_cast<NumberExpression*>(output);
if (number->getUnit().isEmpty()) {
if (number->getUnit().isEmpty())
prop = setFloatProperty(key, number->getValue());
aliasType = "App::PropertyFloat";
}
else {
else
prop = setQuantityProperty(key, number->getValue(), number->getUnit());
aliasType = "App::PropertyQuantity";
}
}
else {
else
prop = setStringProperty(key, freecad_dynamic_cast<StringExpression>(output)->getText().c_str());
aliasType = "App::PropertyString";
}
delete output;
}
else
clear(key);
if (cell && cell->getAlias(alias)) {
/* Update or create alias? */
if (aliasProp.find(key) == aliasProp.end())
aliasProp[key] = props.addDynamicProperty(aliasType, alias.c_str(), 0, 0, Prop_ReadOnly | Prop_Transient, true, true);
aliasProp[key]->Paste(*prop);
}
else {
/* Remove alias? */
std::map<CellAddress, App::Property*>::iterator i = aliasProp.find(key);
if (i != aliasProp.end()) {
props.removeDynamicProperty(props.getPropertyName(i->second));
aliasProp.erase(i);
}
}
cellUpdated(key);
}
@@ -661,7 +658,7 @@ void Sheet::recomputeCell(CellAddress p)
Cell * cell = cells.getValue(p);
std::string docName = getDocument()->Label.getValue();
std::string docObjName = std::string(getNameInDocument());
std::string name = docName + "#" + docObjName + "." + toAddress(p);
std::string name = docName + "#" + docObjName + "." + p.toString();
try {
if (cell) {
@@ -683,6 +680,8 @@ void Sheet::recomputeCell(CellAddress p)
cellErrors.insert(p);
}
updateAlias(p);
if (!cell || cell->spansChanged())
cellSpanChanged(p);
}
@@ -694,6 +693,9 @@ void Sheet::recomputeCell(CellAddress p)
App::DocumentObjectExecReturn *Sheet::execute(void)
{
// Remove all aliases first
removeAliases();
// Get dirty cells that we have to recompute
std::set<CellAddress> dirtyCells = cells.getDirty();
@@ -769,6 +771,8 @@ App::DocumentObjectExecReturn *Sheet::execute(void)
if (cell)
cell->setException("Circular dependency.");
updateProperty(i->first);
updateAlias(i->first);
++i;
}
}
@@ -797,6 +801,10 @@ App::DocumentObjectExecReturn *Sheet::execute(void)
currColumn.purgeTouched();
std::set<App::DocumentObject*> ds(cells.getDocDeps());
// Make sure we don't reference ourselves
ds.erase(this);
std::vector<App::DocumentObject*> dv(ds.begin(), ds.end());
docDeps.setValues(dv);
@@ -837,9 +845,15 @@ short Sheet::mustExecute(void) const
void Sheet::clear(CellAddress address, bool all)
{
std::string addr = toAddress(address);
Cell * cell = getCell(address);
std::string addr = address.toString();
Property * prop = props.getDynamicPropertyByName(addr.c_str());
// Remove alias, if defined
std::string aliasStr;
if (cell && cell->getAlias(aliasStr))
props.removeDynamicProperty(aliasStr.c_str());
cells.clear(address);
propAddress.erase(prop);
@@ -911,7 +925,7 @@ std::vector<std::string> Sheet::getUsedCells() const
std::set<CellAddress> usedSet = cells.getUsedCells();
for (std::set<CellAddress>::const_iterator i = usedSet.begin(); i != usedSet.end(); ++i)
usedCells.push_back(toAddress(*i));
usedCells.push_back(i->toString());
return usedCells;
}
@@ -1031,6 +1045,11 @@ void Sheet::renamedDocumentObject(const App::DocumentObject * docObj)
cells.touch();
}
void Sheet::aliasRemoved(CellAddress address, const std::string & alias)
{
removedAliases[address] = alias;
}
std::set<std::string> Sheet::dependsOn(CellAddress address) const
{
return cells.getDeps(address);
@@ -1040,18 +1059,18 @@ void Sheet::providesTo(CellAddress address, std::set<std::string> & result) cons
{
const char * docName = getDocument()->Label.getValue();
const char * docObjName = getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + toAddress(address);
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
std::set<CellAddress> tmpResult = cells.getDeps(fullName);
for (std::set<CellAddress>::const_iterator i = tmpResult.begin(); i != tmpResult.end(); ++i)
result.insert(std::string(docName) + "#" + std::string(docObjName) + "." + toAddress(*i));
result.insert(std::string(docName) + "#" + std::string(docObjName) + "." + i->toString());
}
void Sheet::providesTo(CellAddress address, std::set<CellAddress> & result) const
{
const char * docName = getDocument()->Label.getValue();
const char * docObjName = getNameInDocument();
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + toAddress(address);
std::string fullName = std::string(docName) + "#" + std::string(docObjName) + "." + address.toString();
result = cells.getDeps(fullName);
}
@@ -1086,3 +1105,23 @@ void Sheet::observeDocument(Document * document)
observers[document->getName()] = observer;
}
}
TYPESYSTEM_SOURCE(Spreadsheet::PropertySpreadsheetQuantity, App::PropertyQuantity);
Property *PropertySpreadsheetQuantity::Copy() const
{
PropertySpreadsheetQuantity * obj = new PropertySpreadsheetQuantity();
obj->_dValue = _dValue;
obj->_Unit = _Unit;
return obj;
}
void PropertySpreadsheetQuantity::Paste(const Property &from)
{
aboutToSetValue();
_dValue = static_cast<const PropertySpreadsheetQuantity*>(&from)->_dValue;
_Unit = static_cast<const PropertySpreadsheetQuantity*>(&from)->_Unit;
hasSetValue();
}

View File

@@ -52,10 +52,25 @@ class Expression;
class Range;
class SheetObserver;
/** Spreadsheet quantity property
* This is a property for quantities, and unlike its ancestor implements
* Copy() and Paste() methods. It is used by the spreadsheet to
* create aliases in a generic way.
*/
class SpreadsheetExport PropertySpreadsheetQuantity : public App::PropertyQuantity
{
TYPESYSTEM_HEADER();
public:
PropertySpreadsheetQuantity(void){}
virtual ~PropertySpreadsheetQuantity(){}
virtual Property *Copy(void) const;
virtual void Paste(const Property &from);
};
class SpreadsheetExport Sheet : public App::DocumentObject
{
PROPERTY_HEADER(Sheet::Sheet);
PROPERTY_HEADER(Spreadsheet::Sheet);
public:
@@ -220,6 +235,8 @@ protected:
App::Property *getProperty(const char * addr) const;
void updateAlias(CellAddress key);
void updateProperty(CellAddress key);
App::Property *setStringProperty(CellAddress key, const std::string & value) ;
@@ -228,20 +245,24 @@ protected:
App::Property *setQuantityProperty(CellAddress key, double value, const Base::Unit &unit);
static std::string toAddress(CellAddress key);
void moveCell(CellAddress currPos, CellAddress newPos);
void renamedDocumentObject(const App::DocumentObject * docObj);
void aliasRemoved(CellAddress address, const std::string &alias);
void removeAliases();
virtual void onSettingDocument();
/* Properties for used cells */
App::DynamicProperty props;
/* Mapping of properties to cell position */
std::map<const App::Property*, CellAddress > propAddress;
/* Mapping of cell position to alias property */
std::map<CellAddress, App::Property*> aliasProp;
/* Removed (unprocessed) aliases */
std::map<CellAddress, std::string> removedAliases;
/* Set of cells with errors */
std::set<CellAddress> cellErrors;

View File

@@ -161,33 +161,6 @@ Spreadsheet::CellAddress Spreadsheet::stringToAddress(const char * strAddress)
throw Base::Exception("Invalid cell specifier.");
}
/**
* Convert given \a row and \a col into a string address.
*
* @param row Row address.
* @param col Column address.
*
* @returns Address given as a string.
*/
std::string Spreadsheet::addressToString(const CellAddress &address)
{
std::stringstream s;
if (address.col() < 26)
s << (char)('A' + address.col());
else {
int col = address.col() - 26;
s << (char)('A' + (col / 26));
s << (char)('A' + (col % 26));
}
s << (address.row() + 1);
return s.str();
}
void Spreadsheet::createRectangles(std::set<std::pair<int, int> > & cells, std::map<std::pair<int, int>, std::pair<int, int> > & rectangles)
{
while (cells.size() != 0) {
@@ -342,3 +315,28 @@ std::string Spreadsheet::unquote(const std::string & input)
return output;
}
/**
* Convert given \a cell address into its string representation.
*
* @returns Address given as a string.
*/
std::string Spreadsheet::CellAddress::toString() const
{
std::stringstream s;
if (col() < 26)
s << (char)('A' + col());
else {
int colnum = col() - 26;
s << (char)('A' + (colnum / 26));
s << (char)('A' + (colnum % 26));
}
s << (row() + 1);
return s.str();
}

View File

@@ -42,12 +42,9 @@ SpreadsheetExport void createRectangles(std::set<std::pair<int, int> > & cells,
SpreadsheetExport std::string quote(const std::string &input);
SpreadsheetExport std::string unquote(const std::string & input);
SpreadsheetExport std::string addressToString(const CellAddress & address);
struct SpreadsheetExport CellAddress {
struct CellAddress {
CellAddress(unsigned int _value) : value(_value) { }
CellAddress(int row = -1, int col = -1) : value(((row & 0xffff) << 16) | (col & 0xffff)) { }
CellAddress(int row = -1, int col = -1) : _row(row), _col(col) { }
CellAddress(const char * address) {
*this = stringToAddress(address);
@@ -57,18 +54,20 @@ struct CellAddress {
*this = stringToAddress(address.c_str());
}
inline int row() const { return (value >> 16) & 0xffff; }
inline int row() const { return _row; }
inline int col() const { return value & 0xffff; }
inline int col() const { return _col; }
inline bool operator<(const CellAddress & other) const { return value < other.value; }
inline bool operator<(const CellAddress & other) const { return asInt() < other.asInt(); }
inline bool operator==(const CellAddress & other) const { return value == other.value; }
inline bool operator==(const CellAddress & other) const { return asInt() == other.asInt(); }
inline bool operator!=(const CellAddress & other) const { return value != other.value; }
inline bool operator!=(const CellAddress & other) const { return asInt() != other.asInt(); }
inline bool isValid() { return (row() >=0 && row() < MAX_ROWS && col() >= 0 && col() < MAX_COLUMNS); }
std::string toString() const;
// Static members
static const int MAX_ROWS;
@@ -76,7 +75,11 @@ struct CellAddress {
static const int MAX_COLUMNS;
protected:
unsigned int value;
inline unsigned int asInt() const { return ((_row << 16) | _col); }
short _row;
short _col;
};
template<typename T> T * freecad_dynamic_cast(Base::BaseClass * t)

View File

@@ -125,7 +125,7 @@ void CmdSpreadsheetSplitCell::activated(int iMsg)
QModelIndex current = sheetView->currentIndex();
if (current.isValid()) {
std::string address = addressToString(CellAddress(current.row(), current.column()));
std::string address = CellAddress(current.row(), current.column()).toString();
Gui::Command::openCommand("Split cell");
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.splitCell('%s')", sheet->getNameInDocument(),
address.c_str());

View File

@@ -147,10 +147,10 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const
if (role == Qt::ToolTipRole) {
QString v;
std::set<std::string> deps = sheet->dependsOn(row, col);
std::set<std::string> deps = sheet->dependsOn(CellAddress(row, col));
std::set<std::string> provides;
sheet->providesTo(row, col, provides);
sheet->providesTo(CellAddress(row, col), provides);
if (deps.size() > 0) {
v += QString::fromUtf8("Depends on:");
@@ -196,7 +196,7 @@ QVariant SheetModel::data(const QModelIndex &index, int role) const
}
// Get display value as computed property
std::string address = addressToString(CellAddress(row, col));
std::string address = CellAddress(row, col).toString();
Property * prop = sheet->getPropertyByName(address.c_str());
if (prop == 0)
@@ -394,8 +394,8 @@ bool SheetModel::setData(const QModelIndex & index, const QVariant & value, int
CellAddress address(index.row(), index.column());
try {
std::string strAddress = addressToString(address);
std::string next_address = addressToString(CellAddress(address.row() + 1, address.col()));
std::string strAddress = address.toString();
std::string next_address = CellAddress(address.row() + 1, address.col()).toString();
QString str = value.toString();
std::string content;
Cell * cell = sheet->getCell(address);

View File

@@ -158,7 +158,7 @@ bool ViewProviderSheet::onDelete(const std::vector<std::string> &)
if (selection.size() > 0) {
Gui::Command::openCommand("Clear cell(s)");
for (QModelIndexList::const_iterator it = selection.begin(); it != selection.end(); ++it) {
std::string address = Spreadsheet::addressToString(CellAddress((*it).row(), (*it).column()));
std::string address = CellAddress((*it).row(), (*it).column()).toString();
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.clear('%s')", sheet->getNameInDocument(),
address.c_str());
}