<commit_before># ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2015 <NAME> <<EMAIL>>              *
# *                                                                         *
# *   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__ = "FreeCAD FEM solver calculix ViewProvider for the document object"
__author__ = "<NAME>"
__url__ = "http://www.freecadweb.org"

## @package ViewProviderFemSolverCalculix
#  \ingroup FEM
#  \brief FreeCAD FEM _ViewProviderFemSolverCalculix

import FreeCAD
import FreeCADGui
import FemGui

# for the panel
from PySide import QtCore
from PySide import QtGui
from PySide.QtCore import Qt
from PySide.QtGui import QApplication
import os
import time
import sys
if sys.version_info.major >= 3:
    def unicode(text, *args):
        return str(text)


class _ViewProviderFemSolverCalculix:
    "A View Provider for the FemSolverCalculix object"

    def __init__(self, vobj):
        vobj.Proxy = self

    def getIcon(self):
        return ":/icons/fem-solver-standard.svg"

    def attach(self, vobj):
        self.ViewObject = vobj
        self.Object = vobj.Object

    def updateData(self, obj, prop):
        return

    def onChanged(self, vobj, prop):
        return

    def setEdit(self, vobj, mode=0):
        taskd = _TaskPanelFemSolverCalculix(self.Object)
        FreeCADGui.Control.showDialog(taskd)
        return True

    def unsetEdit(self, vobj, mode=0):
        FreeCADGui.Control.closeDialog()
        return True

    def doubleClicked(self, vobj):
        doc = FreeCADGui.getDocument(vobj.Object.Document)
        if not doc.getInEdit():
            doc.setEdit(vobj.Object.Name)
        else:
            from PySide.QtGui import QMessageBox
            message = "Active Task Dialog found! Please close this one before opening  a new one!"
            QMessageBox.critical(None, "Error in tree view", message)
            FreeCAD.Console.PrintError(message + "\n")
        return True

    def __getstate__(self):
        return None

    def __setstate__(self, state):
        return None


class _TaskPanelFemSolverCalculix:
    """The TaskPanel for CalculiX ccx tools solver object"""

    def __init__(self, solver_object):
        self.form = FreeCADGui.PySideUic.loadUi(
            FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/SolverCalculix.ui"
        )

        from femtools.ccxtools import CcxTools as ccx
        # we do not need to pass the analysis, it will be found on fea init
        self.fea = ccx(solver_object)
        self.fea.setup_working_dir()
        self.fea.setup_ccx()

        self.Calculix = QtCore.QProcess()
        self.Timer = QtCore.QTimer()
        self.Timer.start(300)

        self.fem_console_message = ""

        # 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.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.form.rb_thermomech_analysis,
            QtCore.SIGNAL("clicked()"),
            self.select_thermomech_analysis
        )
        QtCore.QObject.connect(
            self.form.rb_check_mesh,
            QtCore.SIGNAL("clicked()"),
            self.select_check_mesh
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("started()"),
            self.calculixStarted
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("stateChanged(QProcess::ProcessState)"),
            self.calculixStateChanged
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("error(QProcess::ProcessError)"),
            self.calculixError
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("finished(int)"),
            self.calculixFinished
        )
        QtCore.QObject.connect(
            self.Timer,
            QtCore.SIGNAL("timeout()"),
            self.UpdateText
        )

        self.update()

    def getStandardButtons(self):
        # only show a close button
        # def accept() in no longer needed, since there is no OK button
        return int(QtGui.QDialogButtonBox.Close)

    def reject(self):
        FreeCADGui.ActiveDocument.resetEdit()

    def update(self):
        "fills the widgets"
        self.form.le_working_dir.setText(self.fea.working_dir)
        if self.fea.solver.AnalysisType == "static":
            self.form.rb_static_analysis.setChecked(True)
        elif self.fea.solver.AnalysisType == "frequency":
            self.form.rb_frequency_analysis.setChecked(True)
        elif self.fea.solver.AnalysisType == "thermomech":
            self.form.rb_thermomech_analysis.setChecked(True)
        elif self.fea.solver.AnalysisType == "check":
            self.form.rb_check_mesh.setChecked(True)
        return

    def femConsoleMessage(self, message="", color="#000000"):
        if sys.version_info.major < 3:
            message = message.encode("utf-8", "replace")
        self.fem_console_message = self.fem_console_message + (
            '<font color="#0000FF">{0:4.1f}:</font> <font color="{1}">{2}</font><br>'
            .format(time.time() - self.Start, color, message)
        )
        self.form.textEdit_Output.setText(self.fem_console_message)
        self.form.textEdit_Output.moveCursor(QtGui.QTextCursor.End)

    def printCalculiXstdout(self):
        out = self.Calculix.readAllStandardOutput()
        if out.isEmpty():
            self.femConsoleMessage("CalculiX stdout is empty", "#FF0000")
        else:
            try:
                out = unicode(out, "utf-8", "replace")
                rx = QtCore.QRegExp("\\*ERROR.*\\n\\n")
                print(rx)
                rx.setMinimal(True)
                pos = rx.indexIn(out)
                while not pos < 0:
                    match = rx.cap(0)
                    FreeCAD.Console.PrintError(match.strip().replace("\n", " ") + "\n")
                    pos = rx.indexIn(out, pos + 1)
                out = os.linesep.join([s for s in out.splitlines() if s])
                self.femConsoleMessage(out.replace("\n", "<br>"))
            except UnicodeDecodeError:
                self.femConsoleMessage("Error converting stdout from CalculiX", "#FF0000")
        if "*ERROR in e_c3d: nonpositive jacobian" in out:
            error_message = (
                "\n\nCalculiX returned an error due to "
                "nonpositive jacobian determinant in at least one element\n"
                "Use the run button on selected solver to get a better error output.\n"
            )
            FreeCAD.Console.PrintError(error_message)
        if "*ERROR" in out:
            return False
        else:
            return True

    def UpdateText(self):
        if(self.Calculix.state() == QtCore.QProcess.ProcessState.Running):
            self.form.l_time.setText("Time: {0:4.1f}: ".format(time.time() - self.Start))

    def calculixError(self, error=""):
        print("Error() {}".format(error))
        self.femConsoleMessage("CalculiX execute error: {}".format(error), "#FF0000")

    def calculixNoError(self):
        print("CalculiX done without error!")
        self.femConsoleMessage("CalculiX done without error!", "#00AA00")

    def calculixStarted(self):
        print("calculixStarted()")
        print(self.Calculix.state())
        self.form.pb_run_ccx.setText("Break CalculiX")

    def calculixStateChanged(self, newState):<commit_msg>FEM: calculix task panel, Py3 fix for ccx output print<commit_after># ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2015 <NAME> <<EMAIL>>              *
