diff --git a/src/3rdParty/salomesmesh/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cpp b/src/3rdParty/salomesmesh/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cpp index bd01c08bf..b22b28905 100644 --- a/src/3rdParty/salomesmesh/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cpp +++ b/src/3rdParty/salomesmesh/src/DriverSTL/DriverSTL_R_SMDS_Mesh.cpp @@ -160,7 +160,7 @@ Driver_Mesh::Status DriverSTL_R_SMDS_Mesh::Perform() static Standard_Real readFloat(OSD_File& theFile) { union { - Standard_Boolean i; + Standard_Integer i; Standard_ShortReal f; }u; diff --git a/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_Mesher.cpp b/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_Mesher.cpp index fdbd6d5d3..b647d95fd 100644 --- a/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_Mesher.cpp +++ b/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_Mesher.cpp @@ -54,6 +54,10 @@ #include // Netgen include files +#ifdef _MSC_VER +#pragma warning(disable : 4067) +#endif + namespace nglib { #include } diff --git a/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cpp b/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cpp index 98c486cca..fbf10f0b6 100644 --- a/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cpp +++ b/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_2D_ONLY.cpp @@ -50,6 +50,10 @@ /* Netgen include files */ +#ifdef _MSC_VER +#pragma warning(disable : 4067) +#endif + namespace nglib { #include } diff --git a/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_3D.cpp b/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_3D.cpp index de80ede30..3e5ad7408 100644 --- a/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_3D.cpp +++ b/src/3rdParty/salomesmesh/src/NETGENPlugin/NETGENPlugin_NETGEN_3D.cpp @@ -59,6 +59,9 @@ /* Netgen include files */ +#ifdef _MSC_VER +#pragma warning(disable : 4067) +#endif namespace nglib { #include diff --git a/src/App/Expression.cpp b/src/App/Expression.cpp index 104a2de04..d6291040a 100644 --- a/src/App/Expression.cpp +++ b/src/App/Expression.cpp @@ -63,6 +63,8 @@ #if defined(_MSC_VER) #define strtoll _strtoi64 +#pragma warning(disable : 4003) +#pragma warning(disable : 4065) #endif using namespace Base; diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 0059f55a5..772d41d45 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -706,8 +706,23 @@ void Application::slotActiveDocument(const App::Document& Doc) { std::map::iterator doc = d->documents.find(&Doc); // this can happen when closing a document with two views opened - if (doc != d->documents.end()) + if (doc != d->documents.end()) { + // this can happen when calling App.setActiveDocument directly from Python + // because no MDI view will be activated + if (d->activeDocument != doc->second) { + d->activeDocument = doc->second; + if (d->activeDocument) { + Base::PyGILStateLocker lock; + Py::Object active(d->activeDocument->getPyObject(), true); + Py::Module("FreeCADGui").setAttr(std::string("ActiveDocument"),active); + } + else { + Base::PyGILStateLocker lock; + Py::Module("FreeCADGui").setAttr(std::string("ActiveDocument"),Py::None()); + } + } signalActiveDocument(*doc->second); + } } void Application::slotNewObject(const ViewProvider& vp) diff --git a/src/Gui/Application.h b/src/Gui/Application.h index 29748a4dc..50520bfb9 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -231,6 +231,7 @@ public: PYFUNCDEF_S(sExport); PYFUNCDEF_S(sActiveDocument); + PYFUNCDEF_S(sSetActiveDocument); PYFUNCDEF_S(sGetDocument); PYFUNCDEF_S(sDoCommand); diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index 0672a6277..d311b216d 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -54,6 +54,7 @@ #include "Language/Translator.h" #include "DownloadManager.h" #include +#include #include #include #include @@ -139,6 +140,9 @@ PyMethodDef Application::Methods[] = { {"activeDocument", (PyCFunction) Application::sActiveDocument, 1, "activeDocument() -> object or None\n\n" "Return the active document or None if no one exists"}, + {"setActiveDocument", (PyCFunction) Application::sSetActiveDocument,1, + "setActiveDocument(string or App.Document) -> None\n\n" + "Activate the specified document"}, {"getDocument", (PyCFunction) Application::sGetDocument, 1, "getDocument(string) -> object\n\n" "Get a document by its name"}, @@ -171,19 +175,71 @@ PyObject* Gui::Application::sActiveDocument(PyObject * /*self*/, PyObject *args, } } -PyObject* Application::sGetDocument(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) +PyObject* Gui::Application::sSetActiveDocument(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) { - char *pstr=0; - if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C - return NULL; // NULL triggers exception + Document *pcDoc = 0; + + do { + char *pstr=0; + if (PyArg_ParseTuple(args, "s", &pstr)) { + pcDoc = Instance->getDocument(pstr); + if (!pcDoc) { + PyErr_Format(PyExc_NameError, "Unknown document '%s'", pstr); + return 0; + } + break; + } + + PyErr_Clear(); + PyObject* doc; + if (PyArg_ParseTuple(args, "O!", &(App::DocumentPy::Type), &doc)) { + pcDoc = Instance->getDocument(static_cast(doc)->getDocumentPtr()); + if (!pcDoc) { + PyErr_Format(PyExc_KeyError, "Unknown document instance"); + return 0; + } + break; + } + } + while(false); - Document *pcDoc = Instance->getDocument(pstr); if (!pcDoc) { - PyErr_Format(PyExc_NameError, "Unknown document '%s'", pstr); + PyErr_SetString(PyExc_TypeError, "Either string or App.Document expected"); return 0; } - return pcDoc->getPyObject(); + if (Instance->activeDocument() != pcDoc) { + Gui::MDIView* view = pcDoc->getActiveView(); + getMainWindow()->setActiveWindow(view); + } + Py_Return; +} + +PyObject* Application::sGetDocument(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) +{ + char *pstr=0; + if (PyArg_ParseTuple(args, "s", &pstr)) { + Document *pcDoc = Instance->getDocument(pstr); + if (!pcDoc) { + PyErr_Format(PyExc_NameError, "Unknown document '%s'", pstr); + return 0; + } + return pcDoc->getPyObject(); + } + + PyErr_Clear(); + PyObject* doc; + if (PyArg_ParseTuple(args, "O!", &(App::DocumentPy::Type), &doc)) { + Document *pcDoc = Instance->getDocument(static_cast(doc)->getDocumentPtr()); + if (!pcDoc) { + PyErr_Format(PyExc_KeyError, "Unknown document instance"); + return 0; + } + return pcDoc->getPyObject(); + } + + PyErr_SetString(PyExc_TypeError, "Either string or App.Document exprected"); + return 0; } PyObject* Application::sHide(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) diff --git a/src/Gui/DlgExpressionInput.cpp b/src/Gui/DlgExpressionInput.cpp index 2d8b5717f..dc70038c1 100644 --- a/src/Gui/DlgExpressionInput.cpp +++ b/src/Gui/DlgExpressionInput.cpp @@ -213,7 +213,9 @@ void DlgExpressionInput::mousePressEvent(QMouseEvent* ev) #endif //we need to reject the dialog when clicked on the background. As the background is transparent //this is the expected behaviour for the user - this->reject(); + bool on = ui->expression->completerActive(); + if (!on) + this->reject(); } void DlgExpressionInput::showEvent(QShowEvent* ev) @@ -240,8 +242,12 @@ bool DlgExpressionInput::eventFilter(QObject *obj, QEvent *ev) // on the size of the widget. Instead, it must be checked if the // cursor is on this or an underlying widget or outside. if (!underMouse()) { - qApp->removeEventFilter(this); - reject(); + bool on = ui->expression->completerActive(); + // Do this only if the completer is not shown + if (!on) { + qApp->removeEventFilter(this); + reject(); + } } } diff --git a/src/Gui/DocumentPyImp.cpp b/src/Gui/DocumentPyImp.cpp index 06d081748..0fa3105e8 100644 --- a/src/Gui/DocumentPyImp.cpp +++ b/src/Gui/DocumentPyImp.cpp @@ -111,12 +111,7 @@ PyObject* DocumentPy::setEdit(PyObject *args) } bool ok = getDocumentPtr()->setEdit(getDocumentPtr()->getViewProvider(obj),mod); - if (!ok) { - PyErr_Format(Base::BaseExceptionFreeCADError, "Failed to set object '%s' in edit mode", psFeatStr); - return 0; - } - - Py_Return; + return PyBool_FromLong(ok ? 1 : 0); } PyObject* DocumentPy::getInEdit(PyObject *args) diff --git a/src/Gui/FileDialog.cpp b/src/Gui/FileDialog.cpp index 39aff4a0a..503a0d2b6 100644 --- a/src/Gui/FileDialog.cpp +++ b/src/Gui/FileDialog.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include # include +# include # include # include # include @@ -521,7 +522,14 @@ FileChooser::FileChooser ( QWidget * parent ) layout->setMargin( 0 ); layout->setSpacing( 6 ); - lineEdit = new QLineEdit( this ); + lineEdit = new QLineEdit ( this ); + completer = new QCompleter ( this ); + completer->setMaxVisibleItems( 12 ); + fs_model = new QFileSystemModel( completer ); + fs_model->setRootPath(QString::fromUtf8("")); + completer->setModel( fs_model ); + lineEdit->setCompleter( completer ); + layout->addWidget( lineEdit ); connect(lineEdit, SIGNAL(textChanged(const QString &)), diff --git a/src/Gui/FileDialog.h b/src/Gui/FileDialog.h index 1ba5714e2..e5c5948eb 100644 --- a/src/Gui/FileDialog.h +++ b/src/Gui/FileDialog.h @@ -26,6 +26,8 @@ #include #include +#include +#include class QButtonGroup; class QGridLayout; @@ -177,6 +179,8 @@ private Q_SLOTS: private: QLineEdit *lineEdit; + QCompleter *completer; + QFileSystemModel *fs_model; QPushButton *button; Mode md; QString _filter; diff --git a/src/Gui/Quarter/InputDevice.cpp b/src/Gui/Quarter/InputDevice.cpp index 6a074d1cc..d7562a0d6 100644 --- a/src/Gui/Quarter/InputDevice.cpp +++ b/src/Gui/Quarter/InputDevice.cpp @@ -30,6 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \**************************************************************************/ +#ifdef _MSC_VER +#pragma warning(disable : 4267) +#endif + #include #include #include diff --git a/src/Gui/Quarter/Keyboard.cpp b/src/Gui/Quarter/Keyboard.cpp index f6b6f1a27..49ebc987a 100644 --- a/src/Gui/Quarter/Keyboard.cpp +++ b/src/Gui/Quarter/Keyboard.cpp @@ -38,6 +38,10 @@ */ +#ifdef _MSC_VER +#pragma warning(disable : 4267) +#endif + #include #include diff --git a/src/Gui/Quarter/Mouse.cpp b/src/Gui/Quarter/Mouse.cpp index b77b494be..e61673d92 100644 --- a/src/Gui/Quarter/Mouse.cpp +++ b/src/Gui/Quarter/Mouse.cpp @@ -37,6 +37,10 @@ QuarterWidget. */ +#ifdef _MSC_VER +#pragma warning(disable : 4267) +#endif + #include #include diff --git a/src/Gui/Quarter/QuarterWidget.cpp b/src/Gui/Quarter/QuarterWidget.cpp index 9e618027f..6532cc64f 100644 --- a/src/Gui/Quarter/QuarterWidget.cpp +++ b/src/Gui/Quarter/QuarterWidget.cpp @@ -48,6 +48,10 @@ \endcode */ +#ifdef _MSC_VER +#pragma warning(disable : 4267) +#endif + #include #include diff --git a/src/Gui/Quarter/QuarterWidgetP.cpp b/src/Gui/Quarter/QuarterWidgetP.cpp index 36685a909..45f328ceb 100644 --- a/src/Gui/Quarter/QuarterWidgetP.cpp +++ b/src/Gui/Quarter/QuarterWidgetP.cpp @@ -34,6 +34,10 @@ #include #include +#ifdef _MSC_VER +#pragma warning(disable : 4267) +#endif + #include #include #include diff --git a/src/Gui/Quarter/SpaceNavigatorDevice.cpp b/src/Gui/Quarter/SpaceNavigatorDevice.cpp index e207ba503..b6c2d29fe 100644 --- a/src/Gui/Quarter/SpaceNavigatorDevice.cpp +++ b/src/Gui/Quarter/SpaceNavigatorDevice.cpp @@ -30,6 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \**************************************************************************/ +#ifdef _MSC_VER +#pragma warning(disable : 4267) +#endif + #include #include diff --git a/src/Gui/Selection.cpp b/src/Gui/Selection.cpp index 77c94bf2d..9859734c3 100644 --- a/src/Gui/Selection.cpp +++ b/src/Gui/Selection.cpp @@ -989,7 +989,9 @@ PyMethodDef SelectionSingleton::Methods[] = { {"getSelection", (PyCFunction) SelectionSingleton::sGetSelection, 1, "getSelection([string]) -- Return a list of selected objets\n" "Return a list of selected objects for a given document name. If no\n" - "document is given the complete selection is returned."}, + "document name is given the selection for the active document is returned."}, + {"getCompleteSelection", (PyCFunction) SelectionSingleton::sGetCompleteSelection, 1, + "getCompleteSelection() -- Return a list of selected objects of all documents."}, {"getSelectionEx", (PyCFunction) SelectionSingleton::sGetSelectionEx, 1, "getSelectionEx([string]) -- Return a list of SelectionObjects\n" "Return a list of SelectionObjects for a given document name. If no\n" @@ -1107,10 +1109,27 @@ PyObject *SelectionSingleton::sGetSelection(PyObject * /*self*/, PyObject *args, return NULL; // NULL triggers exception std::vector sel; - if (documentName) - sel = Selection().getSelection(documentName); - else - sel = Selection().getCompleteSelection(); + sel = Selection().getSelection(documentName); + + try { + Py::List list; + for (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { + list.append(Py::asObject(it->pObject->getPyObject())); + } + return Py::new_reference_to(list); + } + catch (Py::Exception&) { + return 0; + } +} + +PyObject *SelectionSingleton::sGetCompleteSelection(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + if (!PyArg_ParseTuple(args, "")) // convert args: Python->C + return NULL; // NULL triggers exception + + std::vector sel; + sel = Selection().getCompleteSelection(); try { Py::List list; diff --git a/src/Gui/Selection.h b/src/Gui/Selection.h index bbcb99aad..6e411d319 100644 --- a/src/Gui/Selection.h +++ b/src/Gui/Selection.h @@ -311,6 +311,7 @@ protected: static PyObject *sIsSelected (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sCountObjectsOfType (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sGetSelection (PyObject *self,PyObject *args,PyObject *kwd); + static PyObject *sGetCompleteSelection(PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sGetSelectionEx (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sGetSelectionObject (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sAddSelObserver (PyObject *self,PyObject *args,PyObject *kwd); diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index cf59a9e60..5b7ca50f5 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -36,6 +36,7 @@ # include # include # include +# include # include # include # include @@ -531,6 +532,13 @@ void View3DInventor::print(QPrinter* printer) #else QImage img; QPainter p(printer); + if (!p.isActive() && !printer->outputFileName().isEmpty()) { + qApp->setOverrideCursor(Qt::ArrowCursor); + QMessageBox::critical(this, tr("Opening file failed"), + tr("Can't open file '%1' for writing.").arg(printer->outputFileName())); + qApp->restoreOverrideCursor(); + return; + } QRect rect = printer->pageRect(); bool pbuffer = QGLPixelBuffer::hasOpenGLPbuffers(); diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index 6a0f7c49c..e9e3568c5 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -2151,6 +2151,7 @@ QVariant PropertyPathItem::toolTip(const App::Property* prop) const QWidget* PropertyPathItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const { Gui::FileChooser *fc = new Gui::FileChooser(parent); + fc->setMode(FileChooser::Directory); fc->setAutoFillBackground(true); QObject::connect(fc, SIGNAL(fileNameSelected(const QString&)), receiver, method); return fc; diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index c31bb1c12..4cf7b9809 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -422,11 +422,12 @@ def formatObject(target,origin=None): for p in matchrep.PropertiesList: if not p in ["DisplayMode","BoundingBox","Proxy","RootNode","Visibility"]: if p in obrep.PropertiesList: - if hasattr(getattr(matchrep,p),"Value"): - val = getattr(matchrep,p).Value - else: - val = getattr(matchrep,p) - setattr(obrep,p,val) + if not obrep.getEditorMode(p): + if hasattr(getattr(matchrep,p),"Value"): + val = getattr(matchrep,p).Value + else: + val = getattr(matchrep,p) + setattr(obrep,p,val) if matchrep.DisplayMode in obrep.listDisplayModes(): obrep.DisplayMode = matchrep.DisplayMode if hasattr(matchrep,"DiffuseColor") and hasattr(obrep,"DiffuseColor"): @@ -998,13 +999,14 @@ def makeCopy(obj,force=None,reparent=False): for p in obj.PropertiesList: if not p in ["Proxy"]: if p in newobj.PropertiesList: - try: - setattr(newobj,p,obj.getPropertyByName(p)) - except AttributeError: + if not newobj.getEditorMode(p): try: - setattr(newobj,p,obj.getPropertyByName(p).Value) + setattr(newobj,p,obj.getPropertyByName(p)) except AttributeError: - pass + try: + setattr(newobj,p,obj.getPropertyByName(p).Value) + except AttributeError: + pass if reparent: parents = obj.InList if parents: diff --git a/src/Mod/Drawing/Gui/DrawingView.cpp b/src/Mod/Drawing/Gui/DrawingView.cpp index 64d83fd8d..3cbf54648 100644 --- a/src/Mod/Drawing/Gui/DrawingView.cpp +++ b/src/Mod/Drawing/Gui/DrawingView.cpp @@ -593,6 +593,13 @@ void DrawingView::print(QPrinter* printer) } QPainter p(printer); + if (!p.isActive() && !printer->outputFileName().isEmpty()) { + qApp->setOverrideCursor(Qt::ArrowCursor); + QMessageBox::critical(this, tr("Opening file failed"), + tr("Can't open file '%1' for writing.").arg(printer->outputFileName())); + qApp->restoreOverrideCursor(); + return; + } QRect rect = printer->paperRect(); #ifdef Q_OS_WIN32 // On Windows the preview looks broken when using paperRect as render area. diff --git a/src/Mod/Fem/App/AppFem.cpp b/src/Mod/Fem/App/AppFem.cpp index 11a9dd412..66c3a0275 100644 --- a/src/Mod/Fem/App/AppFem.cpp +++ b/src/Mod/Fem/App/AppFem.cpp @@ -118,6 +118,7 @@ void AppFemExport initFem() Fem::FemAnalysis ::init(); Fem::FemAnalysisPython ::init(); + Fem::FeaturePython ::init(); Fem::FemMesh ::init(); Fem::FemMeshObject ::init(); Fem::FemMeshShapeObject ::init(); diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index 309c1b29c..c2a42d21f 100755 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -88,7 +88,6 @@ SET(FemScripts_SRCS _ViewProviderFemAnalysis.py _FemAnalysis.py _CommandMechanicalShowResult.py - _CommandFrequencyAnalysis.py _CommandQuickAnalysis.py _CommandPurgeFemResults.py _CommandMechanicalJobControl.py diff --git a/src/Mod/Fem/App/FemAnalysis.cpp b/src/Mod/Fem/App/FemAnalysis.cpp index 1abb701b8..1aaa113d2 100644 --- a/src/Mod/Fem/App/FemAnalysis.cpp +++ b/src/Mod/Fem/App/FemAnalysis.cpp @@ -28,6 +28,7 @@ #include "FemAnalysis.h" #include +#include #include #include @@ -88,4 +89,24 @@ template<> const char* Fem::FemAnalysisPython::getViewProviderName(void) const { // explicit template instantiation template class AppFemExport FeaturePythonT; -} \ No newline at end of file +} + +// --------------------------------------------------------- + +namespace App { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(Fem::FeaturePython, App::DocumentObject) +template<> const char* Fem::FeaturePython::getViewProviderName(void) const { + return "Gui::ViewProviderPythonFeature"; +} +template<> PyObject* Fem::FeaturePython::getPyObject(void) { + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new App::FeaturePythonPyT(this),true); + } + return Py::new_reference_to(PythonObject); +} +// explicit template instantiation +template class AppFemExport FeaturePythonT; +/// @endcond +} diff --git a/src/Mod/Fem/App/FemAnalysis.h b/src/Mod/Fem/App/FemAnalysis.h index 9c16f49a8..ea61513de 100644 --- a/src/Mod/Fem/App/FemAnalysis.h +++ b/src/Mod/Fem/App/FemAnalysis.h @@ -65,6 +65,7 @@ protected: }; typedef App::FeaturePythonT FemAnalysisPython; +typedef App::FeaturePythonT FeaturePython; } //namespace Fem diff --git a/src/Mod/Fem/App/FemMesh.cpp b/src/Mod/Fem/App/FemMesh.cpp index b4432b18a..4f2314b28 100644 --- a/src/Mod/Fem/App/FemMesh.cpp +++ b/src/Mod/Fem/App/FemMesh.cpp @@ -911,7 +911,12 @@ void FemMesh::writeABAQUS(const std::string &Filename) const std::ofstream anABAQUS_Output; anABAQUS_Output.open(Filename.c_str()); + + // add nodes + // anABAQUS_Output << "*Node, NSET=Nall" << std::endl; + typedef std::map VertexMap; + VertexMap vertexMap; //Extract Nodes and Elements of the current SMESH datastructure SMDS_NodeIteratorPtr aNodeIter = myMesh->GetMeshDS()->nodesIterator(); @@ -921,10 +926,16 @@ void FemMesh::writeABAQUS(const std::string &Filename) const const SMDS_MeshNode* aNode = aNodeIter->next(); current_node.Set(aNode->X(),aNode->Y(),aNode->Z()); current_node = _Mtrx * current_node; - anABAQUS_Output << aNode->GetID() << ", " - << current_node.x << ", " - << current_node.y << ", " - << current_node.z << std::endl; + vertexMap[aNode->GetID()] = current_node; + } + + // This way we get sorted output. + // See http://forum.freecadweb.org/viewtopic.php?f=18&t=12646&start=40#p103004 + for (VertexMap::iterator it = vertexMap.begin(); it != vertexMap.end(); ++it) { + anABAQUS_Output << it->first << ", " + << it->second.x << ", " + << it->second.y << ", " + << it->second.z << std::endl; } typedef std::map > NodesMap; diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 73de859b0..6eee63132 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -30,7 +30,6 @@ INSTALL( _ViewProviderFemAnalysis.py _FemAnalysis.py _CommandMechanicalShowResult.py - _CommandFrequencyAnalysis.py _CommandQuickAnalysis.py _CommandPurgeFemResults.py _CommandMechanicalJobControl.py diff --git a/src/Mod/Fem/FemBeamSection.py b/src/Mod/Fem/FemBeamSection.py index 20ad7b477..f30fac58b 100644 --- a/src/Mod/Fem/FemBeamSection.py +++ b/src/Mod/Fem/FemBeamSection.py @@ -37,7 +37,7 @@ __url__ = "http://www.freecadweb.org" def makeFemBeamSection(width=20.0, height=20.0, name="BeamSection"): '''makeFemBeamSection([width], [height], [name]): creates an beamsection object to define a cross section''' - obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", name) + obj = FemGui.getActiveAnalysis().Document.addObject("Fem::FeaturePython", name) _FemBeamSection(obj) obj.Width = width obj.Height = height @@ -57,8 +57,7 @@ class _CommandFemBeamSection: def Activated(self): FreeCAD.ActiveDocument.openTransaction("Create FemBeamSection") FreeCADGui.addModule("FemBeamSection") - FreeCADGui.doCommand("FemBeamSection.makeFemBeamSection()") - FreeCADGui.doCommand("App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member = App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member + [App.ActiveDocument.ActiveObject]") + FreeCADGui.doCommand("FemGui.getActiveAnalysis().Member = FemGui.getActiveAnalysis().Member + [FemBeamSection.makeFemBeamSection()]") def IsActive(self): if FemGui.getActiveAnalysis(): diff --git a/src/Mod/Fem/FemCommands.py b/src/Mod/Fem/FemCommands.py index 2fdf417a9..e5abf07f8 100644 --- a/src/Mod/Fem/FemCommands.py +++ b/src/Mod/Fem/FemCommands.py @@ -51,11 +51,11 @@ class FemCommands(object): elif self.is_active == 'with_document': active = FreeCADGui.ActiveDocument is not None elif self.is_active == 'with_analysis': - active = FreeCADGui.ActiveDocument is not None and FemGui.getActiveAnalysis() is not None + active = FemGui.getActiveAnalysis() is not None and self.active_analysis_in_active_doc() elif self.is_active == 'with_results': - active = FreeCADGui.ActiveDocument is not None and FemGui.getActiveAnalysis() is not None and self.results_present() + active = FemGui.getActiveAnalysis() is not None and self.active_analysis_in_active_doc() and self.results_present() elif self.is_active == 'with_part_feature': - active = FreeCADGui.ActiveDocument is not None and FemGui.getActiveAnalysis() is not None and self.part_feature_selected() + active = FemGui.getActiveAnalysis() is not None and self.active_analysis_in_active_doc() and self.part_feature_selected() return active def results_present(self): @@ -72,3 +72,6 @@ class FemCommands(object): return True else: return False + + def active_analysis_in_active_doc(self): + return FemGui.getActiveAnalysis().Document is FreeCAD.ActiveDocument diff --git a/src/Mod/Fem/FemShellThickness.py b/src/Mod/Fem/FemShellThickness.py index f0e2c2c95..9b6c04e94 100644 --- a/src/Mod/Fem/FemShellThickness.py +++ b/src/Mod/Fem/FemShellThickness.py @@ -37,7 +37,7 @@ __url__ = "http://www.freecadweb.org" def makeFemShellThickness(thickness=20.0, name="ShellThickness"): '''makeFemShellThickness([thickness], [name]): creates an shellthickness object to define a plate thickness''' - obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", name) + obj = FemGui.getActiveAnalysis().Document.addObject("Fem::FeaturePython", name) _FemShellThickness(obj) obj.Thickness = thickness if FreeCAD.GuiUp: @@ -56,8 +56,7 @@ class _CommandFemShellThickness: def Activated(self): FreeCAD.ActiveDocument.openTransaction("Create FemShellThickness") FreeCADGui.addModule("FemShellThickness") - FreeCADGui.doCommand("FemShellThickness.makeFemShellThickness()") - FreeCADGui.doCommand("App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member = App.activeDocument()." + FemGui.getActiveAnalysis().Name + ".Member + [App.ActiveDocument.ActiveObject]") + FreeCADGui.doCommand("FemGui.getActiveAnalysis().Member = FemGui.getActiveAnalysis().Member + [FemShellThickness.makeFemShellThickness()]") def IsActive(self): if FemGui.getActiveAnalysis(): diff --git a/src/Mod/Fem/FemTools.py b/src/Mod/Fem/FemTools.py index 015f1bc74..0ee36402f 100644 --- a/src/Mod/Fem/FemTools.py +++ b/src/Mod/Fem/FemTools.py @@ -29,13 +29,21 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): finished = QtCore.Signal(int) - def __init__(self, analysis=None): + known_analysis_types = ["static", "frequency"] + + ## The constructor + # @param analysis - analysis object to be used as the core object. + # @param test_mode - True indicates that no real calculations will take place, so ccx bianry is not required. Used by test module. + # "__init__" tries to use current active analysis in analysis is left empty. + # Rises exception if analysis is not set and there is no active analysis + def __init__(self, analysis=None, test_mode=False): QtCore.QRunnable.__init__(self) QtCore.QObject.__init__(self) - self.known_analysis_types = ["static", "frequency"] - if analysis: + ## @var analysis + # FEM analysis - the core object. Has to be present. + # It's set to analysis passed in "__init__" or set to current active analysis by default if nothing has been passed to "__init__". self.analysis = analysis else: import FemGui @@ -44,10 +52,18 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): self.update_objects() self.set_analysis_type() self.set_eigenmode_parameters() + ## @var base_name + # base name of .inp/.frd file (without extension). It is used to construct .inp file path that is passed to CalculiX ccx self.base_name = "" + ## @var results_present + # boolean variable indicating if there are calculation results ready for use self.results_present = False self.setup_working_dir() - self.setup_ccx() + if test_mode: + self.ccx_binary_present = True + else: + self.ccx_binary_present = False + self.setup_ccx() else: raise Exception('FEM: No active analysis found!') @@ -56,7 +72,7 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): def purge_results(self): for m in self.analysis.Member: if (m.isDerivedFrom('Fem::FemResultObject')): - FreeCAD.ActiveDocument.removeObject(m.Name) + self.analysis.Document.removeObject(m.Name) self.results_present = False ## Resets mesh deformation @@ -80,6 +96,13 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): self.reset_mesh_color() self.reset_mesh_deformation() + ## Sets mesh color using selected type of results (Sabs by default) + # @param self The python object self + # @param result_type Type of FEM result, allowed are: + # - U1, U2, U3 - deformation + # - Uabs - absolute deformation + # - Sabs - Von Mises stress + # @param limit cutoff value. All values over the limit are treated as equel to the limit. Useful for filtering out hot spots. def show_result(self, result_type="Sabs", limit=None): self.update_objects() if result_type == "None": @@ -96,6 +119,10 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): values = list(d[match[result_type]]) self.show_color_by_scalar_with_cutoff(values, limit) + ## Sets mesh color using list of values. Internally used by show_result function. + # @param self The python object self + # @param values list of values + # @param limit cutoff value. All values over the limit are treated as equel to the limit. Useful for filtering out hot spots. def show_color_by_scalar_with_cutoff(self, values, limit=None): if limit: filtered_values = [] @@ -120,10 +147,22 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): # [{'Object':pressure_constraints, 'xxxxxxxx':value}, {}, ...] # [{'Object':beam_sections, 'xxxxxxxx':value}, {}, ...] # [{'Object':shell_thicknesses, 'xxxxxxxx':value}, {}, ...] + + ## @var mesh + # mesh of the analysis. Used to generate .inp file and to show results self.mesh = None self.material = [] + ## @var fixed_constraints + # set of fixed constraints from the analysis. Updated with update_objects + # Individual constraints are "Fem::ConstraintFixed" type self.fixed_constraints = [] + ## @var force_constraints + # set of force constraints from the analysis. Updated with update_objects + # Individual constraints are "Fem::ConstraintForce" type self.force_constraints = [] + ## @var pressure_constraints + # set of pressure constraints from the analysis. Updated with update_objects + # Individual constraints are "Fem::ConstraintPressure" type self.pressure_constraints = [] self.beam_sections = [] self.shell_thicknesses = [] @@ -162,6 +201,11 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): message += "No active Analysis\n" if self.analysis_type not in self.known_analysis_types: message += "Unknown analysis type: {}\n".format(self.analysis_type) + if not self.working_dir: + message += "Working directory not set\n" + import os + if not (os.path.isdir(self.working_dir)): + message += "Working directory \'{}\' doesn't exist.".format(self.working_dir) if not self.mesh: message += "No mesh object in the Analysis\n" if not self.material: @@ -207,7 +251,9 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): import multiprocessing import os import subprocess - if self.inp_file_name != "": + self.ccx_stdout = "" + self.ccx_stderr = "" + if self.inp_file_name != "" and self.ccx_binary_present: ont_backup = os.environ.get('OMP_NUM_THREADS') if not ont_backup: ont_backup = "" @@ -226,7 +272,7 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): return p.returncode return -1 - ## sets eigenmode parameters for CalculiX frequency analysis + ## Sets eigenmode parameters for CalculiX frequency analysis # @param self The python object self # @param number number of eigenmodes that wll be calculated, default 10 # @param limit_low lower value of requested eigenfrequency range, default 0.0 @@ -234,6 +280,9 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): def set_eigenmode_parameters(self, number=10, limit_low=0.0, limit_high=1000000.0): self.eigenmode_parameters = (number, limit_low, limit_high) + ## Sets base_name + # @param self The python object self + # @param base_name base name of .inp/.frd file (without extension). It is used to construct .inp file path that is passed to CalculiX ccx def set_base_name(self, base_name=None): if base_name is None: self.base_name = "" @@ -242,34 +291,55 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): # Update inp file name self.set_inp_file_name() - ## sets inp file name that is used to determine location and name of frd result file. + ## Sets inp file name that is used to determine location and name of frd result file. # Normally inp file name is set set by write_inp_file # Can be used to read mock calculations file + # @param self The python object self + # @inp_file_name .inp file name. If empty the .inp file path is constructed from working_dir, base_name and string ".inp" def set_inp_file_name(self, inp_file_name=None): if inp_file_name is not None: self.inp_file_name = inp_file_name else: self.inp_file_name = self.working_dir + '/' + self.base_name + '.inp' - def set_analysis_type(self, analysis_type=None): - if analysis_type is None: - self.analysis_type = "static" - else: - self.analysis_type = analysis_type - - ## Sets working dir for ccx execution. Called with no working_dir uses WorkingDir for FEM preferences + ## Sets analysis type. # @param self The python object self - # @working_dir directory to be used for .inp file and ccx execution - def setup_working_dir(self, working_dir=None): - if working_dir is None: - self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") - self.working_dir = self.fem_prefs.GetString("WorkingDir", "/tmp") + # @param analysis_type type of the analysis. Allowed values are: + # - static + # - frequency + def set_analysis_type(self, analysis_type=None): + if analysis_type is not None: + self.analysis_type = analysis_type else: + self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") + self.analysis_type = self.fem_prefs.GetString("AnalysisType", "static") + + ## Sets working dir for ccx execution. Called with no working_dir uses WorkingDir from FEM preferences + # @param self The python object self + # @working_dir directory to be used for writing .inp file and executing CalculiX ccx + def setup_working_dir(self, working_dir=None): + import os + if working_dir is not None: self.working_dir = working_dir + else: + self.working_dir = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem").GetString("WorkingDir") + + if not (os.path.isdir(self.working_dir)): + try: + os.makedirs(self.working_dir) + except: + print ("Dir \'{}\' doesn't exist and cannot be created.".format(self.working_dir)) + import tempfile + self.working_dir = tempfile.gettempdir() + print ("Dir \'{}\' will be used instead.".format(self.working_dir)) # Update inp file name self.set_inp_file_name() - def setup_ccx(self, ccx_binary=None): + ## Sets CalculiX ccx binary path and velidates if the binary can be executed + # @param self The python object self + # @ccx_binary path to ccx binary, default is guessed: "bin/ccx" windows, "ccx" for other systems + # @ccx_binary_sig expected output form ccx when run empty. Default value is "CalculiX.exe -i jobname" + def setup_ccx(self, ccx_binary=None, ccx_binary_sig="CalculiX"): if not ccx_binary: self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") ccx_binary = self.fem_prefs.GetString("ccxBinaryPath", "") @@ -282,8 +352,23 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): else: ccx_binary = "ccx" self.ccx_binary = ccx_binary + import subprocess + try: + p = subprocess.Popen([self.ccx_binary], stdout=subprocess.PIPE, + stderr=subprocess.PIPE, shell=False) + ccx_stdout, ccx_stderr = p.communicate() + if ccx_binary_sig in ccx_stdout: + self.ccx_binary_present = True + except OSError, e: + FreeCAD.Console.PrintError(e.message) + if e.errno == 2: + raise Exception("FEM: CalculiX binary ccx \'{}\' not found. Please set it in FEM preferences.".format(ccx_binary)) + except Exception as e: + FreeCAD.Console.PrintError(e.message) + raise Exception("FEM: CalculiX ccx \'{}\' output \'{}\' doesn't contain expected phrase \'{}\'. Please use ccx 2.6 or newer". + format(ccx_binary, ccx_stdout, ccx_binary_sig)) - ## Load results of ccx calculiations from .frd file. + ## Load results of ccx calculations from .frd file. # @param self The python object self def load_results(self): import ccxFrdReader @@ -306,7 +391,7 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): self.result_object = m break if not self.result_object: - raise ("{} doesn't exist".format(results_name)) + raise Exception("{} doesn't exist".format(results_name)) def run(self): ret_code = 0 @@ -330,9 +415,13 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): print self.ccx_stdout print "--------end of stdout---------" - ## returns minimum, average and maximum value for provided result type + ## Returns minimum, average and maximum value for provided result type # @param self The python object self - # @result_type Type of FEM result, allowed U1, U2, U3, Uabs, Sabs and None + # @param result_type Type of FEM result, allowed are: + # - U1, U2, U3 - deformation + # - Uabs - absolute deformation + # - Sabs - Von Mises stress + # - None - always return (0.0, 0.0, 0.0) def get_stats(self, result_type): stats = (0.0, 0.0, 0.0) for m in self.analysis.Member: diff --git a/src/Mod/Fem/Gui/AppFemGui.cpp b/src/Mod/Fem/Gui/AppFemGui.cpp index c8e621f3d..e29c17124 100644 --- a/src/Mod/Fem/Gui/AppFemGui.cpp +++ b/src/Mod/Fem/Gui/AppFemGui.cpp @@ -105,7 +105,6 @@ void FemGuiExport initFemGui() Base::Interpreter().loadModule("FemCommands"); Base::Interpreter().loadModule("_CommandMechanicalShowResult"); - Base::Interpreter().loadModule("_CommandFrequencyAnalysis"); Base::Interpreter().loadModule("_CommandQuickAnalysis"); Base::Interpreter().loadModule("_CommandPurgeFemResults"); Base::Interpreter().loadModule("_CommandMechanicalJobControl"); diff --git a/src/Mod/Fem/Gui/Command.cpp b/src/Mod/Fem/Gui/Command.cpp index 0353cabed..5c6f03a4c 100644 --- a/src/Mod/Fem/Gui/Command.cpp +++ b/src/Mod/Fem/Gui/Command.cpp @@ -240,7 +240,7 @@ void CmdFemConstraintBearing::activated(int iMsg) bool CmdFemConstraintBearing::isActive(void) { - return hasActiveDocument(); + return FemGui::ActiveAnalysisObserver::instance()->hasActiveObject(); } //===================================================================================== @@ -278,7 +278,7 @@ void CmdFemConstraintFixed::activated(int iMsg) bool CmdFemConstraintFixed::isActive(void) { - return hasActiveDocument(); + return FemGui::ActiveAnalysisObserver::instance()->hasActiveObject(); } //===================================================================================== @@ -317,7 +317,7 @@ void CmdFemConstraintForce::activated(int iMsg) bool CmdFemConstraintForce::isActive(void) { - return hasActiveDocument(); + return FemGui::ActiveAnalysisObserver::instance()->hasActiveObject(); } //===================================================================================== @@ -357,7 +357,7 @@ void CmdFemConstraintPressure::activated(int iMsg) bool CmdFemConstraintPressure::isActive(void) { - return hasActiveDocument(); + return FemGui::ActiveAnalysisObserver::instance()->hasActiveObject(); } //===================================================================================== @@ -395,7 +395,7 @@ void CmdFemConstraintGear::activated(int iMsg) bool CmdFemConstraintGear::isActive(void) { - return hasActiveDocument(); + return FemGui::ActiveAnalysisObserver::instance()->hasActiveObject(); } //===================================================================================== @@ -438,7 +438,7 @@ void CmdFemConstraintPulley::activated(int iMsg) bool CmdFemConstraintPulley::isActive(void) { - return hasActiveDocument(); + return FemGui::ActiveAnalysisObserver::instance()->hasActiveObject(); } // ##################################################################################################### diff --git a/src/Mod/Fem/Gui/DlgSettingsFem.ui b/src/Mod/Fem/Gui/DlgSettingsFem.ui index dd09ea329..c97dbfa1f 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFem.ui +++ b/src/Mod/Fem/Gui/DlgSettingsFem.ui @@ -7,7 +7,7 @@ 0 0 555 - 429 + 453 @@ -193,6 +193,73 @@ + + + + Default analysis type + + + + + + + + + + + 148 + 0 + + + + Default type on analysis + + + AnalysisType + + + Mod/Fem + + + + Static + + + + :/icons/fem-new-analysis.svg:/icons/fem-new-analysis.svg + + + + + Frequency + + + + :/icons/fem-frequency-analysis.svg:/icons/fem-frequency-analysis.svg + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + @@ -336,6 +403,11 @@ QCheckBox
Gui/PrefWidgets.h
+ + Gui::PrefComboBox + QComboBox +
Gui/PrefWidgets.h
+
diff --git a/src/Mod/Fem/Gui/DlgSettingsFemImp.cpp b/src/Mod/Fem/Gui/DlgSettingsFemImp.cpp index 1fd363d3d..98d7cb00e 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemImp.cpp +++ b/src/Mod/Fem/Gui/DlgSettingsFemImp.cpp @@ -25,6 +25,7 @@ #include "PreCompiled.h" +#include "Gui/Application.h" #include "DlgSettingsFemImp.h" #include @@ -43,10 +44,15 @@ DlgSettingsFemImp::~DlgSettingsFemImp() void DlgSettingsFemImp::saveSettings() { + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Mod/Fem"); + hGrp->SetInt("AnalysisType", cb_analysis_type->currentIndex()); + fc_ccx_working_directory->onSave(); cb_int_editor->onSave(); fc_ext_editor->onSave(); fc_ccx_binary->onSave(); + cb_analysis_type->onSave(); cb_use_built_in_materials->onSave(); cb_use_mat_from_config_dir->onSave(); cb_use_mat_from_custom_dir->onSave(); @@ -59,10 +65,16 @@ void DlgSettingsFemImp::loadSettings() cb_int_editor->onRestore(); fc_ext_editor->onRestore(); fc_ccx_binary->onRestore(); + cb_analysis_type->onRestore(); cb_use_built_in_materials->onRestore(); cb_use_mat_from_config_dir->onRestore(); cb_use_mat_from_custom_dir->onRestore(); fc_custom_mat_dir->onRestore(); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Mod/Fem"); + int index = hGrp->GetInt("AnalysisType", 0); + if (index > -1) cb_analysis_type->setCurrentIndex(index); } /** @@ -71,7 +83,9 @@ void DlgSettingsFemImp::loadSettings() void DlgSettingsFemImp::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { + int c_index = cb_analysis_type->currentIndex(); retranslateUi(this); + cb_analysis_type->setCurrentIndex(c_index); } else { QWidget::changeEvent(e); diff --git a/src/Mod/Fem/Gui/PreCompiled.h b/src/Mod/Fem/Gui/PreCompiled.h index 28fd50da9..3fc41062f 100644 --- a/src/Mod/Fem/Gui/PreCompiled.h +++ b/src/Mod/Fem/Gui/PreCompiled.h @@ -62,6 +62,9 @@ #include #include +// boost +#include + #ifdef FC_OS_WIN32 # include #endif diff --git a/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp b/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp index e6bb15237..a4bd3ad0d 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp @@ -274,7 +274,7 @@ TaskDlgFemConstraintGear::TaskDlgFemConstraintGear(ViewProviderFemConstraintGear { this->ConstraintView = ConstraintView; assert(ConstraintView); - this->parameter = new TaskFemConstraintGear(ConstraintView, 0, "Fem_ConstraintGear"); + this->parameter = new TaskFemConstraintGear(ConstraintView, 0, "fem-constraint-gear"); Content.push_back(parameter); } diff --git a/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp b/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp index 16d0be6cb..fbc4fbe2e 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp @@ -50,7 +50,7 @@ using namespace Gui; /* TRANSLATOR FemGui::TaskFemConstraintPulley */ TaskFemConstraintPulley::TaskFemConstraintPulley(ViewProviderFemConstraintPulley *ConstraintView,QWidget *parent) - : TaskFemConstraintGear(ConstraintView, parent, "Fem_ConstraintPulley") + : TaskFemConstraintGear(ConstraintView, parent, "fem-constraint-pulley") { connect(ui->spinOtherDiameter, SIGNAL(valueChanged(double)), this, SLOT(onOtherDiameterChanged(double))); diff --git a/src/Mod/Fem/Gui/ViewProviderAnalysis.cpp b/src/Mod/Fem/Gui/ViewProviderAnalysis.cpp index 1ad1ab02e..aa44cee48 100644 --- a/src/Mod/Fem/Gui/ViewProviderAnalysis.cpp +++ b/src/Mod/Fem/Gui/ViewProviderAnalysis.cpp @@ -25,12 +25,16 @@ #ifndef _PreComp_ # include +# include +# include +# include #endif #include "ViewProviderAnalysis.h" #include #include #include +#include #include #include @@ -43,10 +47,7 @@ using namespace FemGui; - - - - +/* TRANSLATOR FemGui::ViewProviderFemAnalysis */ PROPERTY_SOURCE(FemGui::ViewProviderFemAnalysis, Gui::ViewProviderDocumentObject) @@ -85,10 +86,9 @@ std::vector ViewProviderFemAnalysis::claimChildren(void)co void ViewProviderFemAnalysis::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { - //QAction* act; - //act = menu->addAction(QObject::tr("Edit pad"), receiver, member); - //act->setData(QVariant((int)ViewProvider::Default)); - //PartGui::ViewProviderPart::setupContextMenu(menu, receiver, member); + Gui::ActionFunction* func = new Gui::ActionFunction(menu); + QAction* act = menu->addAction(tr("Activate analysis")); + func->trigger(act, boost::bind(&ViewProviderFemAnalysis::doubleClicked, this)); } bool ViewProviderFemAnalysis::setEdit(int ModNum) @@ -119,11 +119,10 @@ bool ViewProviderFemAnalysis::setEdit(int ModNum) // Gui::Control().showDialog(padDlg); // else - Fem::FemAnalysis* pcAna = static_cast(this->getObject()); - - Gui::Control().showDialog(new TaskDlgAnalysis(pcAna)); - - return true; + //Fem::FemAnalysis* pcAna = static_cast(this->getObject()); + //Gui::Control().showDialog(new TaskDlgAnalysis(pcAna)); + //return true; + return false; } else { return Gui::ViewProviderDocumentObject::setEdit(ModNum); @@ -176,6 +175,8 @@ bool ViewProviderFemAnalysis::canDragObject(App::DocumentObject* obj) const return true; else if (obj->getTypeId().isDerivedFrom(Fem::FemSetObject::getClassTypeId())) return true; + else if (obj->getTypeId().isDerivedFrom(Base::Type::fromName("Fem::FeaturePython"))) + return true; else if (obj->getTypeId().isDerivedFrom(App::MaterialObject::getClassTypeId())) return true; else diff --git a/src/Mod/Fem/Gui/ViewProviderAnalysis.h b/src/Mod/Fem/Gui/ViewProviderAnalysis.h index 76efc3379..bb8ba4759 100644 --- a/src/Mod/Fem/Gui/ViewProviderAnalysis.h +++ b/src/Mod/Fem/Gui/ViewProviderAnalysis.h @@ -27,6 +27,7 @@ #include #include #include +#include class SoCoordinate3; class SoDrawStyle; @@ -42,6 +43,7 @@ namespace FemGui class FemGuiExport ViewProviderFemAnalysis : public Gui::ViewProviderDocumentObject { + Q_DECLARE_TR_FUNCTIONS(FemGui::ViewProviderFemAnalysis) PROPERTY_HEADER(FemGui::ViewProviderAnalysis); public: diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index 27592c9d2..e9210037e 100755 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -72,7 +72,6 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "Separator" << "Fem_MechanicalJobControl" << "Fem_Quick_Analysis" - << "Fem_Frequency_Analysis" << "Fem_PurgeResults" << "Fem_ShowResult"; return root; @@ -102,7 +101,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Separator" << "Fem_MechanicalJobControl" << "Fem_Quick_Analysis" - << "Fem_Frequency_Analysis" << "Fem_PurgeResults" << "Fem_ShowResult"; diff --git a/src/Mod/Fem/InitGui.py b/src/Mod/Fem/InitGui.py index 50da9da6e..bb6339365 100755 --- a/src/Mod/Fem/InitGui.py +++ b/src/Mod/Fem/InitGui.py @@ -52,9 +52,29 @@ class FemWorkbench (Workbench): ccx_path = p1.stdout.read().split('\n')[0] elif system() == 'Windows': ccx_path = FreeCAD.getHomePath() + 'bin/ccx.exe' - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem").SetString("ccxBinaryPath", ccx_path) + if ccx_path: + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem").SetString("ccxBinaryPath", ccx_path) + else: + FreeCAD.Console.PrintError("CalculiX ccx binary not found! Please set it manually in FEM preferences.\n") except Exception as e: FreeCAD.Console.PrintError(e.message) + fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") + + import os + working_dir = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem").GetString("WorkingDir") + if not (os.path.isdir(working_dir)): + try: + os.makedirs(working_dir) + except: + print ("Dir \'{}\' from FEM preferences doesn't exist and cannot be created.".format(working_dir)) + import tempfile + working_dir = tempfile.gettempdir() + print ("Dir \'{}\' will be used instead.".format(working_dir)) + if working_dir: + FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem").SetString("WorkingDir", working_dir) + else: + FreeCAD.Console.PrintError("Setting working directory \'{}\' for ccx failed!\n") + def GetClassName(self): return "FemGui::Workbench" diff --git a/src/Mod/Fem/MechanicalAnalysis.ui b/src/Mod/Fem/MechanicalAnalysis.ui index cb23c4cac..779c48270 100644 --- a/src/Mod/Fem/MechanicalAnalysis.ui +++ b/src/Mod/Fem/MechanicalAnalysis.ui @@ -6,8 +6,8 @@ 0 0 - 193 - 384 + 258 + 458
@@ -15,56 +15,103 @@ - - - - - true - - - true + + + Working directory + + + + 0 + + + 0 + + + + + true + + + true + + + + + + + true + + + ... + + + + + + + + + + Analysis type + + + + 0 + + + 0 + + + + + Static + + + true + + + + + + + Frequency + + + + + + + + + + + + Write .inp file - - + + - true + false - ... + Edit .inp file + + + + + + + false + + + Run Calculix - - - - Write Calculix Input File - - - - - - - false - - - Edit Calculix Input File - - - - - - - false - - - Run Calculix - - - @@ -73,7 +120,7 @@ - + 12 @@ -86,6 +133,13 @@
+ + + Gui::PrefRadioButton + QRadioButton +
Gui/PrefWidgets.h
+
+
diff --git a/src/Mod/Fem/MechanicalMaterial.py b/src/Mod/Fem/MechanicalMaterial.py index e2669d49f..0f6820f73 100644 --- a/src/Mod/Fem/MechanicalMaterial.py +++ b/src/Mod/Fem/MechanicalMaterial.py @@ -21,6 +21,7 @@ # *************************************************************************** import FreeCAD +from FemCommands import FemCommands if FreeCAD.GuiUp: import FreeCADGui @@ -45,13 +46,15 @@ def makeMechanicalMaterial(name): return obj -class _CommandMechanicalMaterial: +class _CommandMechanicalMaterial(FemCommands): "the Fem Material command definition" - def GetResources(self): - return {'Pixmap': 'fem-material', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_Material", "Mechanical material..."), - 'Accel': "M, M", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_Material", "Creates or edit the mechanical material definition.")} + def __init__(self): + super(_CommandMechanicalMaterial, self).__init__() + self.resources = {'Pixmap': 'fem-material', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_Material", "Mechanical material..."), + 'Accel': "M, M", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_Material", "Creates or edit the mechanical material definition.")} + self.is_active = 'with_analysis' def Activated(self): MatObj = None @@ -60,6 +63,9 @@ class _CommandMechanicalMaterial: MatObj = i if (not MatObj): + femDoc = FemGui.getActiveAnalysis().Document + if FreeCAD.ActiveDocument is not femDoc: + FreeCADGui.setActiveDocument(femDoc) FreeCAD.ActiveDocument.openTransaction("Create Material") FreeCADGui.addModule("MechanicalMaterial") FreeCADGui.doCommand("MechanicalMaterial.makeMechanicalMaterial('MechanicalMaterial')") @@ -67,14 +73,10 @@ class _CommandMechanicalMaterial: FreeCADGui.doCommand("Gui.activeDocument().setEdit(App.ActiveDocument.ActiveObject.Name,0)") # FreeCADGui.doCommand("Fem.makeMaterial()") else: + if FreeCAD.ActiveDocument is not MatObj.Document: + FreeCADGui.setActiveDocument(MatObj.Document) FreeCADGui.doCommand("Gui.activeDocument().setEdit('" + MatObj.Name + "',0)") - def IsActive(self): - if FemGui.getActiveAnalysis(): - return True - else: - return False - class _MechanicalMaterial: "The Material object" @@ -153,11 +155,13 @@ class _MechanicalMaterialTaskPanel: def accept(self): self.obj.Material = self.material - FreeCADGui.ActiveDocument.resetEdit() - FreeCAD.ActiveDocument.recompute() + doc = FreeCADGui.getDocument(self.obj.Document) + doc.resetEdit() + doc.Document.recompute() def reject(self): - FreeCADGui.ActiveDocument.resetEdit() + doc = FreeCADGui.getDocument(self.obj.Document) + doc.resetEdit() def goMatWeb(self): import webbrowser diff --git a/src/Mod/Fem/TestFem.py b/src/Mod/Fem/TestFem.py index b81131c41..c4561a663 100644 --- a/src/Mod/Fem/TestFem.py +++ b/src/Mod/Fem/TestFem.py @@ -117,9 +117,11 @@ class FemTest(unittest.TestCase): def compare_inp_files(self, file_name1, file_name2): file1 = open(file_name1, 'r') - file2 = open(file_name2, 'r') f1 = file1.readlines() + file1.close() + file2 = open(file_name2, 'r') f2 = file2.readlines() + file2.close() lf1 = [l for l in f1 if not l.startswith('** written ')] lf2 = [l for l in f2 if not l.startswith('** written ')] import difflib @@ -127,8 +129,8 @@ class FemTest(unittest.TestCase): result = '' for l in diff: result += l - file1.close() - file2.close() + if result: + result = "Comparing {} to {} failed!\n".format(file_name1, file_name2) + result return result def compare_stats(self, fea, stat_file=None): @@ -179,17 +181,21 @@ class FemTest(unittest.TestCase): self.assertTrue(self.pressure_constraint, "FemTest of new pressure constraint failed") self.analysis.Member = self.analysis.Member + [self.pressure_constraint] - fea = FemTools.FemTools(self.analysis) - fcc_print('Checking FEM inp file prerequisites...') + fea = FemTools.FemTools(self.analysis, test_mode=True) + fcc_print('Setting up working directory {}'.format(static_analysis_dir)) + fea.setup_working_dir(static_analysis_dir) + self.assertTrue(True if fea.working_dir == static_analysis_dir else False, + "Setting working directory {} failed".format(static_analysis_dir)) + + fcc_print('Checking FEM inp file prerequisites for static analysis...') error = fea.check_prerequisites() self.assertFalse(error, "FemTools check_prerequisites returned error message: {}".format(error)) fcc_print('Checking FEM inp file write...') - fcc_print('Setting up working directory {}'.format(static_analysis_dir)) - fea.setup_working_dir(static_analysis_dir) - self.assertTrue(True if fea.working_dir == static_analysis_dir else False, - "Setting working directory {} failed".format(static_analysis_dir)) + fcc_print('Setting analysis type to \'static\"') + fea.set_analysis_type("static") + self.assertTrue(True if fea.analysis_type == 'static' else False, "Setting anlysis type to \'static\' failed") fcc_print('Writing {}/{}.inp for static analysis'.format(static_analysis_dir, mesh_name)) error = fea.write_inp_file() @@ -232,6 +238,10 @@ class FemTest(unittest.TestCase): self.assertTrue(True if fea.working_dir == frequency_analysis_dir else False, "Setting working directory {} failed".format(frequency_analysis_dir)) + fcc_print('Checking FEM inp file prerequisites for frequency analysis...') + error = fea.check_prerequisites() + self.assertFalse(error, "FemTools check_prerequisites returned error message: {}".format(error)) + fcc_print('Writing {}/{}.inp for frequency analysis'.format(frequency_analysis_dir, mesh_name)) error = fea.write_inp_file() self.assertFalse(error, "Writing failed") diff --git a/src/Mod/Fem/_CommandFrequencyAnalysis.py b/src/Mod/Fem/_CommandFrequencyAnalysis.py deleted file mode 100644 index 0f2ebb88c..000000000 --- a/src/Mod/Fem/_CommandFrequencyAnalysis.py +++ /dev/null @@ -1,64 +0,0 @@ -#*************************************************************************** -#* * -#* Copyright (c) 2013-2015 - Juergen Riegel * -#* * -#* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with this program; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#*************************************************************************** - -__title__ = "Command Frequency Analysis" -__author__ = "Juergen Riegel" -__url__ = "http://www.freecadweb.org" - -import FreeCAD -from FemCommands import FemCommands -from FemTools import FemTools - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore, QtGui - - -class _CommandFrequencyAnalysis(FemCommands): - def __init__(self): - super(_CommandFrequencyAnalysis, self).__init__() - self.resources = {'Pixmap': 'fem-frequency-analysis', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Fem_Frequency_Analysis", "Run frequency analysis with CalculiX ccx"), - 'Accel': "R, F", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Fem_Frequency_Analysis", "Write .inp file and run frequency analysis with CalculiX ccx")} - self.is_active = 'with_analysis' - - def Activated(self): - def load_results(ret_code): - if ret_code == 0: - self.fea.load_results() - else: - print "CalculiX failed ccx finished with error {}".format(ret_code) - - self.fea = FemTools() - self.fea.reset_all() - self.fea.set_analysis_type('frequency') - message = self.fea.check_prerequisites() - if message: - QtGui.QMessageBox.critical(None, "Missing prerequisite", message) - return - self.fea.finished.connect(load_results) - QtCore.QThreadPool.globalInstance().start(self.fea) - - -if FreeCAD.GuiUp: - FreeCADGui.addCommand('Fem_Frequency_Analysis', _CommandFrequencyAnalysis()) diff --git a/src/Mod/Fem/_CommandNewMechanicalAnalysis.py b/src/Mod/Fem/_CommandNewMechanicalAnalysis.py index 3d41f404f..cb243f60e 100644 --- a/src/Mod/Fem/_CommandNewMechanicalAnalysis.py +++ b/src/Mod/Fem/_CommandNewMechanicalAnalysis.py @@ -46,7 +46,6 @@ class _CommandNewMechanicalAnalysis(FemCommands): FreeCAD.ActiveDocument.openTransaction("Create Analysis") FreeCADGui.addModule("FemGui") FreeCADGui.addModule("MechanicalAnalysis") - #FreeCADGui.doCommand("FreeCADGui.ActiveDocument.ActiveView.setAxisCross(True)") FreeCADGui.doCommand("MechanicalAnalysis.makeMechanicalAnalysis('MechanicalAnalysis')") FreeCADGui.doCommand("FemGui.setActiveAnalysis(App.activeDocument().ActiveObject)") sel = FreeCADGui.Selection.getSelection() @@ -57,12 +56,7 @@ class _CommandNewMechanicalAnalysis(FemCommands): FreeCADGui.doCommand("App.activeDocument().addObject('Fem::FemMeshShapeNetgenObject', '" + sel[0].Name + "_Mesh')") FreeCADGui.doCommand("App.activeDocument().ActiveObject.Shape = App.activeDocument()." + sel[0].Name) FreeCADGui.doCommand("FemGui.getActiveAnalysis().Member = FemGui.getActiveAnalysis().Member + [App.activeDocument().ActiveObject]") - #FreeCADGui.doCommand("Gui.activeDocument().hide('" + sel[0].Name + "')") - #FreeCADGui.doCommand("App.activeDocument().ActiveObject.touch()") - #FreeCADGui.doCommand("App.activeDocument().recompute()") FreeCADGui.doCommand("Gui.activeDocument().setEdit(App.ActiveDocument.ActiveObject.Name)") - - #FreeCAD.ActiveDocument.commitTransaction() FreeCADGui.Selection.clearSelection() if FreeCAD.GuiUp: diff --git a/src/Mod/Fem/_CommandQuickAnalysis.py b/src/Mod/Fem/_CommandQuickAnalysis.py index 0c574d5db..07a7938d0 100644 --- a/src/Mod/Fem/_CommandQuickAnalysis.py +++ b/src/Mod/Fem/_CommandQuickAnalysis.py @@ -48,7 +48,7 @@ class _CommandQuickAnalysis(FemCommands): self.fea.load_results() self.show_results_on_mesh() else: - print "CalculiX failed ccx finished with error {}".format(ret_code) + print ("CalculiX failed ccx finished with error {}".format(ret_code)) self.fea = FemTools() self.fea.reset_all() diff --git a/src/Mod/Fem/_FemAnalysis.py b/src/Mod/Fem/_FemAnalysis.py index 3e5f9fb07..943213f30 100644 --- a/src/Mod/Fem/_FemAnalysis.py +++ b/src/Mod/Fem/_FemAnalysis.py @@ -24,13 +24,20 @@ __title__ = "Fem Analysis" __author__ = "Juergen Riegel" __url__ = "http://www.freecadweb.org" +import FreeCAD +from FemTools import FemTools + class _FemAnalysis: "The FemAnalysis container object" def __init__(self, obj): self.Type = "FemAnalysis" obj.Proxy = self - obj.addProperty("App::PropertyString", "OutputDir", "Base", "Directory where the jobs get generated") + fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") + obj.addProperty("App::PropertyEnumeration", "AnalysisType", "Fem", "Type of the analysis") + obj.AnalysisType = FemTools.known_analysis_types + analysis_type = fem_prefs.GetInt("AnalysisType", 0) + obj.AnalysisType = FemTools.known_analysis_types[analysis_type] def execute(self, obj): return diff --git a/src/Mod/Fem/_JobControlTaskPanel.py b/src/Mod/Fem/_JobControlTaskPanel.py index f5a14dbaf..8a7fd9b2c 100644 --- a/src/Mod/Fem/_JobControlTaskPanel.py +++ b/src/Mod/Fem/_JobControlTaskPanel.py @@ -45,7 +45,7 @@ class _JobControlTaskPanel: ccx_binary = self.fem_prefs.GetString("ccxBinaryPath", "") if ccx_binary: self.CalculixBinary = ccx_binary - print "Using ccx binary path from FEM preferences: {}".format(ccx_binary) + print ("Using ccx binary path from FEM preferences: {}".format(ccx_binary)) else: from platform import system if system() == 'Linux': @@ -58,6 +58,7 @@ class _JobControlTaskPanel: self.working_dir = self.fem_prefs.GetString("WorkingDir", '/tmp') self.analysis_object = analysis_object + self.Calculix = QtCore.QProcess() self.Timer = QtCore.QTimer() self.Timer.start(300) @@ -66,9 +67,11 @@ class _JobControlTaskPanel: #Connect Signals and Slots QtCore.QObject.connect(self.form.tb_choose_working_dir, QtCore.SIGNAL("clicked()"), self.choose_working_dir) - QtCore.QObject.connect(self.form.pushButton_write, QtCore.SIGNAL("clicked()"), self.write_input_file_handler) - QtCore.QObject.connect(self.form.pushButton_edit, QtCore.SIGNAL("clicked()"), self.editCalculixInputFile) - QtCore.QObject.connect(self.form.pushButton_generate, QtCore.SIGNAL("clicked()"), self.runCalculix) + QtCore.QObject.connect(self.form.pb_write_inp, QtCore.SIGNAL("clicked()"), self.write_input_file_handler) + QtCore.QObject.connect(self.form.pb_edit_inp, QtCore.SIGNAL("clicked()"), self.editCalculixInputFile) + QtCore.QObject.connect(self.form.pb_run_ccx, QtCore.SIGNAL("clicked()"), self.runCalculix) + QtCore.QObject.connect(self.form.rb_static_analysis, QtCore.SIGNAL("clicked()"), self.select_static_analysis) + QtCore.QObject.connect(self.form.rb_frequency_analysis, QtCore.SIGNAL("clicked()"), self.select_frequency_analysis) QtCore.QObject.connect(self.Calculix, QtCore.SIGNAL("started()"), self.calculixStarted) QtCore.QObject.connect(self.Calculix, QtCore.SIGNAL("stateChanged(QProcess::ProcessState)"), self.calculixStateChanged) @@ -106,16 +109,16 @@ class _JobControlTaskPanel: def UpdateText(self): if(self.Calculix.state() == QtCore.QProcess.ProcessState.Running): - self.form.label_Time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) + self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) def calculixError(self, error): - print "Error()", error + print ("Error() {}".format(error)) self.femConsoleMessage("CalculiX execute error: {}".format(error), "#FF0000") def calculixStarted(self): - print "calculixStarted()" - print self.Calculix.state() - self.form.pushButton_generate.setText("Break Calculix") + print ("calculixStarted()") + print (self.Calculix.state()) + self.form.pb_run_ccx.setText("Break CalculiX") def calculixStateChanged(self, newState): if (newState == QtCore.QProcess.ProcessState.Starting): @@ -126,8 +129,8 @@ class _JobControlTaskPanel: self.femConsoleMessage("CalculiX stopped.") def calculixFinished(self, exitCode): - print "calculixFinished()", exitCode - print self.Calculix.state() + print ("calculixFinished() {}".format(exitCode)) + print (self.Calculix.state()) # Restore previous cwd QtCore.QDir.setCurrent(self.cwd) @@ -137,10 +140,9 @@ class _JobControlTaskPanel: self.femConsoleMessage("Calculix done!", "#00AA00") - self.form.pushButton_generate.setText("Re-run Calculix") - print "Loading results...." + self.form.pb_run_ccx.setText("Re-run CalculiX") self.femConsoleMessage("Loading result sets...") - self.form.label_Time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) + self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) fea = FemTools() fea.reset_all() frd_result_file = os.path.splitext(self.inp_file_name)[0] + '.frd' @@ -151,7 +153,7 @@ class _JobControlTaskPanel: self.femConsoleMessage("Loading results done!", "#00AA00") else: self.femConsoleMessage("Loading results failed! Results file doesn\'t exist", "#FF0000") - self.form.label_Time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) + self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) def getStandardButtons(self): return int(QtGui.QDialogButtonBox.Close) @@ -159,6 +161,10 @@ class _JobControlTaskPanel: def update(self): 'fills the widgets' self.form.le_working_dir.setText(self.working_dir) + if self.analysis_object.AnalysisType == 'static': + self.form.rb_static_analysis.setChecked(True) + elif self.analysis_object.AnalysisType == 'frequency': + self.form.rb_frequency_analysis.setChecked(True) return def accept(self): @@ -168,13 +174,14 @@ class _JobControlTaskPanel: FreeCADGui.Control.closeDialog() def choose_working_dir(self): - self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") - self.working_dir = QtGui.QFileDialog.getExistingDirectory(None, - 'Choose CalculiX working directory', - self.fem_prefs.GetString("WorkingDir", '/tmp')) - if self.working_dir: - self.fem_prefs.SetString("WorkingDir", str(self.working_dir)) - self.form.le_working_dir.setText(self.working_dir) + current_wd = get_working_dir() + wd = QtGui.QFileDialog.getExistingDirectory(None, 'Choose CalculiX working directory', + current_wd) + if wd: + self.analysis_object.WorkingDir = wd + else: + self.analysis_object.WorkingDir = current_wd + self.form.le_working_dir.setText(self.analysis_object.WorkingDir) def write_input_file_handler(self): QApplication.restoreOverrideCursor() @@ -182,13 +189,14 @@ class _JobControlTaskPanel: QApplication.setOverrideCursor(Qt.WaitCursor) self.inp_file_name = "" fea = FemTools() + fea.set_analysis_type(self.analysis_object.AnalysisType) fea.update_objects() fea.write_inp_file() if fea.inp_file_name != "": self.inp_file_name = fea.inp_file_name self.femConsoleMessage("Write completed.") - self.form.pushButton_edit.setEnabled(True) - self.form.pushButton_generate.setEnabled(True) + self.form.pb_edit_inp.setEnabled(True) + self.form.pb_run_ccx.setEnabled(True) else: self.femConsoleMessage("Write .inp file failed!", "#FF0000") QApplication.restoreOverrideCursor() @@ -196,7 +204,7 @@ class _JobControlTaskPanel: def check_prerequisites_helper(self): self.Start = time.time() self.femConsoleMessage("Check dependencies...") - self.form.label_Time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) + self.form.l_time.setText('Time: {0:4.1f}: '.format(time.time() - self.Start)) fea = FemTools() fea.update_objects() @@ -213,7 +221,7 @@ class _JobControlTaskPanel: self.ext_editor_process.start(ext_editor_path, [filename]) def editCalculixInputFile(self): - print 'editCalculixInputFile {}'.format(self.inp_file_name) + print ('editCalculixInputFile {}'.format(self.inp_file_name)) if self.fem_prefs.GetBool("UseInternalEditor", True): FemGui.open(self.inp_file_name) else: @@ -221,18 +229,18 @@ class _JobControlTaskPanel: if ext_editor_path: self.start_ext_editor(ext_editor_path, self.inp_file_name) else: - print "External editor is not defined in FEM preferences. Falling back to internal editor" + print ("External editor is not defined in FEM preferences. Falling back to internal editor") FemGui.open(self.inp_file_name) def runCalculix(self): - print 'runCalculix' + print ('runCalculix') self.Start = time.time() self.femConsoleMessage("CalculiX binary: {}".format(self.CalculixBinary)) self.femConsoleMessage("Run Calculix...") # run Calculix - print 'run Calculix at: ', self.CalculixBinary, ' with: ', os.path.splitext(self.inp_file_name)[0] + print ('run Calculix at: {} with: {}'.format(self.CalculixBinary, os.path.splitext(self.inp_file_name)[0])) # change cwd because ccx may crash if directory has no write permission # there is also a limit of the length of file names so jump to the document directory self.cwd = QtCore.QDir.currentPath() @@ -241,3 +249,30 @@ class _JobControlTaskPanel: self.Calculix.start(self.CalculixBinary, ['-i', fi.baseName()]) QApplication.restoreOverrideCursor() + + def select_analysis_type(self, analysis_type): + if self.analysis_object.AnalysisType != analysis_type: + self.analysis_object.AnalysisType = analysis_type + self.form.pb_edit_inp.setEnabled(False) + self.form.pb_run_ccx.setEnabled(False) + + def select_static_analysis(self): + self.select_analysis_type('static') + + def select_frequency_analysis(self): + self.select_analysis_type('frequency') + + +#Code duplication!!!! +def get_working_dir(): + fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem") + working_dir = fem_prefs.GetString("WorkingDir", "") + if not (os.path.isdir(working_dir)): + try: + os.path.makedirs(working_dir) + except: + print ("Dir \'{}\' from FEM preferences doesn't exist and cannot be created.".format(working_dir)) + import tempfile + working_dir = tempfile.gettempdir() + print ("Dir \'{}\' will be used instead.".format(working_dir)) + return working_dir diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index 922059758..6c9a40b74 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -290,51 +290,58 @@ void PropertyPartShape::SaveDocFile (Base::Writer &writer) const shape.exportBinary(writer.Stream()); } else { - // create a temporary file and copy the content to the zip stream - // once the tmp. filename is known use always the same because otherwise - // we may run into some problems on the Linux platform - static Base::FileInfo fi(App::Application::getTempFileName()); + bool direct = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Mod/Part/General")->GetBool("DirectAccess", false); + if (!direct) { + // create a temporary file and copy the content to the zip stream + // once the tmp. filename is known use always the same because otherwise + // we may run into some problems on the Linux platform + static Base::FileInfo fi(App::Application::getTempFileName()); - if (!BRepTools::Write(myShape,(const Standard_CString)fi.filePath().c_str())) { - // Note: Do NOT throw an exception here because if the tmp. file could - // not be created we should not abort. - // We only print an error message but continue writing the next files to the - // stream... - App::PropertyContainer* father = this->getContainer(); - if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) { - App::DocumentObject* obj = static_cast(father); - Base::Console().Error("Shape of '%s' cannot be written to BRep file '%s'\n", - obj->Label.getValue(),fi.filePath().c_str()); - } - else { - Base::Console().Error("Cannot save BRep file '%s'\n", fi.filePath().c_str()); + if (!BRepTools::Write(myShape,(const Standard_CString)fi.filePath().c_str())) { + // Note: Do NOT throw an exception here because if the tmp. file could + // not be created we should not abort. + // We only print an error message but continue writing the next files to the + // stream... + App::PropertyContainer* father = this->getContainer(); + if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) { + App::DocumentObject* obj = static_cast(father); + Base::Console().Error("Shape of '%s' cannot be written to BRep file '%s'\n", + obj->Label.getValue(),fi.filePath().c_str()); + } + else { + Base::Console().Error("Cannot save BRep file '%s'\n", fi.filePath().c_str()); + } + + std::stringstream ss; + ss << "Cannot save BRep file '" << fi.filePath() << "'"; + writer.addError(ss.str()); } - std::stringstream ss; - ss << "Cannot save BRep file '" << fi.filePath() << "'"; - writer.addError(ss.str()); + Base::ifstream file(fi, std::ios::in | std::ios::binary); + if (file){ + unsigned long ulSize = 0; + std::streambuf* buf = file.rdbuf(); + if (buf) { + unsigned long ulCurr; + ulCurr = buf->pubseekoff(0, std::ios::cur, std::ios::in); + ulSize = buf->pubseekoff(0, std::ios::end, std::ios::in); + buf->pubseekoff(ulCurr, std::ios::beg, std::ios::in); + } + + // read in the ASCII file and write back to the stream + std::strstreambuf sbuf(ulSize); + file >> &sbuf; + writer.Stream() << &sbuf; + } + + file.close(); + // remove temp file + fi.deleteFile(); } - - Base::ifstream file(fi, std::ios::in | std::ios::binary); - if (file){ - unsigned long ulSize = 0; - std::streambuf* buf = file.rdbuf(); - if (buf) { - unsigned long ulCurr; - ulCurr = buf->pubseekoff(0, std::ios::cur, std::ios::in); - ulSize = buf->pubseekoff(0, std::ios::end, std::ios::in); - buf->pubseekoff(ulCurr, std::ios::beg, std::ios::in); - } - - // read in the ASCII file and write back to the stream - std::strstreambuf sbuf(ulSize); - file >> &sbuf; - writer.Stream() << &sbuf; + else { + BRepTools::Write(myShape, writer.Stream()); } - - file.close(); - // remove temp file - fi.deleteFile(); } } @@ -347,47 +354,55 @@ void PropertyPartShape::RestoreDocFile(Base::Reader &reader) setValue(shape); } else { - BRep_Builder builder; + bool direct = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Mod/Part/General")->GetBool("DirectAccess", false); + if (!direct) { + BRep_Builder builder; + // create a temporary file and copy the content from the zip stream + Base::FileInfo fi(App::Application::getTempFileName()); - // create a temporary file and copy the content from the zip stream - Base::FileInfo fi(App::Application::getTempFileName()); + // read in the ASCII file and write back to the file stream + Base::ofstream file(fi, std::ios::out | std::ios::binary); + unsigned long ulSize = 0; + if (reader) { + std::streambuf* buf = file.rdbuf(); + reader >> buf; + file.flush(); + ulSize = buf->pubseekoff(0, std::ios::cur, std::ios::in); + } + file.close(); - // read in the ASCII file and write back to the file stream - Base::ofstream file(fi, std::ios::out | std::ios::binary); - unsigned long ulSize = 0; - if (reader) { - std::streambuf* buf = file.rdbuf(); - reader >> buf; - file.flush(); - ulSize = buf->pubseekoff(0, std::ios::cur, std::ios::in); - } - file.close(); - - // Read the shape from the temp file, if the file is empty the stored shape was already empty. - // If it's still empty after reading the (non-empty) file there must occurred an error. - TopoDS_Shape shape; - if (ulSize > 0) { - if (!BRepTools::Read(shape, (const Standard_CString)fi.filePath().c_str(), builder)) { - // Note: Do NOT throw an exception here because if the tmp. created file could - // not be read it's NOT an indication for an invalid input stream 'reader'. - // We only print an error message but continue reading the next files from the - // stream... - App::PropertyContainer* father = this->getContainer(); - if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) { - App::DocumentObject* obj = static_cast(father); - Base::Console().Error("BRep file '%s' with shape of '%s' seems to be empty\n", - fi.filePath().c_str(),obj->Label.getValue()); - } - else { - Base::Console().Warning("Loaded BRep file '%s' seems to be empty\n", fi.filePath().c_str()); + // Read the shape from the temp file, if the file is empty the stored shape was already empty. + // If it's still empty after reading the (non-empty) file there must occurred an error. + TopoDS_Shape shape; + if (ulSize > 0) { + if (!BRepTools::Read(shape, (const Standard_CString)fi.filePath().c_str(), builder)) { + // Note: Do NOT throw an exception here because if the tmp. created file could + // not be read it's NOT an indication for an invalid input stream 'reader'. + // We only print an error message but continue reading the next files from the + // stream... + App::PropertyContainer* father = this->getContainer(); + if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) { + App::DocumentObject* obj = static_cast(father); + Base::Console().Error("BRep file '%s' with shape of '%s' seems to be empty\n", + fi.filePath().c_str(),obj->Label.getValue()); + } + else { + Base::Console().Warning("Loaded BRep file '%s' seems to be empty\n", fi.filePath().c_str()); + } } } + + // delete the temp file + fi.deleteFile(); + setValue(shape); + } + else { + BRep_Builder builder; + TopoDS_Shape shape; + BRepTools::Read(shape, reader, builder); + setValue(shape); } - - // delete the temp file - fi.deleteFile(); - - setValue(shape); } } diff --git a/src/Mod/Path/libarea/PythonStuff.cpp b/src/Mod/Path/libarea/PythonStuff.cpp index 42d40e3cf..9029dc6e1 100644 --- a/src/Mod/Path/libarea/PythonStuff.cpp +++ b/src/Mod/Path/libarea/PythonStuff.cpp @@ -51,6 +51,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef __GNUG__ #pragma implementation #endif +#ifdef _MSC_VER +#pragma warning(disable : 4244) +#endif #include #include diff --git a/src/Mod/Points/App/PreCompiled.h b/src/Mod/Points/App/PreCompiled.h index e4a6109a2..827c0d986 100644 --- a/src/Mod/Points/App/PreCompiled.h +++ b/src/Mod/Points/App/PreCompiled.h @@ -30,19 +30,17 @@ #ifdef FC_OS_WIN32 # define PointsExport __declspec(dllexport) #else // for Linux -# define PointsExport +# define PointsExport #endif -#ifdef _PreComp_ - // here get the warnings of too long specifiers disabled (needed for VC6) #ifdef _MSC_VER -# pragma warning( disable : 4251 ) -# pragma warning( disable : 4503 ) -# pragma warning( disable : 4275 ) -# pragma warning( disable : 4786 ) // specifier longer then 255 chars +# pragma warning( disable : 4181 ) +# pragma warning( disable : 4305 ) #endif +#ifdef _PreComp_ + // standard #include #include diff --git a/src/Mod/Robot/App/kdl_cp/utilities/svd_eigen_HH.cpp b/src/Mod/Robot/App/kdl_cp/utilities/svd_eigen_HH.cpp index 36da50914..35349039f 100644 --- a/src/Mod/Robot/App/kdl_cp/utilities/svd_eigen_HH.cpp +++ b/src/Mod/Robot/App/kdl_cp/utilities/svd_eigen_HH.cpp @@ -19,6 +19,11 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +#ifdef _MSC_VER +#pragma warning(disable : 4244) +#pragma warning(disable : 4800) +#endif + #include "svd_eigen_HH.hpp" namespace KDL{ diff --git a/src/Mod/Sketcher/App/planegcs/GCS.cpp b/src/Mod/Sketcher/App/planegcs/GCS.cpp index 7de4aeda2..4cc104ed7 100644 --- a/src/Mod/Sketcher/App/planegcs/GCS.cpp +++ b/src/Mod/Sketcher/App/planegcs/GCS.cpp @@ -19,6 +19,11 @@ * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable : 4244) +#endif + #include #include #include diff --git a/src/Mod/Sketcher/App/planegcs/qp_eq.cpp b/src/Mod/Sketcher/App/planegcs/qp_eq.cpp index d2c63ee75..42619750a 100644 --- a/src/Mod/Sketcher/App/planegcs/qp_eq.cpp +++ b/src/Mod/Sketcher/App/planegcs/qp_eq.cpp @@ -19,6 +19,11 @@ * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable : 4244) +#endif + #include #include diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index a9b524598..1452a0b09 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -107,6 +107,7 @@ #include "ViewProviderSketch.h" #include "DrawSketchHandler.h" #include "TaskDlgEditSketch.h" +#include "TaskSketcherValidation.h" // The first is used to point at a SoDatumLabel for some // constraints, and at a SoMaterial for others... @@ -4151,7 +4152,7 @@ void ViewProviderSketch::attach(App::DocumentObject *pcFeat) void ViewProviderSketch::setupContextMenu(QMenu *menu, QObject *receiver, const char *member) { - menu->addAction(QObject::tr("Edit sketch"), receiver, member); + menu->addAction(tr("Edit sketch"), receiver, member); } bool ViewProviderSketch::setEdit(int ModNum) @@ -4165,8 +4166,8 @@ bool ViewProviderSketch::setEdit(int ModNum) sketchDlg = 0; // another sketch left open its task panel if (dlg && !sketchDlg) { QMessageBox msgBox; - msgBox.setText(QObject::tr("A dialog is already open in the task panel")); - msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setText(tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(tr("Do you want to close this dialog?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); @@ -4178,8 +4179,21 @@ bool ViewProviderSketch::setEdit(int ModNum) Sketcher::SketchObject* sketch = getSketchObject(); if (!sketch->evaluateConstraints()) { - QMessageBox::critical(Gui::getMainWindow(), tr("Invalid sketch"), - tr("The sketch is invalid and cannot be edited.\nUse the sketch validation tool.")); + QMessageBox box(Gui::getMainWindow()); + box.setIcon(QMessageBox::Critical); + box.setWindowTitle(tr("Invalid sketch")); + box.setText(tr("Do you want to open the sketch validation tool?")); + box.setInformativeText(tr("The sketch is invalid and cannot be edited.")); + box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + box.setDefaultButton(QMessageBox::Yes); + switch (box.exec()) + { + case QMessageBox::Yes: + Gui::Control().showDialog(new TaskSketcherValidation(getSketchObject())); + break; + default: + break; + } return false; } diff --git a/src/Mod/Spreadsheet/App/SpreadsheetExpression.cpp b/src/Mod/Spreadsheet/App/SpreadsheetExpression.cpp index b69a2c86d..807ed22d2 100644 --- a/src/Mod/Spreadsheet/App/SpreadsheetExpression.cpp +++ b/src/Mod/Spreadsheet/App/SpreadsheetExpression.cpp @@ -62,6 +62,8 @@ #if defined(_MSC_VER) #define strtoll _strtoi64 +#pragma warning(disable : 4003) +#pragma warning(disable : 4065) #endif using namespace App; diff --git a/src/Mod/Test/BaseTests.py b/src/Mod/Test/BaseTests.py index 3b059dde5..668ccdf14 100644 --- a/src/Mod/Test/BaseTests.py +++ b/src/Mod/Test/BaseTests.py @@ -179,6 +179,13 @@ class ParameterTestCase(unittest.TestCase): self.failUnless(b.XLength==0,"X length > 0") self.failUnless(b.YLength==0,"Y length > 0") self.failUnless(b.ZLength==0,"Z length > 0") + self.failUnless(b.Center==FreeCAD.Vector(0,0,0),"Center is not at (0,0,0)") + self.failUnless(b.isInside(b.Center),"Center is not inside Bbox") + b.add(2,2,2) + self.failUnless(b.isInside(b.getIntersectionPoint(b.Center,FreeCAD.Vector(0,1,0))),"Intersection point is not inside Bbox") + self.failUnless(b.intersect(b),"Bbox doesn't intersect with itself") + self.failUnless(not b.intersected(FreeCAD.BoundBox(4,4,4,6,6,6)).isValid(),"Bbox should not intersect with Bbox outside") + self.failUnless(b.intersected(FreeCAD.BoundBox(-2,-2,-2,2,2,2)).Center == b.Center,"Bbox is not a full subset") def testNesting(self): # Parameter testing diff --git a/src/Mod/Test/Gui/AppTestGui.cpp b/src/Mod/Test/Gui/AppTestGui.cpp index 7c2ef2142..548e43106 100644 --- a/src/Mod/Test/Gui/AppTestGui.cpp +++ b/src/Mod/Test/Gui/AppTestGui.cpp @@ -79,41 +79,6 @@ private: return Py::None(); } }; -/* -static PyObject* addTest(PyObject *self, PyObject *args) -{ - char *pstr=0; - if (!PyArg_ParseTuple(args, "|s", &pstr)) // convert args: Python->C - return NULL; // NULL triggers exception - - TestGui::UnitTestDialog* dlg = TestGui::UnitTestDialog::instance(); - if (pstr) - dlg->addUnitTest(QString::fromLatin1(pstr)); - dlg->show(); - dlg->raise(); - Py_Return; -} - -static PyObject* setTest(PyObject *self, PyObject *args) -{ - char *pstr=0; - if (!PyArg_ParseTuple(args, "|s", &pstr)) // convert args: Python->C - return NULL; // NULL triggers exception - - TestGui::UnitTestDialog* dlg = TestGui::UnitTestDialog::instance(); - if (pstr) - dlg->setUnitTest(QString::fromLatin1(pstr)); - dlg->show(); - dlg->raise(); - Py_Return; -} -*/ -/* registration table */ -//static struct PyMethodDef TestGui_methods[] = { -// {"addTest", addTest, 1}, -// {"setTest", setTest, 1}, -// {NULL, NULL} /* end of table marker */ -//}; void loadTestResource() { @@ -130,10 +95,6 @@ void AppTestGuiExport initQtUnitGui() // with the Python runtime system (void)new UnitTestModule; - //if(PyType_Ready(&TestGui::UnitTestPy::Type) < 0) return; - //PyObject* pyModule = Py_InitModule("QtUnitGui", TestGui_methods); /* mod name, table ptr */ - //union PyType_Object pyDlgType = {&TestGui::UnitTestPy::Type}; - //PyModule_AddObject(pyModule, "UnitTest", pyDlgType.o); Base::Console().Log("Loading GUI of Test module... done\n"); // add resources and reloads the translators diff --git a/src/Mod/Test/Gui/UnitTestImp.cpp b/src/Mod/Test/Gui/UnitTestImp.cpp index baae6d015..5ecf4d52c 100644 --- a/src/Mod/Test/Gui/UnitTestImp.cpp +++ b/src/Mod/Test/Gui/UnitTestImp.cpp @@ -117,7 +117,22 @@ void UnitTestDialog::setProgressColor(const QColor& col) */ void UnitTestDialog::on_treeViewFailure_itemDoubleClicked(QTreeWidgetItem * item, int column) { - QMessageBox::information(this, item->text(0), item->data(0, Qt::UserRole).toString()); + QString text = item->data(0, Qt::UserRole).toString(); + + QMessageBox msgBox(this); + msgBox.setIcon(QMessageBox::Information); + msgBox.setWindowTitle(item->text(0)); + msgBox.setDetailedText(text); + + // truncate the visible text when it's too long + if (text.count(QLatin1Char('\n')) > 20) { + QStringList lines = text.split(QLatin1Char('\n')); + lines.erase(lines.begin()+20, lines.end()); + text = lines.join(QLatin1String("\n")); + } + + msgBox.setText(text); + msgBox.exec(); } /** @@ -229,6 +244,14 @@ void UnitTestDialog::setUnitTest(const QString& unit) } } +/** + * Clears the unit tests. + */ +void UnitTestDialog::clearUnitTests() +{ + this->comboTests->clear(); +} + /** * Returns the unit test. */ diff --git a/src/Mod/Test/Gui/UnitTestImp.h b/src/Mod/Test/Gui/UnitTestImp.h index ec8556bc0..ad7e6daef 100644 --- a/src/Mod/Test/Gui/UnitTestImp.h +++ b/src/Mod/Test/Gui/UnitTestImp.h @@ -38,6 +38,7 @@ public: void showErrorDialog(const char* title, const char* message); void addUnitTest(const QString& unit); void setUnitTest(const QString& unit); + void clearUnitTests(); QString getUnitTest() const; void setStatusText(const QString& text); void setProgressFraction(float fraction, const QString& = QString::null); diff --git a/src/Mod/Test/Gui/UnitTestPy.cpp b/src/Mod/Test/Gui/UnitTestPy.cpp index 2311d0930..2a9bda1b0 100644 --- a/src/Mod/Test/Gui/UnitTestPy.cpp +++ b/src/Mod/Test/Gui/UnitTestPy.cpp @@ -55,6 +55,8 @@ void UnitTestDialogPy::init_type() add_varargs_method("setErrorCount",&UnitTestDialogPy::setErrorCount,"setErrorCount"); add_varargs_method("setRemainCount",&UnitTestDialogPy::setRemainCount,"setRemainCount"); add_varargs_method("updateGUI",&UnitTestDialogPy::updateGUI,"updateGUI"); + add_varargs_method("addUnitTest",&UnitTestDialogPy::addUnitTest,"addUnitTest"); + add_varargs_method("clearUnitTests",&UnitTestDialogPy::clearUnitTests,"clearUnitTests"); } UnitTestDialogPy::UnitTestDialogPy() @@ -191,262 +193,25 @@ Py::Object UnitTestDialogPy::updateGUI(const Py::Tuple& args) { if (!PyArg_ParseTuple(args.ptr(), "")) throw Py::Exception(); - qApp->processEvents(); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); return Py::None(); } -//-------------------------------------------------------------------------- -// Type structure -//-------------------------------------------------------------------------- - -PyTypeObject TestGui::UnitTestPy::Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, /*ob_size*/ - "TestGui.UnitTest", /*tp_name*/ - sizeof(UnitTestPy), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - PyDestructor, /*tp_dealloc*/ - 0, /*tp_print*/ - __getattr, /*tp_getattr*/ - __setattr, /*tp_setattr*/ - 0, /*tp_compare*/ - __repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call */ - 0, /*tp_str */ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - /* --- Functions to access object as input/output buffer ---------*/ - 0, /* tp_as_buffer */ - /* --- Flags to define presence of optional/expanded features */ - Py_TPFLAGS_HAVE_CLASS, /*tp_flags */ - "About TestGui.UnitTest", /*tp_doc */ - 0, /*tp_traverse */ - 0, /*tp_clear */ - 0, /*tp_richcompare */ - 0, /*tp_weaklistoffset */ - 0, /*tp_iter */ - 0, /*tp_iternext */ - 0, /*tp_methods */ - 0, /*tp_members */ - 0, /*tp_getset */ - &Base::PyObjectBase::Type, /*tp_base */ - 0, /*tp_dict */ - 0, /*tp_descr_get */ - 0, /*tp_descr_set */ - 0, /*tp_dictoffset */ - 0, /*tp_init */ - 0, /*tp_alloc */ - UnitTestPy::PyMake, /*tp_new */ - 0, /*tp_free Low-level free-memory routine */ - 0, /*tp_is_gc For PyObject_IS_GC */ - 0, /*tp_bases */ - 0, /*tp_mro method resolution order */ - 0, /*tp_cache */ - 0, /*tp_subclasses */ - 0 /*tp_weaklist */ - -}; - -//-------------------------------------------------------------------------- -// Methods structure -//-------------------------------------------------------------------------- -PyMethodDef TestGui::UnitTestPy::Methods[] = { - PYMETHODEDEF(clearErrorList) - PYMETHODEDEF(insertError) - PYMETHODEDEF(setUnitTest) - PYMETHODEDEF(getUnitTest) - PYMETHODEDEF(setStatusText) - PYMETHODEDEF(setProgressFraction) - PYMETHODEDEF(errorDialog) - PYMETHODEDEF(setRunCount) - PYMETHODEDEF(setFailCount) - PYMETHODEDEF(setErrorCount) - PYMETHODEDEF(setRemainCount) - PYMETHODEDEF(updateGUI) - {NULL, NULL} /* Sentinel */ -}; - -//-------------------------------------------------------------------------- -// Constructor -//-------------------------------------------------------------------------- -TestGui::UnitTestPy::UnitTestPy(PyTypeObject *T) -: PyObjectBase(0, T) +Py::Object UnitTestDialogPy::addUnitTest(const Py::Tuple& args) { + char *pstr; + if (!PyArg_ParseTuple(args.ptr(), "s", &pstr)) + throw Py::Exception(); + + TestGui::UnitTestDialog* dlg = TestGui::UnitTestDialog::instance(); + dlg->addUnitTest(QString::fromLatin1(pstr)); + return Py::None(); } -PyObject *UnitTestPy::PyMake(PyTypeObject *ignored, PyObject *args, PyObject *kwds) // Python wrapper +Py::Object UnitTestDialogPy::clearUnitTests(const Py::Tuple& args) { - return new UnitTestPy(); + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + UnitTestDialog::instance()->clearUnitTests(); + return Py::None(); } - -//-------------------------------------------------------------------------- -// destructor -//-------------------------------------------------------------------------- -UnitTestPy::~UnitTestPy() // Everything handled in parent -{ -} - - -//-------------------------------------------------------------------------- -// UnitTestPy representation -//-------------------------------------------------------------------------- -PyObject *UnitTestPy::_repr(void) -{ - return Py_BuildValue("s", "UnitTest"); -} - -//-------------------------------------------------------------------------- -// UnitTestPy Attributes -//-------------------------------------------------------------------------- -PyObject *UnitTestPy::_getattr(char *attr) // __getattr__ function: note only need to handle new state -{ - _getattr_up(PyObjectBase); -} - -int UnitTestPy::_setattr(char *attr, PyObject *value) // __setattr__ function: note only need to handle new state -{ - return PyObjectBase::_setattr(attr, value); -} - - -//-------------------------------------------------------------------------- -// Python wrappers -//-------------------------------------------------------------------------- - -PYFUNCIMP_D(UnitTestPy,clearErrorList) -{ - PY_TRY { - UnitTestDialog::instance()->clearErrorList(); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,insertError) -{ - char *failure=0; - char *details=0; - if (!PyArg_ParseTuple(args, "ss", &failure,&details)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->insertError(QString::fromLatin1(failure), - QString::fromLatin1(details)); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,setUnitTest) -{ - char *pstr=0; - if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->setUnitTest(QString::fromLatin1(pstr)); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,getUnitTest) -{ - if (!PyArg_ParseTuple(args, "")) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - return Py_BuildValue("s", (const char*)UnitTestDialog::instance()->getUnitTest().toAscii()); - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,setStatusText) -{ - char *pstr=0; - if (!PyArg_ParseTuple(args, "s", &pstr)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->setStatusText(QString::fromLatin1(pstr)); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,setProgressFraction) -{ - float fraction; - char* pColor=0; - if (!PyArg_ParseTuple(args, "f|s",&fraction, &pColor)) // convert args: Python->C - return NULL; // NULL triggers exception - - PY_TRY { - if (pColor) - UnitTestDialog::instance()->setProgressFraction(fraction,QString::fromLatin1(pColor)); - else - UnitTestDialog::instance()->setProgressFraction(fraction); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,errorDialog) -{ - char *title=0; - char *message=0; - if (!PyArg_ParseTuple(args, "ss", &title, &message)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->showErrorDialog(title,message); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,setRunCount) -{ - int count; - if (!PyArg_ParseTuple(args, "i", &count)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->setRunCount(count); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,setFailCount) -{ - int count; - if (!PyArg_ParseTuple(args, "i", &count)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->setFailCount(count); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,setErrorCount) -{ - int count; - if (!PyArg_ParseTuple(args, "i", &count)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->setErrorCount(count); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,setRemainCount) -{ - int count; - if (!PyArg_ParseTuple(args, "i", &count)) // convert args: Python->C - return NULL; // NULL triggers exception - PY_TRY { - UnitTestDialog::instance()->setRemainCount(count); - Py_Return; - }PY_CATCH; -} - -PYFUNCIMP_D(UnitTestPy,updateGUI) -{ - PY_TRY { - qApp->processEvents(); - Py_Return; - }PY_CATCH; -} - diff --git a/src/Mod/Test/Gui/UnitTestPy.h b/src/Mod/Test/Gui/UnitTestPy.h index c2a24222f..9b41112f0 100644 --- a/src/Mod/Test/Gui/UnitTestPy.h +++ b/src/Mod/Test/Gui/UnitTestPy.h @@ -56,6 +56,8 @@ public: Py::Object setErrorCount (const Py::Tuple&); Py::Object setRemainCount (const Py::Tuple&); Py::Object updateGUI (const Py::Tuple&); + Py::Object addUnitTest (const Py::Tuple&); + Py::Object clearUnitTests (const Py::Tuple&); private: typedef PyObject* (*method_varargs_handler)(PyObject *_self, PyObject *_args); @@ -63,43 +65,6 @@ private: static PyObject *method_varargs_ext_handler(PyObject *_self, PyObject *_args); }; -//=========================================================================== -// UnitTestPy - Python wrapper -//=========================================================================== - -class UnitTestPy :public Base::PyObjectBase -{ - Py_Header; - -protected: - ~UnitTestPy(); - -public: - UnitTestPy(PyTypeObject *T = &Type); - static PyObject *PyMake(PyTypeObject *, PyObject *, PyObject *); - - //--------------------------------------------------------------------- - // python exports goes here +++++++++++++++++++++++++++++++++++++++++++ - //--------------------------------------------------------------------- - - virtual PyObject *_repr(void); // the representation - PyObject *_getattr(char *attr); // __getattr__ function - int _setattr(char *attr, PyObject *value); // __setattr__ function - - PYFUNCDEF_D(UnitTestPy,clearErrorList) - PYFUNCDEF_D(UnitTestPy,insertError) - PYFUNCDEF_D(UnitTestPy,setUnitTest) - PYFUNCDEF_D(UnitTestPy,getUnitTest) - PYFUNCDEF_D(UnitTestPy,setStatusText) - PYFUNCDEF_D(UnitTestPy,setProgressFraction) - PYFUNCDEF_D(UnitTestPy,errorDialog) - PYFUNCDEF_D(UnitTestPy,setRunCount) - PYFUNCDEF_D(UnitTestPy,setFailCount) - PYFUNCDEF_D(UnitTestPy,setErrorCount) - PYFUNCDEF_D(UnitTestPy,setRemainCount) - PYFUNCDEF_D(UnitTestPy,updateGUI) -}; - } //namespace TESTGUI_UNITTESTPY_H diff --git a/src/Mod/Test/TestApp.py b/src/Mod/Test/TestApp.py index 9ee92b6b4..b57bafb4e 100644 --- a/src/Mod/Test/TestApp.py +++ b/src/Mod/Test/TestApp.py @@ -70,7 +70,8 @@ def Test(s): def testAll(): - TestText(All()) + r = unittest.TextTestRunner(stream=sys.stdout, verbosity=2) + r.run(All()) def testUnit():