# -*- coding: utf-8 -*-
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE', which is part of this source code package.
#
import os
import re
import functools
import signal
import numpy as np
from functools import partial
from Muscat.Helpers.IO.TemporaryDirectory import TemporaryDirectory
from Muscat.Helpers.IO.FileTools import WriteTempFile
from Muscat.Helpers import ParserHelper as PH
from Muscat.Helpers import LocalVariables as LV
import Muscat.ImplicitGeometry.ImplicitGeometryObjects as IGObs
from Muscat.Helpers.Logger import Info, Debug
from Muscat.Containers.Filters.FilterObjects import ElementFilter
from Muscat.Containers.Filters.FilterOperators import ComplementaryFilter
import OpenPisco.QtApp.QtImplementation as QT
import OpenPisco.CLApp.OpenPiscoCL as MainApp
from OpenPisco.QtApp.res import ResourcePath
from OpenPisco.QtApp.QtWorker import Worker
from OpenPisco.QtApp.OpenPiscoUIMainWindow import Ui_MainWindow
from OpenPisco.QtApp.OpenPiscoVTKRenderZone import StopoVTKRenderZone
exportSTLPath = "/var/spool/print/"
[docs]class SimpleView(QT.QMainWindow):
printInStatusBar = QT.Signal(str)
setFullScreen = QT.Signal()
MaxRecentFiles = 10
def __init__(self, parent = None,with_demo=True):
QT.QMainWindow.__init__(self, parent)
self.with_demo = with_demo
self.MainApp = MainApp.MainApp()
self.thread = Worker(self)
self.ui = Ui_MainWindow(self)
self.vtk = StopoVTKRenderZone(self)
# for recent files
self.recentFileActs = []
self.actions = {}
self.CreateActions()
self.ui.setupUi(self)
self.vtk.setup3DView(self)
self.thread.finished.connect(self.UpdateApp)
self.printInStatusBar.connect(self.statusBar().showMessage)
self.setFullScreen.connect(self.SetFullScreen)
settings = QT.QtCore.QSettings()
self.restoreGeometry(settings.value("geometry", bytearray()))#.toPyObject()
self.restoreState(settings.value("windowState", bytearray()))#.toPyObject()
commandlogfilename = TemporaryDirectory.GetTempPath()+"commands.py"
self.commandlogfile = open(commandlogfilename,"w")
self.WriteToCommandLog("import numpy as np")
self.WriteToCommandLog("import xml.etree.ElementTree as ElementTree")
self.WriteToCommandLog("import OpenPisco.CLApp.MainApp as MainApp ")
self.WriteToCommandLog("import Muscat.ImplicitGeometry.ImplicitGeometryObjects as IGObs")
self.WriteToCommandLog("import Muscat.ImplicitGeometry.ImplicitGeometryOperators as IGOps")
self.WriteToCommandLog("mainApp = MainApp.MainApp() ")
print("Sending commands log to "+ commandlogfilename)
self.colormapBy = 1
self.single_window = False
self.time = 0
self.currentFile = None
self.erodeDilateScale = 200
self.interactiveSphereRadius = 40
[docs] def ResetApp(self):
self.ui.ObjectInspectorEditorScrollArea.setWidget(QT.QWidget())
self.ResetMainApp()
self.ui.controlPanelAppScrollArea.setWidget(QT.QWidget())
self.ui.inputFileEditorTextEdit.setPlainText("")
self.ResetIsoOrDensity()
self.vtk.Reset()
self.ui.tab2D.Reset()
self.UpdateApp()
[docs] def ResetMainApp(self):
if self.MainApp is not None:
self.MainApp.CloseAllOutputs()
# we keep the same parsertype for the reader
ext = self.MainApp.inputReader.ext
self.MainApp = MainApp.MainApp()
self.MainApp.SetInternalReaderTo(ext)
self.ui._pythonConsole.pushVariables({"mainApp":self.MainApp})
self.WriteToCommandLog("mainApp = MainApp.MainApp() ")
[docs] def SetFullScreen(self,val=True):
if val == False:
self.showNormal()
return
import time
ctime = time.time()
if ctime-self.time > 1:
self.hide()
self.showNormal()
self.hide()
QT.QTimer.singleShot(500,self.showFullScreen)
#self.showFullScreen()
self.time = ctime
else:
self.setFullScreen.emit()
[docs] def GetActiveLevelset(self):
IDD = self.ui.GetActiveLevelset()
if IDD == -1:
return (None,None)
ls = self.MainApp.levelSets[ int(IDD)]
return (IDD,ls)
[docs] def WhatToPlotFunction(self):
res = dict()
res["sideToPlot"] = self.ui.combo_inout.itemData(self.ui.combo_inout.currentIndex())
_,ls = self.GetActiveLevelset()
if ls is None:
return {}
cutByName = self.ui.GetActiveFieldToPlot()
if cutByName is None:
return {}
if "phi" in cutByName.lower() :
if "phi" == cutByName.lower() :
cutBy = ls.phi
else:
cutBy = ls.extraNodesFields[cutByName]
elif cutByName.startswith("field: "):
cutBy = ls.extraNodesFields[cutByName.split(": ")[1]]
else:
activeZone = self.MainApp.zones[int(cutByName)]
cutBy = activeZone(ls.support)
res["cutByField"] = cutBy
res["cutByName"] = cutByName
res["cutByValue"] = float(self.ui.ls_slider.value())
ls.extraNodesFields["phi"] = ls.phi
colorby = (self.colormapBy)%len(ls.extraNodesFields)
res["colorByField"] = list(ls.extraNodesFields.values())[colorby]
res["colorByName"] = list(ls.extraNodesFields.keys())[colorby]
res["cutOrExtractInsideElements"] = self.ui.viewLevelset
return res
[docs] def ViewNextField(self):
self.colormapBy += 1
self.vtk.update3D.emit()
[docs] def Exit(self):
settings = QT.QSettings()
settings.setValue("geometry", self.saveGeometry())
settings.setValue("windowState", self.saveState())
if self.MainApp is not None:
self.MainApp.CloseAllOutputs()
self.ui.toConsole.detach()
QT.QApplication.quit()
[docs] def closeEvent(self, event):
self.Exit()
event.accept()
[docs] def About(self):
box =QT.QMessageBox(self)
box.setWindowTitle("About OpenPisco")
logo = QT.QtGui.QPixmap(ResourcePath()+'K0205_Logo_OpenPISCO_RVB.svg')
box.setIconPixmap (logo.scaledToHeight(150))
box.setText("""<a href="https://openpisco.irt-systemx.fr/" >Web Page</a><br>
<a href="https://openpisco.readthedocs.io/" >Documentation</a><br>
<a href="https://gitlab.com/openpisco/openpisco/" >Sources</a><br>
<a href="https://anaconda.org/OpenPisco/" >Anaconda</a><br>
<a href="https://gitlab.com/openpisco/openpisco/-/blob/master/COPYING" >License</a><br>
""")
box.show()
[docs] def ResetIsoOrDensity(self):
self.ui.ls_slider.setValue(50)
[docs] def SaveCopyToTemp(self,data,ext=""):
filename ="PiscoInputFile.back"+ext
Info("Saving backup of the input data to "+ str(filename) )
try :
WriteTempFile(filename,data)
except:
Info("Error saving backup of input file")
Info(data)
[docs] def OpenFileDialog(self):
import os
dlg = QT.QFileDialog(self,filter="OpenPisco Input Files (*.xml);;OpenPisco Input Files (*.pisco);;OpenPisco Input Files (*.xml *.pisco);;OpenPisco App Files (*.piscoApp)")
dlg.setFileMode(QT.QFileDialog.AnyFile)
# dlg.setFilter()
dlg.setDirectory(os.getcwd())
dlg.setWindowTitle("Open File")
if dlg.exec_() ==QT.QDialog.Accepted :
selectedFiles = dlg.selectedFiles()
if len (selectedFiles) != 1:
return
fname = str(selectedFiles[0])
#qfd.getOpenFileName( caption= 'Open file')
else :
return
del dlg
self.OpenFile( fname)
[docs] def OpenFile(self, fname,callback=None):
self.ResetApp()
#fname =QT.QFileDialog.getOpenFileName(parent= self, caption= 'Open file', directory=os.getcwd(),filter="*.xml")
import os
cleanfilename, file_extension = os.path.splitext(fname)
LV.AddToGlobalDictionary("INPUTFILE",os.path.basename(cleanfilename))
from functools import partial
self.setCurrentFile(fname)
if file_extension == ".piscoApp":
from OpenPisco.QtApp.PiscoAppReader import PiscoAppReader
self.appReader = PiscoAppReader()
try:
string, widget = self.appReader.ReadFromFile(fname,self,self.MainApp)
except Exception as inst:
print("Error opeing file: "+fname)
self.setCurrentFile("")
print(inst)
raise
self.ui.controlPanelAppScrollArea.setWidget(widget)
self.ui.inputFileEditorTextEdit.setPlainText(string)
self.thread.SetFunctionToRunAndRun(partial(self.LoadInputFromString_fuction,string))
def reloadData():
string = str(self.ui.inputFileEditorTextEdit.toPlainText())
self.SaveCopyToTemp(string)
self.ResetMainApp()
self.thread.SetFunctionToRunAndRun(partial(self.LoadInputFromString_fuction,string))
widget.pushEnded.connect(reloadData )
self.controlpanelWidgetCached = widget
return
self.WriteToCommandLog("mainApp.ReadFile('"+fname+"')\n" )
if callback is not None:
self.thread.SetFunctionToRunAndRun(partial(self.OpenFile_function,fname),callback=callback)
else:
self.thread.SetFunctionToRunAndRun(partial(self.OpenFile_function,fname))
[docs] def UpdateApp(self):
self.ui.updateGUI.emit()
self.vtk.update3D.emit()
[docs] def OpenRecentFile(self):
action = self.sender()
if action:
fname = str(action.data())#.toPyObject()
self.OpenFile(fname)
[docs] def setCurrentFile(self, fileName):
if fileName:
self.setWindowTitle("%s - OpenPisco" % QT.QFileInfo(fileName).fileName() )
self.currentFile = fileName
else:
self.setWindowTitle("OpenPisco")
self.currentFile = None
if fileName is None:
return
settings = QT.QSettings()
files = settings.value('recentFileList')#.toPyObject()
try:
if files.count(fileName) :
files.remove(fileName)
files.insert(0, fileName)
del files[SimpleView.MaxRecentFiles:]
except :
files = [fileName]
settings.setValue('recentFileList', files)
settings.sync()
self.ui.UpdateRecentFileActions()
_, file_extension = os.path.splitext(fileName)
print(fileName)
if file_extension != ".piscoApp" and fileName != "":
with open(fileName,"r",errors='ignore') as content:
self.ui.inputFileEditorTextEdit.setPlainText(content.read())
[docs] def ReloadOpenFile(self):
if self.currentFile is not None:
self.OpenFile(self.currentFile)
# this is a threated function no direct GUI calls!!
[docs] def OpenFile_function(self,fname):
if len(fname) > 0 :
self.printInStatusBar.emit('Loading File : ' + fname)
from OpenPisco.QtApp.ProxyWriter import ProxyWriter
self.MainApp.useProxyWriter = partial(ProxyWriter,self)
self.MainApp.closeOutputFilesIfSIGINT = False
self.MainApp.ReadFile(str(fname))
for OA in self.MainApp.optimAlgos:
self.MainApp.optimAlgos[OA].beforeWritingCallBack = self.vtk.update3D.emit
[docs] def RunLoadedFile(self):
self.thread.SetFunctionToRunAndRun (self.RunLoadedFile_function)
self.WriteToCommandLog("mainApp.MainApp.Start()")
[docs] def RunLoadedFile_function(self):
if self.MainApp is not None:
self.MainApp.beforeActionCallBack = self.vtk.update3D.emit
for OA in self.MainApp.optimAlgos:
self.MainApp.optimAlgos[OA].beforeWritingCallBack = self.vtk.update3D.emit
self.MainApp.Start()
#self.MainApp.CloseAllOutputs()
#self.PlotLevelset()
#self.MainApp.beforeActionCallBack = None
#for OA in self.MainApp.optimAlgos:
# self.MainApp.optimAlgos[OA].beforeWritingCallBack = None
[docs] def RunAction_Action(self):
selectedText = str(self.ui.inputFileEditorTextEdit.textCursor().selectedText())
dlg = QT.QInputDialog(self)
dlg.setOption(QT.QInputDialog.InputDialogOption.UsePlainTextEditForTextInput)
dlg.setWindowTitle('Please enter an action (in '+self.MainApp.inputReader.ext+' Format)' )
dlg.setLabelText('Action (in '+self.MainApp.inputReader.ext+' Format) : ' )
dlg.setTextValue(selectedText)
dlg.setOkButtonText("Run Actions")
dlg.resize(1000,300)
ok = dlg.exec_()
text = PH.ApplyGlobalDictionary(str(dlg.textValue()))
if ok and len(text):
Debug(text.encode("utf-8"))
if self.MainApp.inputReader.ext == ".xml":
text = "<Actions>"+text+"</Actions>"
try:
_, dic = self.MainApp.inputReader.ReadFromString(text.encode("utf-8", "ignore").decode() )
except Exception as inst:
print("Problem reading input")
print(inst)
return
try:
self.MainApp.inputReader.ReplaceObjectFromAlmanac(dic,self.MainApp)
except Exception as inst:
print("Problem reading input (interpretation)")
print(inst)
return
self.thread.SetFunctionToRunAndRun(partial(self.RunAction_ActionAndPlot, dic) )
self.WriteToCommandLog("mainApp.RunAction(ElementTree.fromstring('"+str(''.join(text.splitlines()) ).lstrip() +"') )")
[docs] def WriteToCommandLog(self, data):
if self.commandlogfile is not None:
self.commandlogfile.write(data)
self.commandlogfile.write("\n")
self.commandlogfile.flush()
[docs] def RunAction_ActionAndPlot(self,action):
for tag, child in action['children']:
self.MainApp.RunAction(child)
[docs] def runOptim(self,numberofiter):
IDD = self.ui.GetActiveOptimAlgo()
if IDD == -1:
return
if numberofiter:
self.MainApp.optimAlgos[IDD].numberOfDesignStepsMax = int(numberofiter)
action = {'type': 'Repeate', 'times': '1', 'children': [('Action', {'type': 'ResetTopoOp', 'TopoOp': str(IDD)}), ('Action', {'type': 'RunTopoOp', 'TopoOp': str(IDD)})]}
self.WriteToCommandLog("action = "+ str(action) )
self.MainApp.inputReader.ReplaceObjectFromAlmanac(action,self.MainApp)
self.WriteToCommandLog("mainApp.inputReader.ReplaceObjectFromAlmanac(action,self.main)")
self.thread.SetFunctionToRunAndRun(functools.partial(self.MainApp.RunAction, action) )
self.WriteToCommandLog("mainApp.RunAction(action)")
[docs] def updatePhys(self):
IDD = self.ui.GetActiveOptimAlgo()
if IDD == -1:
return
optimalgo = self.MainApp.optimAlgos[IDD]
optimalgo.UpdateOptimProblem()
OF = optimalgo.optimProblem
(newJ, newg, newh) = optimalgo.GetValuesFromOptimProblem()
newC = np.concatenate((newg, newh))
newMerit = optimalgo.ComputeMeritFunction(newJ,newC)
(DJ, Dg, Dh) = optimalgo.GetSensitivitiesFromOptimProblem()
(DJExt,DgExt,DhExt) = optimalgo.GetDirectionsFromGradients(DJ,Dg,Dh)
OF.GetCurrentPoint().extraNodesFields['JDirection'] = DJExt
optimalgo.ComputeGain(newMerit)
optimalgo.PrintCurrentState(OF, newMerit)
self.WriteToCommandLog("OF = self.MainApp.optimAlgos["+str(IDD)+"].optimProblem ")
self.WriteToCommandLog("DJExt = self.MainApp.optimAlgos["+str(IDD)+"].ComputeObjectiveAndGradient()[1] ")
self.WriteToCommandLog("OF.GetCurrentPoint().extraNodesFields['JDirection'] = DJExt ")
self.vtk.update3D.emit()
[docs] def CreateActions(self) :
# icons from https://material.io/icons/
openAction = QT.QAction(QT.QIcon(ResourcePath()+'ic_folder_open_black_24px.svg'), '&Open...', self)
openAction.setShortcut('Ctrl+O')
openAction.setStatusTip('Open a file from disk...')
openAction.triggered.connect(self.OpenFileDialog )
self.actions['open'] = openAction
# icons from https://material.io/icons/
reloadAction = QT.QAction(QT.QIcon(ResourcePath()+'ic_folder_reopen_black_24px.svg'),'&Reload From File', self)
reloadAction.setStatusTip('Reload input file')
reloadAction.triggered.connect(self.ReloadOpenFile)
self.actions['reload'] = reloadAction
# icons from https://material.io/icons/
loadAction = QT.QAction(QT.QIcon(ResourcePath()+'Reload_From_Editor.svg'),'&Reload From Editor', self)
loadAction.setStatusTip('Reload input data from editor')
loadAction.triggered.connect(self.LoadInputData )
self.actions['load'] = loadAction
# icons from https://material.io/icons/
saveAction = QT.QAction(QT.QIcon(ResourcePath()+'baseline-save_alt-24px.svg'), '&Save Input Data', self)
saveAction.setStatusTip('Save input data to file... (Shift+Click resave to the current file)')
saveAction.triggered.connect(self.SaveInputData)
self.actions['save'] = saveAction
runAction = QT.QAction(QT.QIcon(ResourcePath()+'ic_play_circle_filled_black_24px.svg'), '&Run', self)
runAction.setShortcut('Ctrl+R')
runAction.setStatusTip('Run all actions')
runAction.triggered.connect(self.RunLoadedFile)
self.actions['run'] = runAction
for i in range(SimpleView.MaxRecentFiles):
self.recentFileActs.append(QT.QAction(self, visible=False,triggered=self.OpenRecentFile))
updateAction = QT.QAction(QT.QIcon(ResourcePath()+'ic_directions_walk_black_24px.svg'), '&Update Physical Problem', self)
updateAction.setStatusTip('Update criteria of the the optimization problem')
updateAction.triggered.connect(self.updatePhys )
self.actions['update_physical_problems'] = updateAction
updateAction.setEnabled(False)
runAction = QT.QAction(QT.QIcon(ResourcePath()+'ic_replay_1_black_24px.svg'), '&Run Optimization', self)
runAction.setStatusTip('Run again the current optimization problem')
runAction.triggered.connect(functools.partial(self.runOptim,int(0)) )
self.actions['run_optim_again'] = runAction
runAction.setEnabled(False)
for i in [5,10,30,]:
runAction = QT.QAction(QT.QIcon(ResourcePath()+'ic_replay_'+str(i)+'_black_24px.svg'), '&Run '+str(i)+' Optimization Iterations', self)
runAction.setStatusTip('Run '+str(i)+' iterations of current optimization problem')
runAction.triggered.connect(functools.partial(self.runOptim,int(i)) )
self.actions['run_optim_'+str(i) ] = runAction
runAction.setEnabled(False)
ResetIsoOrDensityAction =QT.QAction(QT.QIcon(ResourcePath()+'ic_vertical_align_center_black_24px.svg'), '&Reset Iso/Density View', self)
ResetIsoOrDensityAction.setShortcut('Ctrl+Shift+R')
ResetIsoOrDensityAction.setStatusTip('Reset iso/density view')
ResetIsoOrDensityAction.triggered.connect(self.ResetIsoOrDensity )
self.actions['set 0.0 Ls or 0.5 density'] = ResetIsoOrDensityAction
updateGUIAction =QT.QAction(QT.QIcon(ResourcePath()+'Update_GUI.svg'),'&Update GUI', self)
#exitAction.setShortcut('')
updateGUIAction.setStatusTip('Update GUI from internal objects')
updateGUIAction.triggered.connect(self.ui.updateGUI)
self.actions['updateGUI'] = updateGUIAction
exitAction =QT.QAction(QT.QIcon(ResourcePath()+'ic_exit_to_app_black_24px.svg'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.Exit)
self.actions['exit'] = exitAction
runAction =QT.QAction(QT.QIcon(ResourcePath()+'ic_play_arrow_black_24px.svg'), '&Run Action...', self)
#exitAction.setShortcut('Ctrl+Shift+Q')
runAction.setStatusTip('Run an action in XML format')
runAction.triggered.connect(self.RunAction_Action)
self.actions['run_action'] = runAction
refreshAction =QT.QAction(QT.QIcon(ResourcePath()+'ic_cached_black_24px.svg'), '&Refresh 3D View', self)
refreshAction.setStatusTip('Refresh the 3D view')
refreshAction.triggered.connect(self.vtk.update3D.emit)
self.actions['refresh_action'] = refreshAction
icon = QT.QIcon()
icon.addFile(ResourcePath()+'view_levelset.svg', state =QT.stateOn)
icon.addFile(ResourcePath()+'view_mesh_inside_outside.svg', state = QT.stateOff)
viewLsOrMeshAction =QT.QAction(icon, '&Use Levelset Or Mesh To Cut The Domain', self,checkable=True)
viewLsOrMeshAction.setStatusTip('Use the levelset or the mesh tags to cut the domain')
viewLsOrMeshAction.triggered.connect(self.SetViewLevelset )
self.actions['viewLsOrMesh_action'] = viewLsOrMeshAction
viewZonesAction =QT.QAction(QT.QIcon(ResourcePath()+'baseline-visibility-24px.svg'), '&Draw Zones', self,checkable=True)
viewZonesAction.setStatusTip('Draw a graphical representation of the zones')
viewZonesAction.triggered.connect(self.ShowHideZones )
self.actions['viewZones_action'] = viewZonesAction
viewNextFieldAction =QT.QAction(QT.QIcon(ResourcePath()+'baseline-layers-24px.svg'), '&View Next Field', self)
viewNextFieldAction.setShortcut('a')
viewNextFieldAction.setStatusTip('View next field')
viewNextFieldAction.triggered.connect(self.ViewNextField)
self.actions['nextfield_action'] = viewNextFieldAction
updatedistanceAction =QT.QAction(QT.QIcon(ResourcePath()+'ic_autorenew_black_24px.svg'), '&Update Distance', self)
updatedistanceAction.setStatusTip('Update distance')
updatedistanceAction.triggered.connect(self.UpdateDistance)
self.actions['updatedistance_action'] = updatedistanceAction
remeshAction =QT.QAction('&Remesh ', self)
remeshAction.setStatusTip('Remesh current levelset')
remeshAction.triggered.connect(self.Remesh)
self.actions['remesh_action'] = remeshAction
debug_clean_log_action =QT.QAction(QT.QIcon(ResourcePath()+'ic_clear_black_24px.svg'), '&Clean Log', self)
debug_clean_log_action.setStatusTip('Clean log')
debug_clean_log_action.triggered.connect(self.CleanLogAction)
self.actions['debug_clean_log_action'] = debug_clean_log_action
about_action =QT.QAction('&About OpenPisco', self)
about_action.setStatusTip('About OpenPisco and external links')
about_action.triggered.connect(self.About)
self.actions['about_action'] = about_action
openinparaview_action =QT.QAction(QT.QIcon(ResourcePath()+'To-ParaView.svg') ,'&Open in ParaView', self)
openinparaview_action.setStatusTip('Open current fields in ParaView (Shift+Click only re-exports the file)')
openinparaview_action.triggered.connect(self.OpenInParaView)
self.actions['openinparaview_action'] = openinparaview_action
exportSTL_action =QT.QAction(QT.QIcon(ResourcePath()+'To-STL.svg'), '&Export STL', self)
exportSTL_action.setStatusTip('Save current shape to STL')
exportSTL_action.triggered.connect(self.ExportStl)
self.actions['exportSTL_action'] = exportSTL_action
if self.with_demo :
from OpenPisco.Demos import GetDemoPath
from os import walk
from os import listdir
from os.path import isfile, join, isdir,islink
self.demofiles = []
def AddFilesRecursive(path,menu):
def treatEntries(entries):
for entry in entries:
fullpath = join( path, entry)
if len(entry) > 1 and entry[0:2] == "__":
continue
if isfile(fullpath) and islink(fullpath) == False and any(entry.endswith(x) for x in [".xml", ".pisco", ".piscoApp"] ):
CreateFileToMenu(entry,fullpath,menu)
if isdir(fullpath) and islink(fullpath) == False :
res = []
AddFilesRecursive(fullpath,res)
if len(res):
menu.append((entry,res))
ff = ["NonConform","Conform","Zset" ]
entries = sorted(listdir(path))
fflist = []
for f in ff:
data = list(filter(lambda x: x.find(f) >= 0, entries))
fflist.append(data)
entries = list(filter(lambda x: x.find(f) < 0, entries))
fflist.append(entries)
fflist.reverse()
for f in fflist :
treatEntries(f)
menu.append(None)
def CreateFileToMenu(filename,fullpath,menu):
act = QT.QAction(self, visible=False,triggered=self.OpenRecentFile)
act.setText(filename)
act.setData(fullpath)
act.setVisible(True)
act.setStatusTip("Open demo file: " + str(fullpath) )
menu.append(act)
AddFilesRecursive(GetDemoPath(),self.demofiles)
printLsStats_action = QT.QAction(QT.QIcon(ResourcePath()+'stats.svg'),'&LS Statistics', self)
printLsStats_action.setStatusTip('Print levelset statistics')
printLsStats_action.triggered.connect(self.PrintLsStatsAction)
self.actions['printLsStats'] = printLsStats_action
wireframeview_action = QT.QAction(QT.QIcon(ResourcePath()+'baseline-business-wireframe-24px.svg'),'&Wireframe View', self)
wireframeview_action.setShortcut('w')
wireframeview_action.setStatusTip('Wireframe view')
wireframeview_action.triggered.connect(self.SetWireframeViewAction)
self.actions['wireframeview'] = wireframeview_action
solidview_action = QT.QAction(QT.QIcon(ResourcePath()+'baseline-business-full-24px.svg'),'&Solid View', self)
solidview_action.setShortcut('s')
solidview_action.setStatusTip('Solid view')
solidview_action.triggered.connect(self.SetSolidViewAction)
self.actions['solidview'] = solidview_action
resetcamera3dview_action = QT.QAction(QT.QIcon(ResourcePath()+'zoom_out_map-24px.svg'),'&Reset Camera', self)
resetcamera3dview_action.setShortcut('r')
resetcamera3dview_action.setStatusTip('Reset the camera view in the 3D view')
resetcamera3dview_action.triggered.connect(self.ResetCamera)
self.actions['resetcamera3dview'] = resetcamera3dview_action
addEdit_action =QT.QAction(QT.QIcon(ResourcePath()+'baseline-create-plus-24px.svg'),'&Interactive Dilate Levelset', self,checkable=True)
addEdit_action.setShortcut('+')
addEdit_action.setStatusTip('Edit: Interactive dilatation levelset using the pointer')
addEdit_action.triggered.connect(self.AddEditAction)
self.actions['addedit'] = addEdit_action
substractEdit_action =QT.QAction(QT.QIcon(ResourcePath()+'baseline-create-minus-24px.svg'),'&Interactive Erode Levelset', self,checkable=True)
substractEdit_action.setShortcut('-')
substractEdit_action.setStatusTip('Edit: Interactive erodation levelset using the pointer')
substractEdit_action.triggered.connect(self.SubstractEditAction)
self.actions['substractedit'] = substractEdit_action
moveLSp_action =QT.QAction(QT.QIcon(ResourcePath()+'dilate.svg'),'&Dilate Levelset', self)
moveLSp_action.setStatusTip('Dilate levelset')
moveLSp_action.triggered.connect(partial(self.MoveLevelset,"+"))
self.actions['MoveLS+'] = moveLSp_action
moveLSm_action =QT.QAction(QT.QIcon(ResourcePath()+'erode.svg'),'&Erode Levelset', self)
moveLSm_action.setStatusTip('Erode Levelset')
moveLSm_action.triggered.connect(partial(self.MoveLevelset,"-"))
self.actions['MoveLS-'] = moveLSm_action
moveLSd_action =QT.QAction(QT.QIcon(ResourcePath()+'ls_move_direction.svg'),'&Advance Levelset', self)
moveLSd_action.setStatusTip('Update levelset using the "direction" field ')
moveLSd_action.triggered.connect(partial(self.MoveLevelset,"d"))
self.actions['MoveLSd'] = moveLSd_action
[docs] def SetViewLevelset(self,data):
self.ui.viewLevelset = not data
self.ui.ls_slider.setEnabled(self.ui.viewLevelset)
self.ui.updateGUI.emit()
self.vtk.update3D.emit()
[docs] def MoveLevelset(self,direction):
from OpenPisco.Actions.FieldsActions import LevelSetOffset
IDD,ls = self.GetActiveLevelset()
if IDD == -1 or ls is None:
self.statusBar().showMessage("No levelset to work on ")
return
boundingMin , boundingMax = ls.support.ComputeBoundingBox()
length = np.linalg.norm(boundingMax -boundingMin)/self.erodeDilateScale
self.statusBar().showMessage("Operation " + str(direction))
if direction == "+" or direction == "-":
if direction == "+":
length *= -1
act = LevelSetOffset()
act.Apply(self.MainApp, {"ls":ls, "val":length})
self.WriteToCommandLog("from OpenPisco.Actions.FieldsActions import LevelSetOffset")
self.WriteToCommandLog("act = LevelSetOffset()")
self.WriteToCommandLog("act.Apply(mainApp, {'ls':" + str(IDD) + ",'val':"+str(length)+"} )" )
if direction == "d" and "Direction" in ls.extraNodesFields:
direction = ls.extraNodesFields["Direction"]
ls.TransportAndReinitialize(direction,length)
self.vtk.update3D.emit()
[docs] def ViewNextFieldAction(self):
self.colormapBy = self.colormapBy+1
self.vtk.update3D.emit()
[docs] def PrintLsStatsAction(self):
IDD,ls = self.GetActiveLevelset()
if IDD == -1 or ls is None:
return
from OpenPisco.Actions.FieldsActions import ComputeLevelsetStatistics
ComputeLevelsetStatistics().Apply(self.MainApp, {"ls":ls, "label":IDD})
[docs] def SetWireframeViewAction(self):
self.vtk.SetViewType("w")
self.vtk.update3D.emit()
[docs] def SetSolidViewAction(self):
self.vtk.SetViewType("s")
self.vtk.update3D.emit()
[docs] def ResetCamera(self):
self.vtk.ren.ResetCamera()
[docs] def AddEditAction(self,val):
if val :
self.vtk.style.mod = "add"
self.actions['substractedit'].setChecked(False)
else:
self.vtk.style.mod = "none"
[docs] def SubstractEditAction(self,val):
if val:
self.vtk.style.mod = "minus"
self.actions['addedit'].setChecked(False)
else:
self.vtk.style.mod = "none"
[docs] def OpenInParaView(self):
from Muscat.Actions.OpenInParaView import OpenInParaView
IDD,ls = self.GetActiveLevelset()
if IDD == -1 or ls is None:
OpenInParaView()
return
tempdir = TemporaryDirectory.GetTempPath()
from Muscat.IO.XdmfWriter import WriteMeshToXdmf
filename =tempdir+"PiscoSolution"+str(IDD)+".xmf"
tmpdic = ls.extraNodesFields.copy()
tmpdic['phi'] = ls.phi.flatten()
FieldsNames = list(tmpdic.keys())
Fields = list(tmpdic.values())
if ls.IsNodal():
WriteMeshToXdmf(filename,ls.support,PointFieldsNames=FieldsNames,PointFields=Fields )
else:
from Muscat.IO.XdmfWriter import XdmfWriter
if ls.support.props.get("IsConstantRectilinear",False) :
WriteMeshToXdmf(filename,ls.support,CellFieldsNames=FieldsNames,CellFields=Fields )
else:
from Muscat.Containers.MeshInspectionTools import ExtractElementsByElementFilter
writer = XdmfWriter()
writer.SetBinary(True)
writer.SetMultidomain()
writer.Open(filename=filename)
support = ExtractElementsByElementFilter(ls.support,elementFilter=ElementFilter(dimensionality= ls.support.GetElementsDimensionality() ) )
writer.Write(support,CellFieldsNames=FieldsNames,CellFields=Fields )
support = ExtractElementsByElementFilter(ls.support,elementFilter= ComplementaryFilter(ElementFilter(dimensionality= ls.support.GetElementsDimensionality() ) ) )
if support.GetNumberOfElements() > 0 :
writer.Write(support)
writer.Close()
modifiers =QT.QApplication.keyboardModifiers()
if modifiers == QT.Qt.ShiftModifier:
print('Shift+Click : only saving data to the same file ')
return
OpenInParaView(filename = filename)
[docs] def ExportStl(self):
data = self.vtk.GetPlotedSurfaceData()
if data is None:
return
from Muscat.IO.StlWriter import WriteMeshToStl
from Muscat.Helpers.IO.TemporaryDirectory import TemporaryDirectory
fname = "OutputShape.stl"
fpath = TemporaryDirectory.GetTempPath() +os.sep + fname
WriteMeshToStl(fpath,data)
import shutil
if self.inYarss:
shutil.move(fpath,exportSTLPath+fname)
else:
print('STL file in ' + fpath)
[docs] def UpdateDistance(self):
Debug("Update distance" )
IDD,ls = self.GetActiveLevelset()
if IDD == -1:
return
if self.MainApp is None:
return
Debug(IDD)
self.WriteToCommandLog("from OpenPisco.Actions.FieldsActions import UpdateDistance")
self.WriteToCommandLog("act = UpdateDistance()")
self.WriteToCommandLog("act.Apply(mainApp, {'ls':" + str(IDD) + "} )" )
from OpenPisco.Actions.FieldsActions import UpdateDistance
act = UpdateDistance()
act.Apply(self.MainApp, {"ls":ls})
self.vtk.update3D.emit()
[docs] def Remesh(self):
Debug("Remesh" )
IDD,ls = self.GetActiveLevelset()
if IDD == -1:
return
if self.MainApp is None:
return
Debug(IDD)
self.WriteToCommandLog("from OpenPisco.Actions.UnstructuredActions import ActionRemesh")
self.WriteToCommandLog("act = ActionRemesh()")
self.WriteToCommandLog("act.Apply(mainApp, {'ls':" + str(IDD) + ",'iso':0.0 } )" )
from OpenPisco.Actions.UnstructuredActions import ActionRemesh
act = ActionRemesh()
act.Apply(self.MainApp, {"ls":ls, 'iso': 0.0})
self.vtk.update3D.emit()
f'<Action type="Remesh" ls="{IDD}" iso="0.0" />'
[docs] def ShowHideZones(self):
if self.actions["viewZones_action"].isChecked():
self.vtk.plotZones = True
else:
self.vtk.plotZones = False
self.vtk.update3D.emit()
[docs] def CleanLogAction(self):
self.ui.toConsole.Clean()
[docs] def CleanName(self, dirtyName):
return re.sub(r"(\w)([A-Z])", r"\1 \2", dirtyName).title()
[docs] def AddSphere(self,pos,mod):
IDD,ls = self.GetActiveLevelset()
if IDD == -1:
return
if ls.support.props.get("IsConstantRectilinear",False):
dxyz = ls.support.props.get("spacing")*ls.support.props.get("dimensions")
else:
boundingMin, boundingMax = ls.support.ComputeBoundingBox()
dxyz = (boundingMax - boundingMin)
l = np.linalg.norm(dxyz)/self.interactiveSphereRadius
self.WriteToCommandLog("l = "+str(l))
self.WriteToCommandLog("ls = mainApp.levelSets[" +str(int(IDD))+"]")
self.WriteToCommandLog("l = " + str(l))
self.WriteToCommandLog("pos = " + str(pos))
if ls.IsNodal():
isElementfield = False
else:
isElementfield = True
if mod == "add":
sp = (np.exp(-0.5*((IGObs.ImplicitGeometrySphere(radius=l, center=pos).ApplyVector(ls.support,cellCenter=isElementfield) +l)/l)**2)-0.001)*l
np.clip(sp,0,l,out=sp)
sp *= (-1)**isElementfield
ls.phi[:] = ls.phi - sp
self.WriteToCommandLog("sp = (np.exp(-0.5*((IGObs.ImplicitGeometrySphere(radius=l, center=pos).ApplyVector(ls.support,,cellCenter="+str(isElementfield)+") +l)/l)**2)-0.001)*l")
self.WriteToCommandLog("np.clip(sp,0,l,out=sp)")
self.WriteToCommandLog("ls.phi[:] = ls.phi - sp")
elif mod == "explore" :
self.statusBar().showMessage('Pos : ' + str(pos))
return
elif mod == "minus" :
sp = (np.exp(-0.5*((IGObs.ImplicitGeometrySphere(radius=l, center=pos).ApplyVector(ls.support,cellCenter=isElementfield) +l)/l)**2)-0.001)*l
np.clip(sp,0,l,out=sp)
sp *= (-1)**isElementfield
ls.phi[:] = ls.phi + sp
self.WriteToCommandLog("sp = (np.exp(-0.5*((IGObs.ImplicitGeometrySphere(radius=l, center=pos).ApplyVector(ls.support,cellCenter="+str(isElementfield)+") +l)/l)**2)-0.001)*l")
self.WriteToCommandLog("np.clip(sp,0,l,out=sp)")
self.WriteToCommandLog("ls.phi[:] = ls.phi + sp")
elif mod == "smooth":
sp = (np.exp(-0.5*((IGObs.ImplicitGeometrySphere(radius=l, center=pos).ApplyVector(ls.support,cellCenter=isElementfield) +l)/l)**2)-0.001)*0.1
np.clip(sp,0,0.1,out=sp)
ssp = np.sum(sp)
if ssp != 0 :
m = np.sum(ls.phi*sp)/ssp
ls.phi[:] = m*sp+ls.phi*(1-sp)
if isElementfield:
np.clip(ls.phi,a_min=0.0,a_max=1.0,out=ls.phi)
self.WriteToCommandLog("np.clip(ls.phi,a_min=0.0,a_max=1.0,outls.phi)")
self.vtk.update3D.emit()
[docs]def CheckIntegrity(GUI=False):
if not GUI:
return "OK"
raise Exception("for the moment we dont know how to test a QT app")
window = None
[docs]def LaunchPiscoGUI_Workload():
import os
import sys
from Muscat.Helpers.IO.PathController import PathController
logdir = os.path.expanduser("~")+ os.sep +".openpisco" + os.sep
if not os.path.isdir(logdir):
os.mkdir(logdir)
PathController.SetCurrentDirectory(os.getcwd())
from Muscat.Helpers.Logger import SetUseDifferentialTime
SetUseDifferentialTime(True)
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-l','--loadlastfile',help="Load the last file opened",action="store_true")
parser.add_argument('-r','--run',help="Run loaded file",action="store_true")
parser.add_argument('-v','--verbose',help="Force Verbosity", dest='verbose', action='store_true')
parser.add_argument('-s','--single_window',help="Force app to open only one windows", dest='single_window', action='store_true')
parser.add_argument('-f','--full_screen',help="Force full screen", dest='full_screen', action='store_true')
parser.add_argument('-b','--bashmode',help="Do not launch the GUI, run only in command line (not compatible with other options) ", dest='bashmode',action='store_true')
parser.add_argument("File", help="File to load",nargs="?")
args = parser.parse_args()
if args.bashmode:
MainApp.RunSimulation(args.File)
sys.exit()
# change locale to read correctly stl files
import os
os.environ["LANG"] = "en_GB"
#os.environ["LANG"] = "fr_FR"
app = QT.GetQApplication(sys.argv)
#[u'Windows', u'Motif', u'CDE', u'Plastique', u'GTK+', u'Cleanlooks']
#print(app.style())
#app.setStyle(u"Windows")
#app.setStyle(u"CDE")
#app.setStyle(u"Motif")
#app.setStyle(u"Plastique")
#app.setStyle(u"GTK+")
#app.setStyle(u"Cleanlooks")
app.setOrganizationName("The OpenPisco authors")
app.setOrganizationDomain("")
app.setApplicationName("OpenPisco")
app.setWindowIcon(QT.QtGui.QIcon(ResourcePath()+'K0205_favicon_OpenPISCO_mesh.svg'))
global window
window = SimpleView(with_demo = True)
window.single_window = args.single_window
window.inYarss = PH.ReadBool(os.environ.get("INYARSS","false") )
window.setWindowIcon(QT.QIcon(ResourcePath()+'K0205_favicon_OpenPISCO_mesh.svg'))
window.show()
def receive_signal(signum, stack):
if signum == 10:
window.setFullScreen.emit()
if args.full_screen:
window.showFullScreen()
signal.signal(10,receive_signal)
else:
window.showNormal()
filename = args.File
if args.loadlastfile :
settings = QT.QSettings()
recentFilesList = settings.value('recentFileList')
if recentFilesList is None :
print ("recent files list empty")
exit(1)
files = recentFilesList#.toPyObject()
if len(files) :
filename = str(files[0])
if filename is not None:
from Muscat.Helpers.IO.PathController import PathController
fname = PathController.GetFullFilenameCurrentDirectory(filename)
if args.run :
window.OpenFile(fname,callback=window.RunLoadedFile)
else:
window.OpenFile(fname)
res = app.exec_()
window.thread.exiting = True
return res
[docs]def LaunchPiscoGUI():
"""Muscat uses
"""
from threading import Thread
QtThread = Thread(target=LaunchPiscoGUI_Workload)
QtThread.start()
from time import sleep
while(window is None):
print("Waiting for the main window to open")
sleep(1)
window.thread.WaitForActions()
import sys
sys.exit()
if __name__ == "__main__":
LaunchPiscoGUI()