diff --git a/SkeinPyPy_NewUI/fabmetheus_utilities/geometry/solids/triangle_mesh.py b/SkeinPyPy_NewUI/fabmetheus_utilities/geometry/solids/triangle_mesh.py index e624ee8..1335668 100644 --- a/SkeinPyPy_NewUI/fabmetheus_utilities/geometry/solids/triangle_mesh.py +++ b/SkeinPyPy_NewUI/fabmetheus_utilities/geometry/solids/triangle_mesh.py @@ -480,9 +480,9 @@ def getLoopsFromUnprovenMesh(edges, faces, importRadius, vertexes, z): pointTable = {} return getDescendingAreaLoops(allPoints, corners, importRadius) -def getLoopLayerAppend(loopLayers, z): +def getLoopLayerAppend(loopLayers, layerCount, z): 'Get next z and add extruder loops.' - settings.printProgress(len(loopLayers), 'slice') + settings.printProgressByNumber(len(loopLayers), layerCount, 'slice') loopLayer = euclidean.LoopLayer(z) loopLayers.append(loopLayer) return loopLayer @@ -812,8 +812,9 @@ class TriangleMesh( group.Group ): self.zoneArrangement = ZoneArrangement(self.layerHeight, self.getTransformedVertexes()) layerTop = self.cornerMaximum.z - halfHeight * 0.5 z = self.cornerMinimum.z + halfHeight + layerCount = int((layerTop - z) / self.layerHeight) while z < layerTop: - getLoopLayerAppend(self.loopLayers, z).loops = self.getLoopsFromMesh(self.zoneArrangement.getEmptyZ(z)) + getLoopLayerAppend(self.loopLayers, layerCount, z).loops = self.getLoopsFromMesh(self.zoneArrangement.getEmptyZ(z)) z += self.layerHeight return self.loopLayers diff --git a/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py b/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py index 43c1cf7..311544c 100644 --- a/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py +++ b/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py @@ -8,7 +8,7 @@ from __future__ import absolute_import import __init__ import ConfigParser -import os +import os, sys def getSkeinPyPyConfigInformation(): return { @@ -145,7 +145,7 @@ def getSkeinPyPyConfigInformation(): },'skirt': { 'Activate_Skirt': 'save', 'Convex': 'ignore', - 'Gap_over_Perimeter_Width_ratio': 'save', + 'Gap_Width_mm': 'save', 'Layers_To_index': 'ignore', },'chamber': { 'Activate_Chamber': 'ignore', @@ -384,13 +384,20 @@ def storeRepository(repository): continue if info[name] == "save": + try: + globalConfigParser.add_section(repository.name) + except: + pass globalConfigParser.set(repository.name, name, str(p.value)) return repository def printProgress(layerIndex, procedureName): - print("Progress: ", procedureName, layerIndex) + print ("Progress[" + procedureName + ":" + str(layerIndex) + "]") + sys.stdout.flush() + def printProgressByNumber(layerIndex, numberOfLayers, procedureName): - print("Progress: ", procedureName, layerIndex, numberOfLayers) + print ("Progress[" + procedureName + ":" + str(layerIndex) + ":" + str(numberOfLayers) + "]") + sys.stdout.flush() def getAlterationFileLines(fileName): 'Get the alteration file line and the text lines from the fileName in the alterations directories.' @@ -422,7 +429,7 @@ class StringSetting(GeneralSetting): class BooleanSetting( GeneralSetting ): "A class to display, read & write a boolean." def setValueToString(self, value): - self.value = value == "True" + self.value = str(value) == "True" class LatentStringVar: "This is actually used as 'group' object for Radio buttons. (Did I mention the code is a mess?)" diff --git a/SkeinPyPy_NewUI/newui/mainWindow.py b/SkeinPyPy_NewUI/newui/mainWindow.py index 8f2c2da..ec6f1b6 100644 --- a/SkeinPyPy_NewUI/newui/mainWindow.py +++ b/SkeinPyPy_NewUI/newui/mainWindow.py @@ -1,15 +1,15 @@ from __future__ import absolute_import import __init__ -import skeinpypy - import wx, os -from newui import preview3d from fabmetheus_utilities import archive from fabmetheus_utilities import settings from skeinforge_application.skeinforge_utilities import skeinforge_profile +from newui import preview3d +from newui import sliceProgessPanel + def main(): app = wx.App(False) mainWindow() @@ -22,15 +22,18 @@ class mainWindow(wx.Frame): menubar = wx.MenuBar() fileMenu = wx.Menu() fitem = fileMenu.Append(-1, 'Open Profile...', 'Open Profile...') + self.Bind(wx.EVT_MENU, self.OnLoadProfile, fitem) fitem = fileMenu.Append(-1, 'Save Profile...', 'Save Profile...') self.Bind(wx.EVT_MENU, self.OnSaveProfile, fitem) fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quit application') self.Bind(wx.EVT_MENU, self.OnQuit, fitem) menubar.Append(fileMenu, '&File') - menubar.Append(wx.Menu(), 'Expert') + #menubar.Append(wx.Menu(), 'Expert') self.SetMenuBar(menubar) + self.lastPath = "" self.filename = None + self.progressPanelList = [] self.controlList = [] self.plugins = {} for m in skeinforge_profile.getCraftTypePluginModule().getCraftSequence(): @@ -49,7 +52,7 @@ class mainWindow(wx.Frame): nb = wx.Notebook(p, size=(500,10)) configPanel = wx.Panel(nb); - nb.AddPage(configPanel, "Print") + nb.AddPage(configPanel, "Print config") sizer = wx.GridBagSizer(2, 2) configPanel.SetSizer(sizer) @@ -57,7 +60,7 @@ class mainWindow(wx.Frame): self.AddSetting(configPanel, "Layer height (mm)", self.plugins['carve'].preferencesDict['Layer_Height_mm']) self.AddTitle(configPanel, "Skirt") self.AddSetting(configPanel, "Enable skirt", self.plugins['skirt'].preferencesDict['Activate_Skirt']) - self.AddSetting(configPanel, "Skirt distance (mm)", self.plugins['skirt'].preferencesDict['Gap_over_Perimeter_Width_ratio']) + self.AddSetting(configPanel, "Skirt distance (mm)", self.plugins['skirt'].preferencesDict['Gap_Width_mm']) self.AddTitle(configPanel, "Fill") self.AddSetting(configPanel, "Solid layers", self.plugins['fill'].preferencesDict['Solid_Surface_Thickness_layers']) self.AddSetting(configPanel, "Fill Density", self.plugins['fill'].preferencesDict['Infill_Solidity_ratio']) @@ -68,7 +71,7 @@ class mainWindow(wx.Frame): self.AddSetting(configPanel, "Minimal travel (mm)", self.plugins['dimension'].preferencesDict['Minimum_Travel_for_Retraction_millimeters']) configPanel = wx.Panel(nb); - nb.AddPage(configPanel, "Machine") + nb.AddPage(configPanel, "Machine config") sizer = wx.GridBagSizer(2, 2) configPanel.SetSizer(sizer) @@ -108,20 +111,25 @@ class mainWindow(wx.Frame): sizer.AddGrowableCol(2) sizer.AddGrowableRow(0) p.SetSizer(sizer) - + + self.panel = p + self.sizer = sizer + self.SetSize((800, 400)) self.Centre() self.Show(True) def AddTitle(self, panel, name): + "Add a title row to the configuration panel" sizer = panel.GetSizer() title = wx.StaticText(panel, -1, name) title.SetFont(wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.NORMAL, wx.FONTWEIGHT_BOLD)) - sizer.Add(title, (sizer.GetRows(),1), (1,2), flag=wx.EXPAND) - sizer.Add(wx.StaticLine(panel), (sizer.GetRows()+1,1), (1,2), flag=wx.EXPAND) + sizer.Add(title, (sizer.GetRows(),1), (1,3), flag=wx.EXPAND) + sizer.Add(wx.StaticLine(panel), (sizer.GetRows()+1,1), (1,3), flag=wx.EXPAND) sizer.SetRows(sizer.GetRows() + 2) - def AddSetting(self, panel, name, setting): + def AddSetting(self, panel, name, setting, help = False): + "Add a setting to the configuration panel" sizer = panel.GetSizer() sizer.Add(wx.StaticText(panel, -1, name), (sizer.GetRows(),1), flag=wx.ALIGN_CENTER_VERTICAL) ctrl = None @@ -138,25 +146,41 @@ class mainWindow(wx.Frame): ctrl.setting = setting self.controlList.append(ctrl) sizer.Add(ctrl, (sizer.GetRows(),2), flag=wx.ALIGN_BOTTOM|wx.EXPAND) + helpButton = wx.Button(panel, -1, "?", style=wx.BU_EXACTFIT) + sizer.Add(helpButton, (sizer.GetRows(),3)) sizer.SetRows(sizer.GetRows()+1) return ctrl - - def OnSaveProfile(self, e): - dlg=wx.FileDialog(self, "Select profile file to save", style=wx.FD_SAVE) + + def OnLoadProfile(self, e): + dlg=wx.FileDialog(self, "Select profile file to load", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg.SetWildcard("ini files (*.ini)|*.ini") if dlg.ShowModal() == wx.ID_OK: profileFile = dlg.GetPath() - self.updateConfig() + self.lastPath = os.path.split(profileFile)[0] + settings.loadGlobalConfig(profileFile) + self.updateConfigToControls() + dlg.Destroy() + + def OnSaveProfile(self, e): + dlg=wx.FileDialog(self, "Select profile file to save", self.lastPath, style=wx.FD_SAVE) + dlg.SetWildcard("ini files (*.ini)|*.ini") + if dlg.ShowModal() == wx.ID_OK: + profileFile = dlg.GetPath() + self.lastPath = os.path.split(profileFile)[0] settings.saveGlobalConfig(profileFile) + self.updateConfigFromControls() + dlg.Destroy() def OnLoadSTL(self, e): - dlg=wx.FileDialog(self, "Open file to print", style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) - dlg.SetWildcard("OBJ, STL files (*.stl;*.STL;*.obj;*.OBJ;)") + dlg=wx.FileDialog(self, "Open file to print", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) + dlg.SetWildcard("OBJ, STL files (*.stl;*.obj)|*.stl;*.obj") if dlg.ShowModal() == wx.ID_OK: self.filename=dlg.GetPath() if not(os.path.exists(self.filename)): return + self.lastPath = os.path.split(self.filename)[0] self.preview3d.loadFile(self.filename) + dlg.Destroy() def OnSlice(self, e): if self.filename == None: @@ -164,9 +188,41 @@ class mainWindow(wx.Frame): for pluginName in self.plugins.keys(): settings.storeRepository(self.plugins[pluginName]) settings.saveGlobalConfig(settings.getDefaultConfigPath()) - skeinpypy.runSkein([self.filename]) + #Create a progress panel and add it to the window. The progress panel will start the Skein operation. + spp = sliceProgessPanel.sliceProgessPanel(self, self.panel, self.filename) + self.sizer.Add(spp, (len(self.progressPanelList)+2,0), span=(1,4), flag=wx.EXPAND) + self.sizer.Layout() + newSize = self.GetSize(); + newSize.IncBy(0, spp.GetSize().GetHeight()) + self.SetSize(newSize) + self.progressPanelList.append(spp) + + def removeSliceProgress(self, spp): + self.progressPanelList.remove(spp) + newSize = self.GetSize(); + newSize.IncBy(0, -spp.GetSize().GetHeight()) + self.SetSize(newSize) + spp.Destroy() + for spp in self.progressPanelList: + self.sizer.Remove(spp) + i = 2 + for spp in self.progressPanelList: + self.sizer.Add(spp, (i,0), span=(1,4), flag=wx.EXPAND) + i += 1 - def updateConfig(self): + def updateConfigToControls(self): + "Update the configuration wx controls to show the new configuration settings" + for pluginName in self.plugins.keys(): + settings.getReadRepository(self.plugins[pluginName]) + settings.saveGlobalConfig(settings.getDefaultConfigPath()) + for ctrl in self.controlList: + if ctrl.setting.__class__ is settings.BooleanSetting: + ctrl.SetValue(ctrl.setting.value) + else: + ctrl.SetValue(str(ctrl.setting.value)) + + def updateConfigFromControls(self): + "Update the configuration settings with values from the wx controls" for ctrl in self.controlList: ctrl.setting.setValueToString(ctrl.GetValue()) for pluginName in self.plugins.keys(): diff --git a/SkeinPyPy_NewUI/newui/skeinRun.py b/SkeinPyPy_NewUI/newui/skeinRun.py new file mode 100644 index 0000000..08be13c --- /dev/null +++ b/SkeinPyPy_NewUI/newui/skeinRun.py @@ -0,0 +1,48 @@ +from __future__ import absolute_import + +import platform, os, subprocess, sys + +from skeinforge_application.skeinforge_utilities import skeinforge_craft + +def getPyPyExe(): + "Return the path to the pypy executable if we can find it. Else return False" + if platform.system() == "Windows": + pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy/pypy.exe")); + else: + pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy/bin/pypy")); + if os.path.exists(pypyExe): + return pypyExe + pypyExe = "/bin/pypy"; + if os.path.exists(pypyExe): + return pypyExe + pypyExe = "/usr/bin/pypy"; + if os.path.exists(pypyExe): + return pypyExe + pypyExe = "/usr/local/bin/pypy"; + if os.path.exists(pypyExe): + return pypyExe + return False + +def runSkein(fileNames): + "Run the slicer on the files. If we are running with PyPy then just do the slicing action. If we are running as Python, try to find pypy." + pypyExe = getPyPyExe() + for fileName in fileNames: + if platform.python_implementation() == "PyPy": + skeinforge_craft.writeOutput(fileName) + elif pypyExe == False: + print "************************************************" + print "* Failed to find pypy, so slicing with python! *" + print "************************************************" + skeinforge_craft.writeOutput(fileName) + print "************************************************" + print "* Failed to find pypy, so sliced with python! *" + print "************************************************" + else: + subprocess.call([pypyExe, os.path.join(sys.path[0], sys.argv[0]), fileName]) + +def getSkeinCommand(filename): + pypyExe = getPyPyExe() + if pypyExe == False: + pypyExe = sys.executable + return [pypyExe, os.path.join(sys.path[0], sys.argv[0]), filename] + diff --git a/SkeinPyPy_NewUI/newui/sliceProgessPanel.py b/SkeinPyPy_NewUI/newui/sliceProgessPanel.py new file mode 100644 index 0000000..fe90cfe --- /dev/null +++ b/SkeinPyPy_NewUI/newui/sliceProgessPanel.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import +import __init__ + +import wx,sys,math,threading,subprocess +from newui import skeinRun + +class sliceProgessPanel(wx.Panel): + def __init__(self, mainWindow, parent, filename): + wx.Panel.__init__(self, parent, -1) + self.mainWindow = mainWindow + self.filename = filename + self.abort = False + + box = wx.StaticBox(self, -1, filename) + sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer.Add(sizer, 0, flag=wx.EXPAND) + + self.statusText = wx.StaticText(self, -1, "Starting...") + self.progressGauge = wx.Gauge(self, -1) + self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT) + sizer.Add(self.statusText, 2, flag=wx.ALIGN_CENTER ) + sizer.Add(self.progressGauge, 2) + sizer.Add(self.abortButton, 0) + + self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton) + + self.SetSizer(mainSizer) + self.thread = WorkerThread(self, filename) + + def OnAbort(self, e): + if self.abort: + self.mainWindow.removeSliceProgress(self) + else: + self.abort = True + +class WorkerThread(threading.Thread): + def __init__(self, notifyWindow, filename): + threading.Thread.__init__(self) + self.filename = filename + self.notifyWindow = notifyWindow + self.start() + + def run(self): + p = subprocess.Popen(skeinRun.getSkeinCommand(self.filename), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + line = p.stdout.readline() + maxValue = 1 + while(len(line) > 0): + line = line.rstrip() + if line[0:9] == "Progress[" and line[-1:] == "]": + progress = line[9:-1].split(":") + if len(progress) > 2: + maxValue = int(progress[2]) + wx.CallAfter(self.notifyWindow.progressGauge.SetRange, maxValue) + wx.CallAfter(self.notifyWindow.statusText.SetLabel, progress[0] + " [" + progress[1] + "/" + str(maxValue) + "]") + wx.CallAfter(self.notifyWindow.progressGauge.SetValue, int(progress[1])) + else: + wx.CallAfter(self.notifyWindow.statusText.SetLabel, line) + if self.notifyWindow.abort: + p.terminate() + wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Aborted by user.") + return + line = p.stdout.readline() + wx.CallAfter(self.notifyWindow.progressGauge.SetValue, maxValue) + wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Ready.") + diff --git a/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/export.py b/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/export.py index d601612..08eb666 100644 --- a/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/export.py +++ b/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/export.py @@ -328,9 +328,9 @@ class ExportRepository: self.alsoSendOutputTo = settings.StringSetting().getFromValue('Also Send Output To:', self, '') self.analyzeGcode = settings.BooleanSetting().getFromValue('Analyze Gcode', self, True) self.commentChoice = settings.MenuButtonDisplay().getFromName('Comment Choice:', self) - self.doNotDeleteComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Do Not Delete Comments', self, False) + self.doNotDeleteComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Do Not Delete Comments', self, True) self.deleteCraftingComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Delete Crafting Comments', self, False) - self.deleteAllComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Delete All Comments', self, True) + self.deleteAllComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Delete All Comments', self, False) exportPluginsFolderPath = archive.getAbsoluteFrozenFolderPath(archive.getCraftPluginsDirectoryPath('export.py'), 'export_plugins') exportStaticDirectoryPath = os.path.join(exportPluginsFolderPath, 'static_plugins') exportPluginFileNames = archive.getPluginFileNamesFromDirectoryPath(exportPluginsFolderPath) diff --git a/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py b/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py index 21bc7d9..d2c7ff7 100644 --- a/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py +++ b/SkeinPyPy_NewUI/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py @@ -131,9 +131,9 @@ class SkirtRepository: self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Skirt', self, '') self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skirt') - self.activateSkirt = settings.BooleanSetting().getFromValue('Activate Skirt', self, False) + self.activateSkirt = settings.BooleanSetting().getFromValue('Activate Skirt', self, True) self.convex = settings.BooleanSetting().getFromValue('Convex:', self, True) - self.gapOverEdgeWidth = settings.FloatSpin().getFromValue(1.0, 'Gap over Perimeter Width (ratio):', self, 5.0, 3.0) + self.gapWidth = settings.FloatSpin().getFromValue(1.0, 'Gap Width (mm):', self, 5.0, 3.0) self.layersTo = settings.IntSpin().getSingleIncrementFromValue(0, 'Layers To (index):', self, 912345678, 1) self.executeTitle = 'Skirt' @@ -270,7 +270,7 @@ class SkirtSkein: self.skirtFlowRate = self.oldFlowRate elif firstWord == '(': self.edgeWidth = float(splitLine[1]) - self.skirtOutset = (self.repository.gapOverEdgeWidth.value + 0.5) * self.edgeWidth + self.skirtOutset = self.repository.gapWidth.value + 0.5 * self.edgeWidth self.distanceFeedRate.addTagRoundedLine('skirtOutset', self.skirtOutset) elif firstWord == '(': self.travelFeedRateMinute = 60.0 * float(splitLine[1]) diff --git a/SkeinPyPy_NewUI/skeinpypy.py b/SkeinPyPy_NewUI/skeinpypy.py index dabf9ca..af451d7 100644 --- a/SkeinPyPy_NewUI/skeinpypy.py +++ b/SkeinPyPy_NewUI/skeinpypy.py @@ -12,16 +12,10 @@ The slicing code is the same as Skeinforge. But the UI has been revamped to be.. from __future__ import absolute_import from optparse import OptionParser -from skeinforge_application.skeinforge_utilities import skeinforge_craft -#For some reason the newui import fails when we are importing this from newui.mainWindow (circle references not allowed?) in that case we don't need the UI so skip it. -try: - from newui import mainWindow -except: - pass -import os +from newui import mainWindow +from newui import skeinRun + import sys -import platform -import subprocess __author__ = 'Daid' __credits__ = """ @@ -47,48 +41,12 @@ Art of Illusion """ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' -def getPyPyExe(): - "Return the path to the pypy executable if we can find it. Else return False" - if platform.system() == "Windows": - pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy/pypy.exe")); - else: - pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy/bin/pypy")); - if os.path.exists(pypyExe): - return pypyExe - pypyExe = "/bin/pypy"; - if os.path.exists(pypyExe): - return pypyExe - pypyExe = "/usr/bin/pypy"; - if os.path.exists(pypyExe): - return pypyExe - pypyExe = "/usr/local/bin/pypy"; - if os.path.exists(pypyExe): - return pypyExe - return False - -def runSkein(fileNames): - "Run the slicer on the files. If we are running with PyPy then just do the slicing action. If we are running as Python, try to find pypy." - pypyExe = getPyPyExe() - for fileName in fileNames: - if platform.python_implementation() == "PyPy": - skeinforge_craft.writeOutput(fileName) - elif pypyExe == False: - print "************************************************" - print "* Failed to find pypy, so slicing with python! *" - print "************************************************" - skeinforge_craft.writeOutput(fileName) - print "************************************************" - print "* Failed to find pypy, so sliced with python! *" - print "************************************************" - else: - subprocess.call([pypyExe, __file__, fileName]) - def main(): parser = OptionParser() (options, args) = parser.parse_args() sys.argv = [sys.argv[0]] + args if len( args ) > 0: - runSkein(args) + skeinRun.runSkein(args) else: mainWindow.main()