From: daid Date: Thu, 3 May 2012 10:29:55 +0000 (+0200) Subject: Added more GCode validation to GCode editor. Made temp changes to profile really... X-Git-Tag: RC3~13^2~5^2~1 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=e29261a992d85b599af3f21357d93d6504903079;p=cura.git Added more GCode validation to GCode editor. Made temp changes to profile really temp so they are never stored in the ini file. Fixed crash bug in skeinforge where there is no layer code. --- diff --git a/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py b/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py index f018e0c6..eb363a8c 100644 --- a/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py +++ b/Cura/cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py @@ -406,9 +406,10 @@ class CoolSkein: def setMultiplier(self, remainingOrbitTime): 'Set the feed and flow rate multiplier.' layerTimeActive = self.getLayerTimeActive() - self.multiplier = min(1.0, layerTimeActive / (remainingOrbitTime + layerTimeActive)) - - + if remainingOrbitTime + layerTimeActive > 0.00001: + self.multiplier = min(1.0, layerTimeActive / (remainingOrbitTime + layerTimeActive)) + else: + self.multiplier = 1.0 def main(): 'Display the cool dialog.' diff --git a/Cura/gui/gcodeTextArea.py b/Cura/gui/gcodeTextArea.py index e6d85655..6cb108a1 100644 --- a/Cura/gui/gcodeTextArea.py +++ b/Cura/gui/gcodeTextArea.py @@ -13,21 +13,86 @@ class GcodeTextArea(wx.stc.StyledTextCtrl): fontSize = wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize() fontName = wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL).GetFaceName() self.SetStyleBits(5) - self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%s,size:%d" % (fontName, fontSize)) - self.StyleSetSpec(1, "fore:#008000,face:%s,size:%d" % (fontName, fontSize)) + self.StyleSetSpec(0, "face:%s,size:%d" % (fontName, fontSize)) + self.StyleSetSpec(1, "fore:#008000,face:%s,size:%d" % (fontName, fontSize)) + self.IndicatorSetStyle(0, wx.stc.STC_INDIC_TT) + self.IndicatorSetForeground(0, "#0000FF") + self.IndicatorSetStyle(1, wx.stc.STC_INDIC_SQUIGGLE) + self.IndicatorSetForeground(1, "#FF0000") + + #GCodes and MCodes as supported by Marlin + #GCode 21 is not really supported by Marlin, but we still do not report it as error as it's often used. + self.supportedGCodes = [0,1,2,3,4,21,28,90,91,92] + self.supportedMCodes = [17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,42,80,81,82,83,84,85,92,104,105,106,107,109,114,115,117,119,140,190,201,202,203,204,205,206,220,221,240,301,302,303,400,500,501,502,503,999] def OnStyle(self, e): lineNr = self.LineFromPosition(self.GetEndStyled()) while self.PositionFromLine(lineNr) > -1: line = self.GetLine(lineNr) - self.StartStyling(self.PositionFromLine(lineNr), 31) - self.SetStyling(self.LineLength(lineNr), wx.stc.STC_STYLE_DEFAULT) + start = self.PositionFromLine(lineNr) + length = self.LineLength(lineNr) + self.StartStyling(start, 255) + self.SetStyling(length, 0) if ';' in line: pos = line.index(';') - self.StartStyling(self.PositionFromLine(lineNr) + pos, 31) - self.SetStyling(self.LineLength(lineNr) - pos, 1) + self.StartStyling(start + pos, 31) + self.SetStyling(length - pos, 1) + length = pos + + pos = 0 + while pos < length: + if line[pos] in " \t\n\r": + while pos < length and line[pos] in " \t\n\r": + pos += 1 + else: + end = pos + while end < length and not line[end] in " \t\n\r": + end += 1 + if self.checkGCodePart(line[pos:end], start + pos): + self.StartStyling(start + pos, 0x20) + self.SetStyling(end - pos, 0x20) + pos = end lineNr += 1 + def checkGCodePart(self, part, pos): + if len(part) < 2: + self.StartStyling(pos, 0x40) + self.SetStyling(1, 0x40) + return True + if not part[0] in "GMXYZFESTBPIDCJ": + self.StartStyling(pos, 0x40) + self.SetStyling(1, 0x40) + return True + if part[1] == '{': + if part[-1] != '}': + return True + tag = part[2:-1] + if not profile.isProfileSetting(tag) and not profile.isPreference(tag): + self.StartStyling(pos + 2, 0x40) + self.SetStyling(len(tag), 0x40) + return True + elif part[0] in "GM": + try: + code = int(part[1:]) + except (ValueError): + self.StartStyling(pos + 1, 0x40) + self.SetStyling(len(part) - 1, 0x40) + return True + if part[0] == 'G': + if not code in self.supportedGCodes: + return True + if part[0] == 'M': + if not code in self.supportedMCodes: + return True + else: + try: + float(part[1:]) + except (ValueError): + self.StartStyling(pos + 1, 0x40) + self.SetStyling(len(part) - 1, 0x40) + return True + return False + def GetValue(self): return self.GetText() diff --git a/Cura/gui/projectPlanner.py b/Cura/gui/projectPlanner.py index 9b664ce5..a198a76d 100644 --- a/Cura/gui/projectPlanner.py +++ b/Cura/gui/projectPlanner.py @@ -285,9 +285,7 @@ class projectPlanner(wx.Frame): return (maxX - minX) + (maxY - minY) def OnSlice(self, e): - oldProfile = profile.getGlobalProfileString() - - put = profile.putProfileSetting + put = profile.setTempOverride put('model_multiply_x', '1') put('model_multiply_y', '1') @@ -319,7 +317,7 @@ class projectPlanner(wx.Frame): actionList.append(action) #Restore the old profile. - profile.loadGlobalProfileFromString(oldProfile) + profile.resetTempOverride() dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE) dlg.SetWildcard("GCode file (*.gcode)|*.gcode") @@ -670,6 +668,7 @@ class ProjectSliceProgressWindow(wx.Frame): self.sizer.Add(self.statusText, (0,0), flag=wx.ALIGN_CENTER) self.sizer.Add(self.progressGauge, (1, 0), flag=wx.EXPAND) self.sizer.Add(self.progressGauge2, (2, 0), flag=wx.EXPAND) + self.sizer.Add(self.abortButton, (3,0), flag=wx.ALIGN_CENTER) self.sizer.AddGrowableCol(0) self.sizer.AddGrowableRow(0) @@ -702,7 +701,7 @@ class ProjectSliceProgressWindow(wx.Frame): def OnRun(self): resultFile = open(self.resultFilename, "w") - put = profile.putProfileSetting + put = profile.setTempOverride for action in self.actionList: p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) line = p.stdout.readline() @@ -727,7 +726,6 @@ class ProjectSliceProgressWindow(wx.Frame): line = p.stdout.readline() self.returnCode = p.wait() - oldProfile = profile.getGlobalProfileString() put('machine_center_x', action.centerX - self.extruderOffset[action.extruder].x) put('machine_center_y', action.centerY - self.extruderOffset[action.extruder].y) put('clear_z', action.clearZ) @@ -743,7 +741,7 @@ class ProjectSliceProgressWindow(wx.Frame): resultFile.write(';TYPE:CUSTOM\n') resultFile.write(profile.getAlterationFileContents('nextobject.gcode')) resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action)) - profile.loadGlobalProfileFromString(oldProfile) + profile.resetTempOverride() f = open(action.filename[: action.filename.rfind('.')] + "_export.project_tmp", "r") data = f.read(4096) @@ -762,7 +760,7 @@ class ProjectSliceProgressWindow(wx.Frame): resultFile.close() self.abort = True sliceTime = time.time() - self.sliceStartTime - wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %d:%d' % (sliceTime / 60, sliceTime % 60)) + wx.CallAfter(self.statusText.SetLabel, 'Slicing took: %02d:%02d' % (sliceTime / 60, sliceTime % 60)) wx.CallAfter(self.abortButton.SetLabel, 'Close') def main(): diff --git a/Cura/gui/sliceProgessPanel.py b/Cura/gui/sliceProgessPanel.py index 17fc7857..3fd0ad02 100644 --- a/Cura/gui/sliceProgessPanel.py +++ b/Cura/gui/sliceProgessPanel.py @@ -59,21 +59,20 @@ class sliceProgessPanel(wx.Panel): if profile.getPreference('save_profile') == 'True': profile.saveGlobalProfile(self.filelist[0][: self.filelist[0].rfind('.')] + "_profile.ini") cmdList = [] - oldProfile = profile.getGlobalProfileString() for filename in self.filelist: idx = self.filelist.index(filename) print filename, idx if idx > 0: - profile.putProfileSetting('fan_enabled', 'False') - profile.putProfileSetting('skirt_line_count', '0') - profile.putProfileSetting('machine_center_x', profile.getProfileSettingFloat('machine_center_x') - profile.getPreferenceFloat('extruder_offset_x%d' % (idx))) - profile.putProfileSetting('machine_center_y', profile.getProfileSettingFloat('machine_center_y') - profile.getPreferenceFloat('extruder_offset_y%d' % (idx))) - profile.putProfileSetting('alternative_center', self.filelist[0]) + profile.setTempOverride('fan_enabled', 'False') + profile.setTempOverride('skirt_line_count', '0') + profile.setTempOverride('machine_center_x', profile.getProfileSettingFloat('machine_center_x') - profile.getPreferenceFloat('extruder_offset_x%d' % (idx))) + profile.setTempOverride('machine_center_y', profile.getProfileSettingFloat('machine_center_y') - profile.getPreferenceFloat('extruder_offset_y%d' % (idx))) + profile.setTempOverride('alternative_center', self.filelist[0]) if len(self.filelist) > 1: - profile.putProfileSetting('add_start_end_gcode', 'False') - profile.putProfileSetting('gcode_extension', 'multi_extrude_tmp') + profile.setTempOverride('add_start_end_gcode', 'False') + profile.setTempOverride('gcode_extension', 'multi_extrude_tmp') cmdList.append(sliceRun.getSliceCommand(filename)) - profile.loadGlobalProfileFromString(oldProfile) + profile.resetTempOverride() self.thread = WorkerThread(self, filelist, cmdList) def OnAbort(self, e): @@ -188,7 +187,10 @@ class WorkerThread(threading.Thread): resultFile.write(';TYPE:CUSTOM\n') resultFile.write(profile.getAlterationFileContents('start.gcode')) for filename in self.filelist: - files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r")) + if os.path.isfile(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp'): + files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r")) + else: + return currentExtruder = 0 resultFile.write('T%d\n' % (currentExtruder)) diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 96e1eb35..686a8d8b 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -67,6 +67,9 @@ profileDefaultSettings = { 'add_start_end_gcode': 'True', 'gcode_extension': 'gcode', + 'alternative_center': '', + 'clear_z': '0.0', + 'extruder': '0', } alterationDefault = { ####################################################################################### @@ -153,6 +156,7 @@ preferencesDefaultSettings = { ## Profile and preferences functions ######################################################### +## Profile functions def getDefaultProfilePath(): return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini")) @@ -188,17 +192,31 @@ def getGlobalProfileString(): p = [] alt = [] + tempDone = [] if globalProfileParser.has_section('profile'): for key in globalProfileParser.options('profile'): - p.append(key + "=" + globalProfileParser.get('profile', key)) + if key in tempOverride: + p.append(key + "=" + unicode(tempOverride[key])) + tempDone.append(key) + else: + p.append(key + "=" + globalProfileParser.get('profile', key)) if globalProfileParser.has_section('alterations'): for key in globalProfileParser.options('alterations'): - alt.append(key + "=" + globalProfileParser.get('alterations', key)) + if key in tempOverride: + p.append(key + "=" + tempOverride[key]) + tempDone.append(key) + else: + alt.append(key + "=" + globalProfileParser.get('alterations', key)) + for key in tempOverride: + if key not in tempDone: + p.append(key + "=" + unicode(tempOverride[key])) ret = '\b'.join(p) + '\f' + '\b'.join(alt) ret = base64.b64encode(zlib.compress(ret, 9)) return ret def getProfileSetting(name): + if name in tempOverride: + return unicode(tempOverride[name]) #Check if we have a configuration file loaded, else load the default. if not globals().has_key('globalProfileParser'): loadGlobalProfile(getDefaultProfilePath()) @@ -230,6 +248,12 @@ def putProfileSetting(name, value): globalProfileParser.add_section('profile') globalProfileParser.set('profile', name, str(value)) +def isProfileSetting(name): + if name in profileDefaultSettings: + return True + return False + +## Preferences functions global globalPreferenceParser globalPreferenceParser = None @@ -239,11 +263,12 @@ def getPreferencePath(): def getPreferenceFloat(name): try: return float(eval(getPreference(name), {}, {})) - except (ValueError, SyntaxError): return 0.0 def getPreference(name): + if name in tempOverride: + return unicode(tempOverride[name]) global globalPreferenceParser if globalPreferenceParser == None: globalPreferenceParser = ConfigParser.ConfigParser() @@ -273,6 +298,18 @@ def putPreference(name, value): globalPreferenceParser.set('preference', name, unicode(value).encode("utf-8")) globalPreferenceParser.write(open(getPreferencePath(), 'w')) +def isPreference(name): + if name in preferencesDefaultSettings: + return True + return False + +## Temp overrides for multi-extruder slicing and the project planner. +tempOverride = {} +def setTempOverride(name, value): + tempOverride[name] = value +def resetTempOverride(): + tempOverride = {} + ######################################################### ## Utility functions to calculate common profile values ######################################################### @@ -316,7 +353,11 @@ def replaceTagMatch(m): tag = m.group(0)[1:-1] if tag in ['print_speed', 'retraction_speed', 'travel_speed', 'max_z_speed', 'bottom_layer_speed', 'cool_min_feedrate']: return str(getProfileSettingFloat(tag) * 60) - return str(getProfileSettingFloat(tag)) + if isProfileSetting(tag): + return str(getProfileSettingFloat(tag)) + if isPreference(tag): + return str(getProfileSettingFloat(tag)) + return tag ### Get aleration raw contents. (Used internally in Cura) def getAlterationFile(filename):