# *                                                                         *
# *   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__ = "FreeCAD FEM solver calculix ViewProvider for the document object"
__author__ = "<NAME>"
__url__ = "http://www.freecadweb.org"

## @package ViewProviderFemSolverCalculix
#  \ingroup FEM
#  \brief FreeCAD FEM _ViewProviderFemSolverCalculix

import FreeCAD
import FreeCADGui
import FemGui

# for the panel
from PySide import QtCore
from PySide import QtGui
from PySide.QtCore import Qt
from PySide.QtGui import QApplication
import os
import time
import sys
if sys.version_info.major >= 3:
    def unicode(text, *args):
        return str(text)


class _ViewProviderFemSolverCalculix:
    "A View Provider for the FemSolverCalculix object"

    def __init__(self, vobj):
        vobj.Proxy = self

    def getIcon(self):
        return ":/icons/fem-solver-standard.svg"

    def attach(self, vobj):
        self.ViewObject = vobj
        self.Object = vobj.Object

    def updateData(self, obj, prop):
        return

    def onChanged(self, vobj, prop):
        return

    def setEdit(self, vobj, mode=0):
        taskd = _TaskPanelFemSolverCalculix(self.Object)
        FreeCADGui.Control.showDialog(taskd)
        return True

    def unsetEdit(self, vobj, mode=0):
        FreeCADGui.Control.closeDialog()
        return True

    def doubleClicked(self, vobj):
        doc = FreeCADGui.getDocument(vobj.Object.Document)
        if not doc.getInEdit():
            doc.setEdit(vobj.Object.Name)
        else:
            from PySide.QtGui import QMessageBox
            message = "Active Task Dialog found! Please close this one before opening  a new one!"
            QMessageBox.critical(None, "Error in tree view", message)
            FreeCAD.Console.PrintError(message + "\n")
        return True

    def __getstate__(self):
        return None

    def __setstate__(self, state):
        return None


