From 68944bb2213df9b5babbe23c93c2114fbf85a578 Mon Sep 17 00:00:00 2001 From: Daid Date: Mon, 20 Feb 2012 23:27:34 +0100 Subject: [PATCH] Large configuration update, the new UI now starts to work a bit. Still missing a bunch of configuration settings. And the start/end code is not editable yet, and never included. --- .../fabmetheus_utilities/settings.py | 47 ++++-- SkeinPyPy_NewUI/newui/mainWindow.py | 151 ++++++++++++------ SkeinPyPy_NewUI/newui/preview3d.py | 54 +++++-- SkeinPyPy_NewUI/skeinpypy.py | 6 +- 4 files changed, 184 insertions(+), 74 deletions(-) diff --git a/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py b/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py index 0f24b3e..43c1cf7 100644 --- a/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py +++ b/SkeinPyPy_NewUI/fabmetheus_utilities/settings.py @@ -14,7 +14,7 @@ def getSkeinPyPyConfigInformation(): return { 'carve': { 'Add_Layer_Template_to_SVG': 'ignore', - 'Edge_Width_mm': 'ignore', + 'Edge_Width_mm': 'save', 'Extra_Decimal_Places_float': 'ignore', 'Import_Coarseness_ratio': 'ignore', 'Layer_Height_mm': 'save', @@ -71,7 +71,7 @@ def getSkeinPyPyConfigInformation(): 'Line': 'ignore', 'Infill_Perimeter_Overlap_ratio': 'save', 'Infill_Solidity_ratio': 'save', - 'Infill_Width': 'save', + 'Infill_Width': 'use:carve:Edge_Width_mm', 'Solid_Surface_Thickness_layers': 'save', 'Start_From_Choice': 'ignore', 'Surrounding_Angle_degrees': 'ignore', @@ -92,12 +92,12 @@ def getSkeinPyPyConfigInformation(): 'Duty_Cyle_at_Beginning_portion': 'ignore', 'Duty_Cyle_at_Ending_portion': 'ignore', 'Feed_Rate_mm/s': 'save', - 'Flow_Rate_Setting_float': 'use:Feed_Rate_mm/s', + 'Flow_Rate_Setting_float': 'use:speed:Feed_Rate_mm/s', 'Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio': 'save', - 'Object_First_Layer_Feed_Rate_Perimeter_Multiplier_ratio': 'use:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', - 'Object_First_Layer_Feed_Rate_Travel_Multiplier_ratio': 'use:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', - 'Object_First_Layer_Flow_Rate_Infill_Multiplier_ratio': 'use:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', - 'Object_First_Layer_Flow_Rate_Perimeter_Multiplier_ratio': 'use:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', + 'Object_First_Layer_Feed_Rate_Perimeter_Multiplier_ratio': 'use:speed:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', + 'Object_First_Layer_Feed_Rate_Travel_Multiplier_ratio': 'use:speed:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', + 'Object_First_Layer_Flow_Rate_Infill_Multiplier_ratio': 'use:speed:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', + 'Object_First_Layer_Flow_Rate_Perimeter_Multiplier_ratio': 'use:speed:Object_First_Layer_Feed_Rate_Infill_Multiplier_ratio', 'Object_First_Layers_Amount_Of_Layers_For_Speed_Change': 'save', 'Orbital_Feed_Rate_over_Operating_Feed_Rate_ratio': 'ignore', 'Maximum_Z_Feed_Rate_mm/s': 'save', @@ -345,7 +345,8 @@ def getReadRepository(repository): continue #Load this setting from another value. if info[name][0:4] == "use:": - p.setValueToString(globalConfigParser.get(repository.name, info[name][4:])) + i = info[name][4:].split(':') + p.setValueToString(globalConfigParser.get(i[0], i[1])) continue try: @@ -357,10 +358,35 @@ def getReadRepository(repository): except: pass globalConfigParser.set(repository.name, name, str(p.value)) - saveGlobalConfig(getDefaultConfigPath()) + #saveGlobalConfig(getDefaultConfigPath()) #print "============" + str(p) + "|" + p.name + "|" + str(p.value) + "|" + str(type(p.value)) return repository +def storeRepository(repository): + "Store the configuration for this 'repository'" + #Check if we have a configuration file loaded, else load the default. + if not globals().has_key('globalConfigParser'): + loadGlobalConfig(getDefaultConfigPath()) + + info = getSkeinPyPyConfigInformation() + if not info.has_key(repository.name): + print "Warning: Plugin: " + repository.name + " missing from SkeinPyPy info" + return repository + info = info[repository.name] + if not type(info) is dict: + print "Ignoring plugin configuration: " + repository.name + return repository + + for p in repository.preferences: + name = safeConfigName(p.name) + if not info.has_key(name): + print "Setting: " + repository.name + ":" + name + " missing from SkeinPyPy info" + continue + + if info[name] == "save": + globalConfigParser.set(repository.name, name, str(p.value)) + return repository + def printProgress(layerIndex, procedureName): print("Progress: ", procedureName, layerIndex) def printProgressByNumber(layerIndex, numberOfLayers, procedureName): @@ -491,7 +517,8 @@ class FloatSpin( FloatSetting ): "Initialize." self.name = name self.value = value - repository.preferences.append(self) + if repository != None: + repository.preferences.append(self) return self class LabelSeparator: diff --git a/SkeinPyPy_NewUI/newui/mainWindow.py b/SkeinPyPy_NewUI/newui/mainWindow.py index fdf9617..3d4c594 100644 --- a/SkeinPyPy_NewUI/newui/mainWindow.py +++ b/SkeinPyPy_NewUI/newui/mainWindow.py @@ -1,6 +1,8 @@ from __future__ import absolute_import import __init__ +import skeinpypy + import wx, os from newui import preview3d @@ -21,62 +23,86 @@ class mainWindow(wx.Frame): fileMenu = wx.Menu() fitem = fileMenu.Append(-1, 'Open Profile...', 'Open Profile...') 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') self.SetMenuBar(menubar) - plugins = {} + self.filename = None + self.controlList = [] + self.plugins = {} for m in skeinforge_profile.getCraftTypePluginModule().getCraftSequence(): - plugins[m] = archive.getModuleWithDirectoryPath(archive.getCraftPluginsDirectoryPath(), m).getNewRepository() - settings.getReadRepository(plugins[m]) - - p = wx.Panel(self) - nb = wx.Notebook(p, size=(500,10)) - - printConfig = wx.Panel(nb); - - sizer = wx.GridBagSizer(2, 2) - printConfig.SetSizer(sizer) + self.plugins[m] = archive.getModuleWithDirectoryPath(archive.getCraftPluginsDirectoryPath(), m).getNewRepository() + settings.getReadRepository(self.plugins[m]) skeinPyPySettingInfo = settings.getSkeinPyPyConfigInformation() - for pluginName in plugins.keys(): - box, configPanel = self.CreateGroup(printConfig, pluginName) - - for pref in plugins[pluginName].preferences: + for pluginName in self.plugins.keys(): + self.plugins[pluginName].preferencesDict = {} + for pref in self.plugins[pluginName].preferences: if skeinPyPySettingInfo[pluginName][settings.safeConfigName(pref.name)] == 'save': - self.AddSetting(configPanel, pref.name, wx.TextCtrl(configPanel, -1, str(pref.value))) + self.plugins[pluginName].preferencesDict[settings.safeConfigName(pref.name)] = pref - if configPanel.GetSizer().GetRows() > 0: - sizer.Add(box, (sizer.GetRows(),0)) - sizer.SetRows(sizer.GetRows()+1) + p = wx.Panel(self) + nb = wx.Notebook(p, size=(500,10)) - #self.AddSetting(generalConfig, "Speed (mm/s)", wx.TextCtrl(generalConfig, -1, "50.0")) - - machineConfig = wx.Panel(nb); + configPanel = wx.Panel(nb); + nb.AddPage(configPanel, "Print") sizer = wx.GridBagSizer(2, 2) - machineConfig.SetSizer(sizer) - box, dimensionsConfig = self.CreateGroup(machineConfig, "Dimensions") - self.AddSetting(dimensionsConfig, "Printer size (mm)", wx.TextCtrl(dimensionsConfig, -1, "205,205,200")) - sizer.Add(box, (0,0)) + configPanel.SetSizer(sizer) + + self.AddTitle(configPanel, "Accuracy") + 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.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']) + self.AddTitle(configPanel, "Retraction") + self.AddSetting(configPanel, "Speed (mm/s)", self.plugins['dimension'].preferencesDict['Extruder_Retraction_Speed_mm/s']) + self.AddSetting(configPanel, "Distance (mm)", self.plugins['dimension'].preferencesDict['Retraction_Distance_millimeters']) + self.AddSetting(configPanel, "Extra length on start (mm)", self.plugins['dimension'].preferencesDict['Restart_Extra_Distance_millimeters']) + + configPanel = wx.Panel(nb); + nb.AddPage(configPanel, "Machine") + sizer = wx.GridBagSizer(2, 2) + configPanel.SetSizer(sizer) + + self.AddTitle(configPanel, "Machine size") + self.AddSetting(configPanel, "Width (mm)", settings.IntSpin().getFromValue(10, "machine_width", None, 1000, 205)) + self.AddSetting(configPanel, "Depth (mm)", settings.IntSpin().getFromValue(10, "machine_depth", None, 1000, 205)) + self.AddSetting(configPanel, "Height (mm)", settings.IntSpin().getFromValue(10, "machine_height", None, 1000, 200)) + + self.AddTitle(configPanel, "Machine nozzle") + self.AddSetting(configPanel, "Nozzle size (mm)", self.plugins['carve'].preferencesDict['Edge_Width_mm']) + + self.AddTitle(configPanel, "Speed") + self.AddSetting(configPanel, "Print speed (mm/s)", self.plugins['speed'].preferencesDict['Feed_Rate_mm/s']) + self.AddSetting(configPanel, "Travel speed (mm/s)", self.plugins['speed'].preferencesDict['Travel_Feed_Rate_mm/s']) + + self.AddTitle(configPanel, "Filament") + self.AddSetting(configPanel, "Diameter (mm)", self.plugins['dimension'].preferencesDict['Filament_Diameter_mm']) + self.AddSetting(configPanel, "Packing Density", self.plugins['dimension'].preferencesDict['Filament_Packing_Density_ratio']) - nb.AddPage(printConfig, "Print") - nb.AddPage(machineConfig, "Machine") nb.AddPage(wx.Panel(nb), "Start/End-GCode") #Preview window, load and slice buttons. self.preview3d = preview3d.myGLCanvas(p) - loadButton = wx.Button(p, 1, 'Load STL') + loadButton = wx.Button(p, -1, 'Load STL') + sliceButton = wx.Button(p, -1, 'Slice to GCode') self.Bind(wx.EVT_BUTTON, self.OnLoadSTL, loadButton) + self.Bind(wx.EVT_BUTTON, self.OnSlice, sliceButton) sizer = wx.GridBagSizer() sizer.Add(nb, (0,0), span=(2,1), flag=wx.EXPAND) - sizer.Add(self.preview3d, (0,1), span=(1,1), flag=wx.EXPAND) + sizer.Add(self.preview3d, (0,1), span=(1,3), flag=wx.EXPAND) sizer.Add(loadButton, (1,1)) - sizer.AddGrowableCol(1) + sizer.Add(sliceButton, (1,2)) + sizer.AddGrowableCol(2) sizer.AddGrowableRow(0) p.SetSizer(sizer) @@ -84,32 +110,65 @@ class mainWindow(wx.Frame): self.Centre() self.Show(True) - def CreateGroup(self, panel, name): - retPanel = wx.Panel(panel) - sizer = wx.GridBagSizer(2, 2) - retPanel.SetSizer(sizer) - - box = wx.StaticBox(panel, -1, name) - sizer = wx.StaticBoxSizer(box, wx.VERTICAL) - sizer.Add(retPanel) - - return (sizer, retPanel) - - def AddSetting(self, panel, name, ctrl): + def AddTitle(self, panel, name): sizer = panel.GetSizer() - sizer.Add(wx.StaticText(panel, -1, name), (sizer.GetRows(),0), flag=wx.ALIGN_BOTTOM) - sizer.Add(ctrl, (sizer.GetRows(),1), flag=wx.ALIGN_BOTTOM|wx.EXPAND) + 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.SetRows(sizer.GetRows() + 2) + + def AddSetting(self, panel, name, setting): + sizer = panel.GetSizer() + sizer.Add(wx.StaticText(panel, -1, name), (sizer.GetRows(),1), flag=wx.ALIGN_CENTER_VERTICAL) + ctrl = None + if setting.__class__ is settings.FloatSpin: + ctrl = wx.TextCtrl(panel, -1, str(setting.value)) + if setting.__class__ is settings.IntSpin: + ctrl = wx.TextCtrl(panel, -1, str(setting.value)) + if setting.__class__ is settings.BooleanSetting: + ctrl = wx.CheckBox(panel, -1, '') + ctrl.SetValue(setting.value) + if ctrl == None: + print "No WX control for: " + str(setting), str(setting.__class__) + else: + ctrl.setting = setting + self.controlList.append(ctrl) + sizer.Add(ctrl, (sizer.GetRows(),2), flag=wx.ALIGN_BOTTOM|wx.EXPAND) sizer.SetRows(sizer.GetRows()+1) + def OnSaveProfile(self, e): + dlg=wx.FileDialog(self, "Select profile file to save", style=wx.FD_SAVE) + dlg.SetWildcard("ini files (*.ini)|*.ini") + if dlg.ShowModal() == wx.ID_OK: + profileFile = dlg.GetPath() + self.updateConfig() + settings.saveGlobalConfig(profileFile) + 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.SetWildcard("OBJ, STL files (*.stl;*.STL;*.obj;*.OBJ;)") if dlg.ShowModal() == wx.ID_OK: self.filename=dlg.GetPath() if not(os.path.exists(self.filename)): return self.preview3d.loadFile(self.filename) + def OnSlice(self, e): + if self.filename == None: + return + for pluginName in self.plugins.keys(): + settings.storeRepository(self.plugins[pluginName]) + settings.saveGlobalConfig(settings.getDefaultConfigPath()) + skeinpypy.runSkein([self.filename]) + + def updateConfig(self): + for ctrl in self.controlList: + ctrl.setting.setValueToString(ctrl.GetValue()) + for pluginName in self.plugins.keys(): + settings.storeRepository(self.plugins[pluginName]) + settings.saveGlobalConfig(settings.getDefaultConfigPath()) + def OnQuit(self, e): self.Close() diff --git a/SkeinPyPy_NewUI/newui/preview3d.py b/SkeinPyPy_NewUI/newui/preview3d.py index 0b86e11..7a83a47 100644 --- a/SkeinPyPy_NewUI/newui/preview3d.py +++ b/SkeinPyPy_NewUI/newui/preview3d.py @@ -1,6 +1,6 @@ #from wxPython.glcanvas import wxGLCanvas import wx -import sys,math +import sys,math,threading from wx.glcanvas import GLCanvas try: @@ -24,6 +24,7 @@ class myGLCanvas(GLCanvas): wx.EVT_MOTION(self, self.OnMouseMotion) self.init = 0 self.triangleMesh = None + self.modelDisplayList = None self.yaw = 30 self.pitch = 60 self.zoom = 150 @@ -31,8 +32,17 @@ class myGLCanvas(GLCanvas): self.machineCenter = Vector3(100, 100, 0) def loadFile(self, filename): - self.triangleMesh = fabmetheus_interpret.getCarving(filename) + self.filename = filename + #Do the STL file loading in a background thread so we don't block the UI. + thread = threading.Thread(target=self.DoLoad) + thread.setDaemon(True) + thread.start() + + def DoLoad(self): + self.modelDirty = False + self.triangleMesh = fabmetheus_interpret.getCarving(self.filename) self.moveModel() + self.Refresh() def moveModel(self): if self.triangleMesh == None: @@ -46,6 +56,8 @@ class myGLCanvas(GLCanvas): v.y -= min.y + (max.y - min.y) / 2 v.x += self.machineCenter.x v.y += self.machineCenter.y + self.triangleMesh.getMinimumZ() + self.modelDirty = True def OnMouseMotion(self,e): if e.Dragging() and e.LeftIsDown(): @@ -55,11 +67,12 @@ class myGLCanvas(GLCanvas): self.pitch = 170 if self.pitch < 10: self.pitch = 10 + self.Refresh() if e.Dragging() and e.RightIsDown(): self.zoom += e.GetY() - self.oldY + self.Refresh() self.oldX = e.GetX() self.oldY = e.GetY() - self.Refresh() def OnEraseBackground(self,event): pass @@ -85,18 +98,25 @@ class myGLCanvas(GLCanvas): glTranslate(-self.machineCenter.x, -self.machineCenter.y, 0) if self.triangleMesh != None: - glBegin(GL_TRIANGLES) - for face in self.triangleMesh.faces: - v1 = self.triangleMesh.vertexes[face.vertexIndexes[0]] - v2 = self.triangleMesh.vertexes[face.vertexIndexes[1]] - v3 = self.triangleMesh.vertexes[face.vertexIndexes[2]] - normal = (v2 - v1).cross(v3 - v1) - normal.normalize() - glNormal3f(normal.x, normal.y, normal.z) - glVertex3f(v1.x, v1.y, v1.z) - glVertex3f(v2.x, v2.y, v2.z) - glVertex3f(v3.x, v3.y, v3.z) - glEnd() + if self.modelDisplayList == None: + self.modelDisplayList = glGenLists(1); + if self.modelDirty: + self.modelDirty = False + glNewList(self.modelDisplayList, GL_COMPILE) + glBegin(GL_TRIANGLES) + for face in self.triangleMesh.faces: + v1 = self.triangleMesh.vertexes[face.vertexIndexes[0]] + v2 = self.triangleMesh.vertexes[face.vertexIndexes[1]] + v3 = self.triangleMesh.vertexes[face.vertexIndexes[2]] + normal = (v2 - v1).cross(v3 - v1) + normal.normalize() + glNormal3f(normal.x, normal.y, normal.z) + glVertex3f(v1.x, v1.y, v1.z) + glVertex3f(v2.x, v2.y, v2.z) + glVertex3f(v3.x, v3.y, v3.z) + glEnd() + glEndList() + glCallList(self.modelDisplayList) glLineWidth(4) glDisable(GL_LIGHTING) @@ -160,6 +180,6 @@ class myGLCanvas(GLCanvas): glTranslate(0,0,-self.zoom) glRotate(-self.pitch, 1,0,0) glRotate(self.yaw, 0,0,1) - #glRotate(90, 1,0,0) - + if self.triangleMesh != None: + glTranslate(0,0,-self.triangleMesh.getCarveCornerMaximum().z / 2) return diff --git a/SkeinPyPy_NewUI/skeinpypy.py b/SkeinPyPy_NewUI/skeinpypy.py index a4f3afa..dabf9ca 100644 --- a/SkeinPyPy_NewUI/skeinpypy.py +++ b/SkeinPyPy_NewUI/skeinpypy.py @@ -13,7 +13,11 @@ from __future__ import absolute_import from optparse import OptionParser from skeinforge_application.skeinforge_utilities import skeinforge_craft -from newui import mainWindow +#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 import sys import platform