| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "FreeCAD FEM solver job control task panel" |
| | __author__ = "Markus Hovorka" |
| | __url__ = "https://www.freecad.org" |
| |
|
| | |
| | |
| |
|
| | from PySide import QtCore |
| | from PySide import QtGui |
| |
|
| | import FreeCADGui as Gui |
| |
|
| | import femsolver.report |
| | import femsolver.run |
| |
|
| |
|
| | _UPDATE_INTERVAL = 50 |
| | _REPORT_TITLE = "Run Report" |
| | _REPORT_ERR = "Failed to run. Please try again after all of the following errors are resolved." |
| |
|
| |
|
| | class ControlTaskPanel(QtCore.QObject): |
| |
|
| | machineChanged = QtCore.Signal(object) |
| | machineStarted = QtCore.Signal(object) |
| | machineStopped = QtCore.Signal(object) |
| | machineStatusChanged = QtCore.Signal(str) |
| | machineStatusCleared = QtCore.Signal() |
| | machineTimeChanged = QtCore.Signal(float) |
| | machineStateChanged = QtCore.Signal(float) |
| |
|
| | def __init__(self, machine): |
| | super().__init__() |
| | self.form = ControlWidget() |
| | self._machine = None |
| |
|
| | |
| | self._timer = QtCore.QTimer() |
| | self._timer.setInterval(_UPDATE_INTERVAL) |
| | self._timer.timeout.connect(self._timeProxy) |
| |
|
| | |
| | self.form.writeClicked.connect(self.write) |
| | self.form.editClicked.connect(self.edit) |
| | self.form.runClicked.connect(self.run) |
| | self.form.abortClicked.connect(self.abort) |
| | self.form.directoryChanged.connect(self.updateMachine) |
| |
|
| | |
| | |
| | self.form.destroyed.connect(self._disconnectMachine) |
| | self.form.destroyed.connect(self._timer.stop) |
| | |
| | |
| | |
| |
|
| | |
| | self.machineStarted.connect(self._timer.start) |
| | self.machineStarted.connect(self.form.updateState) |
| | self.machineStopped.connect(self._timer.stop) |
| | self.machineStopped.connect(self._displayReport) |
| | self.machineStopped.connect(self.form.updateState) |
| | self.machineStatusChanged.connect(self.form.appendStatus) |
| | self.machineStatusCleared.connect(self.form.clearStatus) |
| | self.machineTimeChanged.connect(self.form.setTime) |
| | self.machineStateChanged.connect(lambda: self.form.updateState(self.machine)) |
| | self.machineChanged.connect(self._updateTimer) |
| |
|
| | |
| | self.machineChanged.connect(self.updateWidget) |
| | self.form.destroyed.connect(lambda: self.machineChanged.disconnect(self.updateWidget)) |
| |
|
| | self.machine = machine |
| |
|
| | @property |
| | def machine(self): |
| | return self._machine |
| |
|
| | @machine.setter |
| | def machine(self, value): |
| | self._connectMachine(value) |
| | self._machine = value |
| | self.machineChanged.emit(value) |
| |
|
| | @QtCore.Slot() |
| | def write(self): |
| | self.machine.reset() |
| | self.machine.target = femsolver.run.PREPARE |
| | self.machine.start() |
| |
|
| | @QtCore.Slot() |
| | def run(self): |
| | self.machine.reset(femsolver.run.SOLVE) |
| | self.machine.target = femsolver.run.RESULTS |
| | self.machine.start() |
| |
|
| | @QtCore.Slot() |
| | def edit(self): |
| | self.machine.reset(femsolver.run.SOLVE) |
| | self.machine.solver.Proxy.edit(self.machine.directory) |
| |
|
| | @QtCore.Slot() |
| | def abort(self): |
| | self.machine.abort() |
| |
|
| | @QtCore.Slot() |
| | def updateWidget(self): |
| | self.form.setDirectory(self.machine.directory) |
| | self.form.setStatus(self.machine.status) |
| | self.form.setTime(self.machine.time) |
| | self.form.updateState(self.machine) |
| |
|
| | @QtCore.Slot() |
| | def updateMachine(self): |
| | if self.form.directory() != self.machine.directory: |
| | self.machine = femsolver.run.getMachine(self.machine.solver, self.form.directory()) |
| |
|
| | @QtCore.Slot() |
| | def _updateTimer(self): |
| | if self.machine.running: |
| | self._timer.start() |
| |
|
| | @QtCore.Slot(object) |
| | def _displayReport(self, machine): |
| | text = _REPORT_ERR if machine.failed else None |
| | femsolver.report.display(machine.report, _REPORT_TITLE, text) |
| |
|
| | def getStandardButtons(self): |
| | return QtGui.QDialogButtonBox.Close |
| |
|
| | def reject(self): |
| | Gui.ActiveDocument.resetEdit() |
| |
|
| | def _connectMachine(self, machine): |
| | self._disconnectMachine() |
| | machine.signalStatus.add(self._statusProxy) |
| | machine.signalStatusCleared.add(self._statusClearedProxy) |
| | machine.signalStarted.add(self._startedProxy) |
| | machine.signalStopped.add(self._stoppedProxy) |
| | machine.signalState.add(self._stateProxy) |
| |
|
| | def _disconnectMachine(self): |
| | if self.machine is not None: |
| | self.machine.signalStatus.remove(self._statusProxy) |
| | self.machine.signalStatusCleared.add(self._statusClearedProxy) |
| | self.machine.signalStarted.remove(self._startedProxy) |
| | self.machine.signalStopped.remove(self._stoppedProxy) |
| | self.machine.signalState.remove(self._stateProxy) |
| |
|
| | def _startedProxy(self): |
| | self.machineStarted.emit(self.machine) |
| |
|
| | def _stoppedProxy(self): |
| | self.machineStopped.emit(self.machine) |
| |
|
| | def _statusProxy(self, line): |
| | self.machineStatusChanged.emit(line) |
| |
|
| | def _statusClearedProxy(self): |
| | self.machineStatusCleared.emit() |
| |
|
| | def _timeProxy(self): |
| | time = self.machine.time |
| | self.machineTimeChanged.emit(time) |
| |
|
| | def _stateProxy(self): |
| | state = self.machine.state |
| | self.machineStateChanged.emit(state) |
| |
|
| |
|
| | class ControlWidget(QtGui.QWidget): |
| |
|
| | writeClicked = QtCore.Signal() |
| | editClicked = QtCore.Signal() |
| | runClicked = QtCore.Signal() |
| | abortClicked = QtCore.Signal() |
| | directoryChanged = QtCore.Signal() |
| |
|
| | def __init__(self, parent=None): |
| | super().__init__(parent) |
| | self._setupUi() |
| | self._inputFileName = "" |
| |
|
| | def _setupUi(self): |
| | self.setWindowTitle(self.tr("Solver Control")) |
| | |
| | self._directoryTxt = QtGui.QLineEdit() |
| | self._directoryTxt.editingFinished.connect(self.directoryChanged) |
| | directoryBtt = QtGui.QToolButton() |
| | directoryBtt.setText("...") |
| | directoryBtt.clicked.connect(self._selectDirectory) |
| | directoryLyt = QtGui.QHBoxLayout() |
| | directoryLyt.addWidget(self._directoryTxt) |
| | directoryLyt.addWidget(directoryBtt) |
| | self._directoryGrp = QtGui.QGroupBox() |
| | self._directoryGrp.setTitle(self.tr("Working Directory")) |
| | self._directoryGrp.setLayout(directoryLyt) |
| |
|
| | |
| | self._writeBtt = QtGui.QPushButton(self.tr("Write")) |
| | self._editBtt = QtGui.QPushButton(self.tr("Edit")) |
| | self._runBtt = QtGui.QPushButton() |
| | self._writeBtt.clicked.connect(self.writeClicked) |
| | self._editBtt.clicked.connect(self.editClicked) |
| | actionLyt = QtGui.QGridLayout() |
| | actionLyt.addWidget(self._writeBtt, 0, 0) |
| | actionLyt.addWidget(self._editBtt, 0, 1) |
| | actionLyt.addWidget(self._runBtt, 1, 0, 1, 2) |
| |
|
| | |
| | self._statusEdt = QtGui.QPlainTextEdit() |
| | self._statusEdt.setReadOnly(True) |
| | |
| | |
| | self._statusEdt.setMinimumHeight(300) |
| |
|
| | |
| | timeHeaderLbl = QtGui.QLabel(self.tr("Elapsed Time:")) |
| | self._timeLbl = QtGui.QLabel() |
| | timeLyt = QtGui.QHBoxLayout() |
| | timeLyt.addWidget(timeHeaderLbl) |
| | timeLyt.addWidget(self._timeLbl) |
| | timeLyt.addStretch() |
| | timeLyt.setContentsMargins(0, 0, 0, 0) |
| | self._timeWid = QtGui.QWidget() |
| | self._timeWid.setLayout(timeLyt) |
| |
|
| | |
| | layout = QtGui.QVBoxLayout() |
| | layout.addWidget(self._directoryGrp) |
| | layout.addLayout(actionLyt) |
| | layout.addWidget(self._statusEdt) |
| | layout.addWidget(self._timeWid) |
| | self.setLayout(layout) |
| |
|
| | @QtCore.Slot(str) |
| | def setStatus(self, text): |
| | if text is None: |
| | text = "" |
| | self._statusEdt.setPlainText(text) |
| | self._statusEdt.moveCursor(QtGui.QTextCursor.End) |
| |
|
| | def status(self): |
| | return self._statusEdt.plainText() |
| |
|
| | @QtCore.Slot(str) |
| | def appendStatus(self, line): |
| | self._statusEdt.moveCursor(QtGui.QTextCursor.End) |
| | self._statusEdt.insertPlainText(line) |
| | self._statusEdt.moveCursor(QtGui.QTextCursor.End) |
| |
|
| | @QtCore.Slot(str) |
| | def clearStatus(self): |
| | self._statusEdt.setPlainText("") |
| |
|
| | @QtCore.Slot(float) |
| | def setTime(self, time): |
| | timeStr = "<b>%05.1f</b>" % time if time is not None else "" |
| | self._timeLbl.setText(timeStr) |
| |
|
| | def time(self): |
| | if self._timeLbl.text() == "": |
| | return None |
| | return float(self._timeLbl.text()) |
| |
|
| | @QtCore.Slot(float) |
| | def setDirectory(self, directory): |
| | self._directoryTxt.setText(directory) |
| |
|
| | def directory(self): |
| | return self._directoryTxt.text() |
| |
|
| | @QtCore.Slot(int) |
| | def updateState(self, machine): |
| | if machine.state <= femsolver.run.PREPARE: |
| | self._writeBtt.setText(self.tr("Write")) |
| | self._editBtt.setText(self.tr("Edit")) |
| | self._runBtt.setText(self.tr("Run")) |
| | elif machine.state <= femsolver.run.SOLVE: |
| | self._writeBtt.setText(self.tr("Re-write")) |
| | self._editBtt.setText(self.tr("Edit")) |
| | self._runBtt.setText(self.tr("Run")) |
| | else: |
| | self._writeBtt.setText(self.tr("Re-write")) |
| | self._editBtt.setText(self.tr("Edit")) |
| | self._runBtt.setText(self.tr("Re-run")) |
| | if machine.running: |
| | self._runBtt.setText(self.tr("Abort")) |
| | self.setRunning(machine) |
| |
|
| | @QtCore.Slot() |
| | def _selectDirectory(self): |
| | path = QtGui.QFileDialog.getExistingDirectory(self) |
| | self.setDirectory(path) |
| | self.directoryChanged.emit() |
| |
|
| | def setRunning(self, machine): |
| | if machine.running: |
| | self._runBtt.clicked.connect(self.runClicked) |
| | self._runBtt.clicked.disconnect() |
| | self._runBtt.clicked.connect(self.abortClicked) |
| | self._directoryGrp.setDisabled(True) |
| | self._writeBtt.setDisabled(True) |
| | self._editBtt.setDisabled(True) |
| | else: |
| | self._runBtt.clicked.connect(self.abortClicked) |
| | self._runBtt.clicked.disconnect() |
| | self._runBtt.clicked.connect(self.runClicked) |
| | self._directoryGrp.setDisabled(False) |
| | self._writeBtt.setDisabled(False) |
| | self._editBtt.setDisabled( |
| | not machine.solver.Proxy.editSupported() or machine.state <= femsolver.run.PREPARE |
| | ) |
| |
|
| |
|
| | |
| |
|