class _TaskPanelFemSolverCalculix:
    """The TaskPanel for CalculiX ccx tools solver object"""

    def __init__(self, solver_object):
        self.form = FreeCADGui.PySideUic.loadUi(
            FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/SolverCalculix.ui"
        )

        from femtools.ccxtools import CcxTools as ccx
        # we do not need to pass the analysis, it will be found on fea init
        self.fea = ccx(solver_object)
        self.fea.setup_working_dir()
        self.fea.setup_ccx()

        self.Calculix = QtCore.QProcess()
        self.Timer = QtCore.QTimer()
        self.Timer.start(300)

        self.fem_console_message = ""

        # 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.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.form.rb_thermomech_analysis,
            QtCore.SIGNAL("clicked()"),
            self.select_thermomech_analysis
        )
        QtCore.QObject.connect(
            self.form.rb_check_mesh,
            QtCore.SIGNAL("clicked()"),
            self.select_check_mesh
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("started()"),
            self.calculixStarted
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("stateChanged(QProcess::ProcessState)"),
            self.calculixStateChanged
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("error(QProcess::ProcessError)"),
            self.calculixError
        )
        QtCore.QObject.connect(
            self.Calculix,
            QtCore.SIGNAL("finished(int)"),
            self.calculixFinished
        )
        QtCore.QObject.connect(
            self.Timer,
            QtCore.SIGNAL("timeout()"),
            self.UpdateText
        )

        self.update()

    def getStandardButtons(self):
        # only show a close button
        # def accept() in no longer needed, since there is no OK button
        return int(QtGui.QDialogButtonBox.Close)

    def reject(self):
        FreeCADGui.ActiveDocument.resetEdit()

    def update(self):
        "fills the widgets"
        self.form.le_working_dir.setText(self.fea.working_dir)
        if self.fea.solver.AnalysisType == "static":
            self.form.rb_static_analysis.setChecked(True)
        elif self.fea.solver.AnalysisType == "frequency":
            self.form.rb_frequency_analysis.setChecked(True)
        elif self.fea.solver.AnalysisType == "thermomech":
            self.form.rb_thermomech_analysis.setChecked(True)
        elif self.fea.solver.AnalysisType == "check":
            self.form.rb_check_mesh.setChecked(True)
        return

    def femConsoleMessage(self, message="", color="#000000"):
        if sys.version_info.major < 3:
            message = message.encode("utf-8", "replace")
        self.fem_console_message = self.fem_console_message + (
            '<font color="#0000FF">{0:4.1f}:</font> <font color="{1}">{2}</font><br>'
            .format(time.time() - self.Start, color, message)
        )
        self.form.textEdit_Output.setText(self.fem_console_message)
        self.form.textEdit_Output.moveCursor(QtGui.QTextCursor.End)

    def printCalculiXstdout(self):

        out = self.Calculix.readAllStandardOutput()
        # print(type(out))
        # <class 'PySide2.QtCore.QByteArray'>

        if out.isEmpty():
            self.femConsoleMessage("CalculiX stdout is empty", "#FF0000")
            return False 

        if sys.version_info.major >= 3:
            # https://forum.freecadweb.org/viewtopic.php?f=18&t=39195 
            # convert QByteArray to a binary string an decode it to "utf-8"
            out = out.data().decode()  # "utf-8" can be omitted
            # print(type(out))
            # print(out)
        else:
            try:
                out = unicode(out, "utf-8", "replace")
                rx = QtCore.QRegExp("\\*ERROR.*\\n\\n")
                # print(rx)
                rx.setMinimal(True)
                pos = rx.indexIn(out)
                while not pos < 0:
                    match = rx.cap(0)
                    FreeCAD.Console.PrintError(match.strip().replace("\n", " ") + "\n")
                    pos = rx.indexIn(out, pos + 1)
            except UnicodeDecodeError:
                self.femConsoleMessage("Error converting stdout from CalculiX", "#FF0000")
        out = os.linesep.join([s for s in out.splitlines() if s])
        out = out.replace("\n", "<br>")
        # print(out)
        self.femConsoleMessage(out)

        if "*ERROR in e_c3d: nonpositive jacobian" in out:
            error_message = (
                "\n\nCalculiX returned an error due to "
                "nonpositive jacobian determinant in at least one element\n"
                "Use the run button on selected solver to get a better error output.\n"
            )
            FreeCAD.Console.PrintError(error_message)

        if "*ERROR" in out:
            return False
        else:
            return True

    def UpdateText(self):
        if(self.Calculix.state() == QtCore.QProcess.ProcessState.Running):
            self.form.l_time.setText("Time: {0:4.1f}: ".format(time.time() - self.Start))

    def calculixError(self, error=""):
        print("Error() {}".format(error))
        self.femConsoleMessage("CalculiX execute error: {}".format(error), "#FF0000")

    def calculixNoError(self):
        print("CalculiX done without error!")
        self.femConsoleMessage("CalculiX done without error!", "#00AA00")

    def calculixStarted(self):
        print("calculixStarted()")
        print(self.Calculix.state())
        self.form.pb_run_ccx.setText("Break CalculiX")

    def calculixStateChanged(self, newState):
{'fraction_non_alphanumeric': 0.11338336775298444, 'fraction_numerical': 0.007253311842038986, 'mean_word_length': 1.8761408083441982, 'pattern_counts': {'":': 10, '<': 28, '<?xml version=': 0, '>': 27, 'https://': 1, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2979446', 'n_tokens_mistral': 5767, 'n_tokens_neox': 5534, 'n_words': 1239}