Exemple #1
0
class GEditDlg(wx.Frame):
	def __init__(self, parent):
		self.parent = parent
		self.history = parent.history
		wx.Frame.__init__(self, None, wx.ID_ANY, TITLE_PREFIX, size=(600, 600))
		self.Show()
		ico = wx.Icon(os.path.join(cmdFolder, "images", "geditico.png"), wx.BITMAP_TYPE_PNG)
		self.SetIcon(ico)
		
		self.Bind(wx.EVT_CLOSE, self.onClose)
		self.settings = Settings(cmdFolder)
		self.propDlg = None
		self.legend = None
		
		self.log = self.parent.log
		
		self.images = Images(os.path.join(cmdFolder, "images"))
		
		self.shiftX = 0
		self.shiftY = 0
		self.modified = False
		self.filename = None
		self.importFileName = None
		self.okToImport = False
		
		self.gObj = self.loadGCode(self.filename)
		if self.gObj is not None:
			self.updateTitle()

		self.gcFrame = GcFrame(self, self.gObj, self.settings)
		self.stLayerText = wx.StaticText(self, wx.ID_ANY, "Layer Height:   0.00")

		ht = self.gcFrame.GetSize().Get()[1] - 2*BUTTONDIM[1] - 20
		
		if self.gObj is None:
			lmax = 1
		else:
			lmax = self.gObj.layerCount()-1
		
		self.slLayers = wx.Slider(
			self, wx.ID_ANY, 0, 0, 1000, size=(-1, ht), 
			style=wx.SL_VERTICAL | wx.SL_AUTOTICKS | wx.SL_LABELS | wx.SL_INVERSE)
		self.Bind(wx.EVT_SCROLL, self.onLayerScroll, self.slLayers)
		if self.gObj is None:
			self.slLayers.Enable(False)
			
		self.lcGCode = GcodeListCtrl(self, self.gcode, self.images)
		self.lcGCode.setLineNumbers(self.settings.uselinenbrs)
		self.currentLayer = 0
		self.setLayerText()
		if self.gObj is not None:
			self.lcGCode.setLayerBounds(self.gObj.getGCodeLines(0))
			
		self.bShift = wx.BitmapButton(self, wx.ID_ANY, self.images.pngShift, size=BUTTONDIM)
		self.bShift.SetToolTip("Move model in x/y direction")
		self.Bind(wx.EVT_BUTTON, self.doShiftModel, self.bShift)
		self.bShift.Enable(False)

		self.bModTemp = wx.BitmapButton(self, wx.ID_ANY, self.images.pngModtemp, size=BUTTONDIM)
		self.bModTemp.SetToolTip("Modify Temperatures")
		self.Bind(wx.EVT_BUTTON, self.onModTemps, self.bModTemp)
		self.bModTemp.Enable(False)
				
		self.bModSpeed = wx.BitmapButton(self, wx.ID_ANY, self.images.pngModspeed, size=BUTTONDIM)
		self.bModSpeed.SetToolTip("Modify Speed")
		self.Bind(wx.EVT_BUTTON, self.onModSpeed, self.bModSpeed)
		self.bModSpeed.Enable(False)
		
		self.bFilChange = wx.BitmapButton(self, wx.ID_ANY, self.images.pngFilchange, size=BUTTONDIM)
		self.bFilChange.SetToolTip("Insert G Code to assist with changing filament")
		self.Bind(wx.EVT_BUTTON, self.onFilChange, self.bFilChange)
		self.bFilChange.Enable(False)
		
		self.bEdit = wx.BitmapButton(self, wx.ID_ANY, self.images.pngEdit, size=BUTTONDIM)
		self.bEdit.SetToolTip("Free edit G Code")
		self.Bind(wx.EVT_BUTTON, self.onEditGCode, self.bEdit)
		self.bEdit.Enable(False)
		
		self.bUp = wx.BitmapButton(self, wx.ID_ANY, self.images.pngUp, size=BUTTONDIM)
		self.bUp.SetToolTip("Move up one layer")
		self.Bind(wx.EVT_BUTTON, self.onUp, self.bUp)
		self.bUp.Enable(False)
		
		self.bDown = wx.BitmapButton(self, wx.ID_ANY, self.images.pngDown, size=BUTTONDIM)
		self.bDown.SetToolTip("Move down one layer")
		self.Bind(wx.EVT_BUTTON, self.onDown, self.bDown)
		self.bDown.Enable(False)
		
		self.bInfo = wx.BitmapButton(self, wx.ID_ANY, self.images.pngInfo, size=BUTTONDIM)
		self.bInfo.SetToolTip("Information")
		self.Bind(wx.EVT_BUTTON, self.onInfo, self.bInfo)
		self.bInfo.Enable(False)
		
		self.bLegend = wx.BitmapButton(self, wx.ID_ANY, self.images.pngLegend, size=BUTTONDIM)
		self.bLegend.SetToolTip("Display a color legend")
		self.Bind(wx.EVT_BUTTON, self.onLegend, self.bLegend)
		self.bLegend.Enable(True)
		
		self.bSaveLayers = wx.BitmapButton(self, wx.ID_ANY, self.images.pngSavelayers, size=BUTTONDIM)
		self.bSaveLayers.SetToolTip("Save specific layers to a file")
		self.Bind(wx.EVT_BUTTON, self.onSaveLayers, self.bSaveLayers)
		self.bSaveLayers.Enable(False)

		self.bOpen = wx.BitmapButton(self, wx.ID_ANY, self.images.pngFileopen, size=BUTTONDIM)
		self.bOpen.SetToolTip("Open a G Code file")
		self.Bind(wx.EVT_BUTTON, self.onOpen, self.bOpen)
		
		self.bImport = wx.BitmapButton(self, wx.ID_ANY, self.images.pngImport, size=BUTTONDIM)
		self.bImport.SetToolTip("Import the current toolbox G Code file")
		self.Bind(wx.EVT_BUTTON, self.onImport, self.bImport)
		self.bImport.Enable(False)
		
		self.bImportQ = wx.BitmapButton(self, wx.ID_ANY, self.images.pngNext, size=BUTTONDIM)
		self.bImportQ.SetToolTip("Import the next G Code file from the queue")
		self.Bind(wx.EVT_BUTTON, self.onImportFromQueue, self.bImportQ)
		self.bImportQ.Enable(False)
		
		self.bExport = wx.BitmapButton(self, wx.ID_ANY, self.images.pngExport, size=BUTTONDIM)
		self.bExport.SetToolTip("Export the current toolbox G Code file")
		self.Bind(wx.EVT_CHECKBOX, self.onExport, self.bExport)
		self.bExport.Enable(not self.settings.autoexport)
		
		self.cbExport = wx.CheckBox(self, wx.ID_ANY, "Auto-export")
		self.cbExport.SetToolTip("Auto-export the current G Code file when saving")
		self.Bind(wx.EVT_CHECKBOX, self.onCbExport, self.cbExport)
		
		self.cbEnqueue = wx.CheckBox(self, wx.ID_ANY, "Add to queue")
		self.cbEnqueue.SetToolTip("Enqueue the current G Code file on the end of the G Code queue when exporting")
		self.Bind(wx.EVT_BUTTON, self.onEnqueue, self.cbEnqueue)
		
		self.bSave = wx.BitmapButton(self, wx.ID_ANY, self.images.pngFilesave, size=BUTTONDIM)
		self.bSave.SetToolTip("Save G Code to the current file")
		self.Bind(wx.EVT_BUTTON, self.onSave, self.bSave)
		self.bSave.Enable(False)
		
		self.bSaveAs = wx.BitmapButton(self, wx.ID_ANY, self.images.pngFilesaveas, size=BUTTONDIM)
		self.bSaveAs.SetToolTip("Save G Code to a different file")
		self.Bind(wx.EVT_BUTTON, self.onSaveAs, self.bSaveAs)
		self.bSaveAs.Enable(False)
		
		self.cbShowMoves = wx.CheckBox(self, wx.ID_ANY, "Show Moves")
		self.cbShowMoves.SetToolTip("Show/Hide non-extrusion moves")
		self.cbShowMoves.SetValue(self.settings.showmoves)
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowMoves, self.cbShowMoves)

		self.cbShowPrevious = wx.CheckBox(self, wx.ID_ANY, "Show Previous Layer")
		self.cbShowPrevious.SetToolTip("Show/Hide the previous layer")
		self.cbShowPrevious.SetValue(self.settings.showprevious)
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowPrevious, self.cbShowPrevious)

		self.cbShowRetractions = wx.CheckBox(self, wx.ID_ANY, "Show Retractions")
		self.cbShowRetractions.SetToolTip("Show/Hide retractions")
		self.cbShowRetractions.SetValue(self.settings.showretractions)
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowRetractions, self.cbShowRetractions)

		self.cbShowRevRetractions = wx.CheckBox(self, wx.ID_ANY, "Show Reverse Retractions")
		self.cbShowRevRetractions.SetToolTip("Show/Hide reverse retractions")
		self.cbShowRevRetractions.SetValue(self.settings.showrevretractions)
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowRevRetractions, self.cbShowRevRetractions)
		
		self.cmbTool = wx.ComboBox(self, wx.ID_ANY, "None", choices = ["None", "0", "1", "2", "3"],
			style = wx.CB_DROPDOWN + wx.CB_READONLY)
		self.cmbTool.SetToolTip("Choose which tool, if any, is highlighted in the display")
		self.Bind(wx.EVT_COMBOBOX, self.onCmbTool, self.cmbTool)

		self.cbLineNbrs = wx.CheckBox(self, wx.ID_ANY, "Line Numbers")
		self.cbLineNbrs.SetToolTip("Use G Code line numbers")
		self.cbLineNbrs.SetValue(self.settings.uselinenbrs)
		self.Bind(wx.EVT_CHECKBOX, self.onCbLineNbrs, self.cbLineNbrs)
		
		self.bBracketStart = wx.BitmapButton(self, wx.ID_ANY, self.images.pngBracketopen, size=BUTTONDIM)
		self.bBracketStart.SetToolTip("Mark the beginning of a block of G code")
		self.Bind(wx.EVT_BUTTON, self.onBracketStart, self.bBracketStart)
		self.bBracketStart.Enable(False)
		
		self.bBracketEnd = wx.BitmapButton(self, wx.ID_ANY, self.images.pngBracketclose, size=BUTTONDIM)
		self.bBracketEnd.SetToolTip("Mark the end of a block of G code")
		self.Bind(wx.EVT_BUTTON, self.onBracketEnd, self.bBracketEnd)
		self.bBracketEnd.Enable(False)
		
		self.bBracketDel = wx.BitmapButton(self, wx.ID_ANY, self.images.pngBracketdel, size=BUTTONDIM)
		self.bBracketDel.SetToolTip("Delete the marked block of G code")
		self.Bind(wx.EVT_BUTTON, self.onBracketDel, self.bBracketDel)
		self.bBracketDel.Enable(False)
		
			
		btnszr = wx.BoxSizer(wx.HORIZONTAL)
		btnszr.AddSpacer(20)
		btnszr.Add(self.bShift)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bModTemp)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bModSpeed)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bFilChange)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bEdit)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bInfo)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bLegend)
		btnszr.AddSpacer(50)
		btnszr.Add(self.bSaveLayers)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bOpen)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bImport)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bImportQ)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bExport)
		btnszr.AddSpacer(5)
		
		optszr = wx.BoxSizer(wx.VERTICAL)
		optszr.AddSpacer(1)
		optszr.Add(self.cbExport)
		optszr.AddSpacer(1)
		optszr.Add(self.cbEnqueue)
		btnszr.Add(optszr)
		
		btnszr.AddSpacer(10)
		btnszr.Add(self.bSave)
		btnszr.AddSpacer(10)
		btnszr.Add(self.bSaveAs)
		btnszr.AddSpacer(10)

		hszr = wx.BoxSizer(wx.HORIZONTAL)
		hszr.AddSpacer(20)
		
		vszr = wx.BoxSizer(wx.VERTICAL)
		vszr.Add(self.gcFrame)
		vszr.Add(self.stLayerText, 1, wx.ALIGN_CENTER_HORIZONTAL, 1)
		vszr.AddSpacer(10)

		opthszr = wx.BoxSizer(wx.HORIZONTAL)
		optszr = wx.BoxSizer(wx.VERTICAL)
		optszr.AddSpacer(1)
		optszr.Add(self.cbShowMoves)
		optszr.AddSpacer(1)
		optszr.Add(self.cbShowPrevious)
		opthszr.Add(optszr)
		opthszr.AddSpacer(10)
		optszr = wx.BoxSizer(wx.VERTICAL)
		optszr.AddSpacer(1)
		optszr.Add(self.cbShowRetractions)
		optszr.AddSpacer(1)
		optszr.Add(self.cbShowRevRetractions)
		opthszr.Add(optszr)
		opthszr.AddSpacer(10)
		optszr = wx.BoxSizer(wx.VERTICAL)
		optszr.AddSpacer(1)
		hsz = wx.BoxSizer(wx.HORIZONTAL)
		hsz.Add(wx.StaticText(self, wx.ID_ANY, "Tool to Hi-light: "), 1, wx.TOP, 5)
		hsz.AddSpacer(5)
		hsz.Add(self.cmbTool)
		optszr.Add(hsz)
		opthszr.Add(optszr)
		vszr.Add(opthszr)
		hszr.Add(vszr)

		szNav = wx.BoxSizer(wx.VERTICAL)
		szNav.Add(self.bUp, 1, wx.ALIGN_CENTER_HORIZONTAL, 1)
		szNav.AddSpacer(10)
		szNav.Add(self.slLayers)
		szNav.AddSpacer(10)
		szNav.Add(self.bDown, 1, wx.ALIGN_CENTER_HORIZONTAL, 1)

		hszr.Add(szNav)
		hszr.AddSpacer(20)
		
		listszr = wx.BoxSizer(wx.VERTICAL)
		listszr.Add(self.lcGCode)
		listszr.AddSpacer(10)
		listszr.Add(self.cbLineNbrs, 1, wx.ALIGN_CENTER_HORIZONTAL, 1)
		
		brksz = wx.BoxSizer(wx.HORIZONTAL)
		brksz.Add(self.bBracketStart)
		brksz.AddSpacer(20)
		brksz.Add(self.bBracketDel)
		brksz.AddSpacer(20)
		brksz.Add(self.bBracketEnd)
		listszr.AddSpacer(10)
		listszr.Add(brksz, 0, wx.ALIGN_CENTER_HORIZONTAL, 1)
		
		hszr.Add(listszr)
		hszr.AddSpacer(20)
		
		vszr = wx.BoxSizer(wx.VERTICAL)
		vszr.AddSpacer(20)
		vszr.Add(btnszr)
		vszr.AddSpacer(10)
		vszr.Add(hszr)
		vszr.AddSpacer(20)
		
		
		self.SetSizer(vszr)
		self.Layout()
		self.Fit()
		
		self.slLayers.SetRange(0, lmax)
		self.slLayers.SetPageSize(int(lmax/10))
		
		if self.gObj is not None:
			self.enableButtons()
			
	def onLegend(self, evt):
		if self.legend is None:
			self.legend = LegendDlg(self)
			self.legend.Show()
		else:
			self.legend.Show()
			self.legend.Raise()
			
	def legendClosed(self):
		self.legend = None
			
	def setImportFile(self, fn):
		self.importFileName = fn
		if fn is None:
			self.bImport.SetToolTip("")
			self.bImport.Enable(False)
		else:
			self.bImport.SetToolTip("Import G Code file (%s)" % fn)
			self.bImport.Enable(True)
			
	def onBracketStart(self, evt):
		b = self.lcGCode.setBracketStart()
		self.gcFrame.setBracket(b)
		self.enableBracketDel(b)

	def onBracketEnd(self, evt):
		b = self.lcGCode.setBracketEnd()
		self.gcFrame.setBracket(b)
		self.enableBracketDel(b)

	def enableBracketDel(self, b=None):
		if b is None:
			b = self.lcGCode.getBracket()
			
		if b[0] is None or b[1] is None:
			self.bBracketDel.Enable(False)
		else:
			self.bBracketDel.Enable(True)
		
	def onBracketDel(self, evt):
		b = self.lcGCode.getBracket()
		if b[0] is None or b[1] is None:
			return
		
		self.gcode = self.gcode[:b[0]] + self.gcode[b[1]+1:]
		self.setModified(True)
		self.gObj = self.buildModel()
		self.modGcSuffixTemps(self.gObj.getTemps())
		l = self.gcFrame.getCurrentLayer()
		self.gcFrame.loadModel(self.gObj, l, self.gcFrame.getZoom())
		lmax = self.gObj.layerCount()-1
		self.slLayers.SetRange(0, lmax)
		self.slLayers.SetPageSize(int(lmax/10))
		self.lcGCode.setGCode(self.gcode)
		self.lcGCode.setLayerBounds(self.gObj.getGCodeLines(l))
		self.bBracketDel.Enable(False)
		self.updateInfoDlg(self.currentLayer)
	
	def updateTitle(self):
		if self.filename is None:
			self.SetTitle("%s" % TITLE_PREFIX)
		else:
			txt = TITLE_PREFIX + " - "
			if self.modified:
				txt += "* "
			txt += self.filename
			self.SetTitle(txt)

			
	def setModified(self, flag=True):
		self.modified = flag
		self.updateTitle()
		
	def onExport(self, evt):
		if self.modified:
			dlg = wx.MessageDialog(self,
				"You have unsaved changes.\nAre you sure you want to export?",
				"Confirm Export With Pending Changes",
				wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
			rc = dlg.ShowModal()
			dlg.Destroy()
			if rc != wx.ID_YES:
				return
		
		self.parent.exportGcFile(self.filename, True, self.settings.autoenqueue)
		
	def onCbExport(self, evt):
		self.settings.autoexport = evt.IsChecked()
		self.bExport.Enable(not self.settings.autoexport)
		
	def onEnqueue(self, evt):
		self.settings.autoenqueue = evt.IsChecked()
		
	def onImport(self, evt):
		fn = self.parent.importGcFile()
		if fn is None:
			return
		
		self.loadGFile(fn)
		
	def onImportFromQueue(self, evt):
		fn = self.parent.importGcFromQueue()
		if fn is None:
			return
		
		self.loadGFile(fn)
		
	def setImportButton(self, msg):
		if msg is None:
			self.okToImport = False
			self.bImportQ.SetToolTip("")
			self.bImportQ.Enable(False)
		else:
			self.okToImport = True
			self.bImportQ.SetToolTip(msg)
			self.bImportQ.Enable(self.bOpen.IsEnabled())
		
	def onOpen(self, evt):
		if self.modified:
			dlg = wx.MessageDialog(self,
				"You have unsaved changes.\nAre you sure you want to open a different file?",
				"Confirm Open With Pending Changes",
				wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
			rc = dlg.ShowModal()
			dlg.Destroy()
			if rc != wx.ID_YES:
				return

		self.gcodeFileDialog()
		
	def onInfo(self, evt):
		if self.propDlg is not None:
			return
		
		self.propDlg = PropertiesDlg(self, self, None, cb=self.onInfoClose)
		
		self.showFileProperties()
		self.showLayerProperties(self.currentLayer)
		self.propDlg.Show()
		
	def onInfoClose(self):
		self.propDlg = None
		
	def updateInfoDlg(self, lx):
		if self.propDlg is None:
			return
		
		self.showFileProperties()
		self.showLayerProperties(lx)
		
	def showFileProperties(self):
		slCfg, filSiz, tempsHE, tempsBed = parseGCSuffix(self.gcode)
		ftime = time.strftime('%y/%m/%d-%H:%M:%S', time.localtime(os.path.getmtime(self.filename)))
		if len(self.filename) > 50:
			self.propDlg.setProperty(PropertyEnum.fileName, os.path.basename(self.filename))
		else:
			self.propDlg.setProperty(PropertyEnum.fileName, self.filename)
		self.propDlg.setProperty(PropertyEnum.slicerCfg, slCfg)
		self.propDlg.setProperty(PropertyEnum.filamentSize, filSiz)
		self.propDlg.setProperty(PropertyEnum.temperatures, "HE:%s  BED:%s" % (tempsHE, tempsBed))
		self.propDlg.setProperty(PropertyEnum.sliceTime, ftime)
		self.propDlg.setProperty(PropertyEnum.printEstimate, self.totalTimeStr)
		
	def showLayerProperties(self, lx):
		if self.propDlg is None:
			return
		
		self.propDlg.setProperty(PropertyEnum.layerNum, "%d" % lx)
		x0, y0, xn, yn = self.gObj.getLayerMinMaxXY(lx)
		if x0 is None:
			self.propDlg.setProperty(PropertyEnum.minMaxXY, "")
		else:
			self.propDlg.setProperty(PropertyEnum.minMaxXY, "(%.2f, %.2f) - (%.2f, %.2f)" % (x0, y0, xn, yn))
		
		le, prior, after = self.gObj.getLayerFilament(lx)
		eUsed = self.gObj.getFilament()
		s = []
		for i in range(self.settings.nextruders):
			s.append("%.2f/%.2f    <: %.2f    >: %.2f" % (le[i], eUsed[i], prior[i], after[i]))
		self.propDlg.setProperty(PropertyEnum.filamentUsed, s)

		f, l = self.gObj.getGCodeLines(lx)
		if f is None:
			self.propDlg.setProperty(PropertyEnum.gCodeRange, "")
		else:
			self.propDlg.setProperty(PropertyEnum.gCodeRange, "%d - %d" % (f, l))
		
		self.propDlg.setProperty(PropertyEnum.layerPrintTime, self.layerTimeStr[lx])
		if lx == 0:
			self.propDlg.setProperty(PropertyEnum.timeUntil, "")
		else:
			t = sum(self.layerTimes[:lx])
			self.propDlg.setProperty(PropertyEnum.timeUntil, formatElapsed(t))
		
	def gcodeFileDialog(self):
		wildcard = "GCode (*.gcode)|*.gcode;*.GCODE|"	 \
			"All files (*.*)|*.*"
			
		dlg = wx.FileDialog(
			self, message="Choose a GCode file",
			defaultDir=self.settings.lastdirectory, 
			defaultFile="",
			wildcard=wildcard,
			style=wx.FD_OPEN)

		rc = dlg.ShowModal()
		if rc == wx.ID_OK:
			path = dlg.GetPath().encode('ascii','ignore')
		dlg.Destroy()
		if rc != wx.ID_OK:
			return
		
		self.loadGFile(path)
		
	def loadGFile(self, path):
		self.settings.lastdirectory = os.path.dirname(path)
		
		self.gObj = self.loadGCode(path)
		if self.gObj is None:
			lmax = 1
			self.slLayers.Enable(False)
			self.bUp.Enable(False)
			self.bDown.Enable(False)
			self.filename = None
		else:
			lmax = self.gObj.layerCount()-1
			self.slLayers.Enable(True)
			self.bUp.Enable(True)
			self.bDown.Enable(True)
			self.filename = path
			
		self.updateTitle()
			
		self.slLayers.SetRange(0, lmax)
		self.slLayers.SetPageSize(int(lmax/10))
		
		self.gcFrame.loadModel(self.gObj)
		self.lcGCode.setGCode(self.gcode)
		self.currentLayer = 0
		self.setLayerText()
		self.slLayers.SetValue(0)
		self.updateInfoDlg(0)

		self.setModified(False)
		if self.gObj is not None:
			self.lcGCode.setLayerBounds(self.gObj.getGCodeLines(0))
			self.enableButtons()
		else:
			self.enableButtons(False)
			
	def enableButtons(self, flag=True, openButtons=False):
		self.bShift.Enable(flag)
		self.bModTemp.Enable(flag)
		self.bModSpeed.Enable(flag)
		self.bEdit.Enable(flag)
		self.bInfo.Enable(flag)
		self.bUp.Enable(flag)
		self.bDown.Enable(flag)
		self.bSaveLayers.Enable(flag)
		self.bSave.Enable(flag)
		self.bSaveAs.Enable(flag)
		self.bExport.Enable(flag and (not self.settings.autoexport))
		self.bFilChange.Enable(flag)
		self.bBracketStart.Enable(flag)
		self.bBracketEnd.Enable(flag)
		self.enableBracketDel()
		if openButtons:
			if flag and self.importFileName is not None:
				self.bImport.Enable(True)
			else:
				self.bImport.Enable(False)
			if flag and self.okToImport:
				self.bImportQ.Enable(True)
			else:
				self.bImportQ.Enable(False)
			self.bOpen.Enable(flag)
			
	def doShiftModel(self, evt):
		dlg = ShiftModelDlg(self, self.gObj, self.settings.buildarea)
		dlg.CenterOnScreen()
		rc = dlg.ShowModal()
		dlg.Destroy()
		if rc == wx.ID_OK:
			self.applyShift()
			self.setModified()
		else:
			self.setShift(0, 0)
		
	def setShift(self, sx, sy):
		self.shiftX = sx
		self.shiftY = sy
		self.gcFrame.setShift(sx, sy)

	def applyShift(self):
		self.gcode = [self.applyAxisShift(self.applyAxisShift(l, 'y', self.shiftY), 'x', self.shiftX) for l in self.gcode]
		self.shiftX = 0
		self.shiftY = 0
		self.gObj = self.buildModel()
		self.gcFrame.loadModel(self.gObj, self.gcFrame.getCurrentLayer(), self.gcFrame.getZoom())
		self.lcGCode.setGCode(self.gcode)
		self.lcGCode.refreshList()
		self.updateInfoDlg(self.currentLayer)

	def applyAxisShift(self, s, axis, shift):
		if "m117" in s or "M117" in s:
			return s

		if axis == 'x':
			m = reX.match(s)
			maxv = self.settings.buildarea[0]
		elif axis == 'y':
			m = reY.match(s)
			maxv = self.settings.buildarea[1]
		elif axis == 'z':
			m = reZ.match(s)
			maxv = self.settings.buildarea[1]
		else:
			return s
		
		if m is None or m.lastindex != 3:
			return s
		
		value = float(m.group(2)) + float(shift)
		if value < 0:
			value = 0.0
		elif value > maxv:
			value = float(maxv)
			
		return "%s%s%s" % (m.group(1), str(value), m.group(3))
	
	def onModTemps(self, evt):
		dlg = ModifyTempsDlg(self, self.gObj, self.settings.platemps, self.settings.abstemps)
		dlg.CenterOnScreen()
		rc = dlg.ShowModal()
		if rc == wx.ID_OK:
			bed, hes = dlg.getResult()
			
		dlg.Destroy()
		if rc != wx.ID_OK:
			return
		
		self.applyTempChange(bed, hes)

	def applyTempChange(self, bed, hes):
		self.currentTool = 0
		self.gcode = [self.applySingleTempChange(l, bed, hes) for l in self.gcode]
		self.setModified(True)
		self.gObj = self.buildModel()
		self.modGcSuffixTemps(self.gObj.getTemps())
		self.gcFrame.loadModel(self.gObj, self.gcFrame.getCurrentLayer(), self.gcFrame.getZoom())
		self.lcGCode.setGCode(self.gcode)
		self.lcGCode.refreshList()
		self.updateInfoDlg(self.currentLayer)
		
	def modGcSuffixTemps(self, nTemps):
		bstr = "%.1f" % nTemps[0]
		
		h = []
		nct = 0
		for x in nTemps[1]:
			if x is None:
				nct += 1
			else:
				if nct != 0:
					h.extend([None]*nct)
					nct = 0
				h.append("%.1f" % x)
		hestr = ",".join(h)
		
		modifyGCSuffix(self.gcode, None, None, hestr, bstr)
		
	def applySingleTempChange(self, s, bed, hes):
		if "m104" in s.lower() or "m109" in s.lower():
			m = reS.match(s)
			difference = hes[self.currentTool]
		elif "m140" in s.lower() or "m190" in s.lower():
			m = reS.match(s)
			difference = bed
		elif s.startswith("T"):
			try:
				t = int(s[1:])
			except:
				t = None

			if t is not None:
				self.currentTool = t
			return s
		else:
			return s

		if m is None or m.lastindex != 3:
			return s

		value = float(m.group(2))
		if value == 0.0:
			return s

		value += float(difference)
		return "%s%s%s" % (m.group(1), str(value), m.group(3))
	
	def onModSpeed(self, evt):
		dlg = ModifySpeedDlg(self)
		dlg.CenterOnScreen()
		val = dlg.ShowModal()

		if val == wx.ID_OK:
			modSpeeds = dlg.getResult()
			
		dlg.Destroy()
		if val != wx.ID_OK:
			return
		
		self.applySpeedChange([float(x)/100.0 for x in modSpeeds])

	def applySpeedChange(self, speeds):
		self.gcode = [self.applySingleSpeedChange(l, speeds) for l in self.gcode]
		self.setModified(True)
		self.gObj = self.buildModel()
		self.gcFrame.loadModel(self.gObj, self.gcFrame.getCurrentLayer(), self.gcFrame.getZoom())
		self.lcGCode.setGCode(self.gcode)
		self.lcGCode.refreshList()
		self.updateInfoDlg(self.currentLayer)

	def applySingleSpeedChange(self, s, speeds):
		if "m117" in s or "M117" in s:
			return s

		m = reF.match(s)
		if m is None or m.lastindex != 3:
			return s

		e = reE.match(s)
		if e is None: #no extrusion - must  be a move
			factor = speeds[1]
		else:
			factor = speeds[0]

		value = float(m.group(2)) * float(factor)
		return "%s%s%s" % (m.group(1), str(value), m.group(3))
	
	def onFilChange(self, evt):
		insertPoint = self.lcGCode.getSelectedLine()
		dlg = FilamentChangeDlg(self, self.gcode, self.gObj,
				insertPoint,
				self.gObj[self.currentLayer].printHeight())
		rc = dlg.ShowModal()
		if rc == wx.ID_OK:
			ngc = dlg.getValues()
			
		dlg.Destroy()
		if rc != wx.ID_OK:
			return
			
		if insertPoint == 0:
			self.gcode = ngc + self.gcode
		else:
			self.gcode = self.gcode[:insertPoint] + ngc + self.gcode[insertPoint:]
		
		self.setModified(True)
		self.enableButtons()
		self.gObj = self.buildModel()
				
		lmax = self.gObj.layerCount()-1
		self.slLayers.SetRange(0, lmax)
		self.slLayers.SetPageSize(int(lmax/10))

		self.gcFrame.loadModel(self.gObj, self.currentLayer, None)
		self.lcGCode.setGCode(self.gcode)
		self.lcGCode.setLayerBounds(self.gObj.getGCodeLines(self.currentLayer))
		self.updateInfoDlg(self.currentLayer)
	
	def onEditGCode(self, evt):
		self.editDlg = EditGCodeDlg(self, self.gcode, "<live buffer>", self.editClosed)
		self.editDlg.CenterOnScreen()
		self.editDlg.Show()
		self.enableButtons(flag=False, openButtons=True)
		
	def editClosed(self, rc):
		self.enableButtons(flag=True, openButtons=True)
		if rc == wx.ID_OK:
			data = self.editDlg.getData()
			
		self.editDlg.Destroy()
		if rc != wx.ID_OK:
			return

		self.gcode = data[:]
		self.setModified(True)
		self.gObj = self.buildModel()
		self.modGcSuffixTemps(self.gObj.getTemps())
		self.gcFrame.loadModel(self.gObj, 0, 1)
		self.currentLayer = 0
		self.setLayerText()
		self.slLayers.SetValue(0)
		
		lmax = self.gObj.layerCount()-1
		self.slLayers.SetRange(0, lmax)
		self.slLayers.SetPageSize(int(lmax/10))

		self.lcGCode.setGCode(self.gcode)
		self.lcGCode.setLayerBounds(self.gObj.getGCodeLines(0))
		self.lcGCode.refreshList()
		self.updateInfoDlg(0)
			
	def onCbShowMoves(self, evt):
		self.settings.showmoves = self.cbShowMoves.GetValue()
		self.gcFrame.setShowMoves(self.settings.showmoves)
	
	def onCbShowPrevious(self, evt):
		self.settings.showprevious = self.cbShowPrevious.GetValue()
		self.gcFrame.setShowPrevious(self.settings.showprevious)
	
	def onCbShowRetractions(self, evt):
		self.settings.showretractions = self.cbShowRetractions.GetValue()
		self.gcFrame.setShowRetractions(self.settings.showretractions)
	
	def onCbShowRevRetractions(self, evt):
		self.settings.showrevretractions = self.cbShowRevRetractions.GetValue()
		self.gcFrame.setShowRevRetractions(self.settings.showrevretractions)
		
	def onCmbTool(self, evt):
		sel = self.cmbTool.GetStringSelection()
		if sel == "" or sel == "None":
			sel = None
		else:
			try:
				sel = int(sel)
			except:
				sel = None
				
		self.gcFrame.setHilightTool(sel)
		
	def onCbLineNbrs(self, evt):
		self.settings.uselinenbrs = self.cbLineNbrs.GetValue()
		self.lcGCode.setLineNumbers(self.settings.uselinenbrs)
		
	def onLayerScroll(self, evt):
		v = self.slLayers.GetValue()
		if v == self.currentLayer:
			return
		
		self.changeLayer(v)
		
	def onUp(self, evt):
		lmax = self.slLayers.GetRange()[1]
		if self.currentLayer >= lmax:
			return
		
		v = self.currentLayer + 1
		self.changeLayer(v)
	
	def onDown(self, evt):
		if self.currentLayer <= 0:
			return
		
		v = self.currentLayer - 1
		self.changeLayer(v)
		
	def changeLayer(self, v):
		self.currentLayer = v
		self.gcFrame.setLayer(v)
		self.slLayers.SetValue(v)
		self.setLayerText()
		self.lcGCode.setLayerBounds(self.gObj.getGCodeLines(v))
		self.showLayerProperties(v)
		
	def setLayerText(self):
		if self.gObj is None:
			ht = 0.0
		else:
			ht = self.gObj[self.currentLayer].printHeight()
		self.stLayerText.SetLabel("Layer Height: %0.3f" % ht)
		
	def reportSelectedLine(self, ln):
		self.gcFrame.reportSelectedLine(ln)
					
	def onClose(self, evt):
		self.settings.save()
		if self.modified:
			dlg = wx.MessageDialog(self,
				"You have unsaved changes.\nAre you sure you want to exit?",
				"Confirm Exit With Pending Changes",
				wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
			rc = dlg.ShowModal()
			dlg.Destroy()
			if rc == wx.ID_YES:
				self.parent.GEditClosed()
		else:
			self.parent.GEditClosed()
		
	def loadGCode(self, fn):
		if fn is None:
			self.gcode = []
			return None
		
		try:
			self.gcode = list(open(fn))
		except:
			print "Error opening file %s" % fn
			return None
		
		return self.buildModel()
		
	def buildModel(self):
		rgcode = [s.rstrip() for s in self.gcode]
		
		cnc = CNC(self.settings.acceleration, self.settings.layerheight)
		
		ln = -1
		for gl in rgcode:
			ln += 1
			if ";" in gl:
				gl = gl.split(";")[0]
			if gl.strip() == "":
				continue
			
			p = re.split("\\s+", gl, 1)
			
			params = {}
			if not (p[0].strip() in ["M117", "m117"]):
				if len(p) >= 2:
					self.paramStr = p[1]
					
					if "X" in self.paramStr:
						params["X"] = self._get_float("X")
						
					if "Y" in self.paramStr:
						params["Y"] = self._get_float("Y")
			
					if "Z" in self.paramStr:
						params["Z"] = self._get_float("Z")
			
					if "E" in self.paramStr:
						params["E"] = self._get_float("E")
			
					if "F" in self.paramStr:
						params["F"] = self._get_float("F")
			
					if "S" in self.paramStr:
						params["S"] = self._get_float("S")
			
			cnc.execute(p[0], params, ln)
			
		gobj = cnc.getGObject()
		gobj.setMaxLine(ln)
		self.totalTime, self.layerTimes = cnc.getTimes()

		self.totalTimeStr = formatElapsed(self.totalTime)
		self.layerTimeStr = [formatElapsed(s) for s in self.layerTimes]

		return gobj
				
	def _get_float(self,which):
		try:
			return float(gcRegex.findall(self.paramStr.split(which)[1])[0])
		except:
			print "exception: ", self.paramStr
	
	def onSaveAs(self, evt):
		wildcard = "GCode (*.gcode)|*.gcode;*.GCODE"

		dlg = wx.FileDialog(
			self, message="Save as ...", defaultDir=self.settings.lastdirectory, 
			defaultFile="", wildcard=wildcard, style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
		
		val = dlg.ShowModal()

		if val != wx.ID_OK:
			dlg.Destroy()
			return
		
		path = dlg.GetPath()
		dlg.Destroy()

		ext = os.path.splitext(os.path.basename(path))[1]
		if ext == "":
			path += ".gcode"
			
		self.saveFile(path)
		
	def onSave(self, evt):
		if self.filename is None:
			self.onSaveAs(evt)
		else:
			self.saveFile(self.filename)
		
	def saveFile(self, path):			
		fp = file(path, 'w')
		
		for ln in self.gcode:
			fp.write("%s\n" % ln.rstrip())
			
		self.setModified(False)
			
		fp.close()
		
		self.filename = path
		self.parent.exportGcFile(path, self.settings.autoexport, self.settings.autoenqueue)
		self.updateTitle()
		
		dlg = wx.MessageDialog(self, "G Code file\n" + path + "\nwritten.",
			'Save Successful', wx.OK | wx.ICON_INFORMATION)
		dlg.ShowModal()
		dlg.Destroy()
		
	def onSaveLayers(self, evt):
		dlg = SaveLayerDlg(self, self.gObj)
		rc = dlg.ShowModal()
		if rc == wx.ID_OK:
			sx, ex, ereset, zmodify, zdelta = dlg.getValues()
			
		dlg.Destroy()
		if rc != wx.ID_OK:
			return
		
		startLine = self.gObj.getGCodeLines(sx)[0]
		endLine = self.gObj.getGCodeLines(ex)[1]
		
		wildcard = "GCode (*.gcode)|*.gcode;*.GCODE"

		dlg = wx.FileDialog(
			self, message="Save as ...", defaultDir=self.settings.lastdirectory, 
			defaultFile="", wildcard=wildcard, style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
		
		val = dlg.ShowModal()

		if val != wx.ID_OK:
			dlg.Destroy()
			return
		
		path = dlg.GetPath()
		dlg.Destroy()

		ext = os.path.splitext(os.path.basename(path))[1]
		if ext == "":
			path += ".gcode"
			
		fp = file(path, 'w')
		
		if ereset:
			fp.write("G92 E%0.5f\n" % self.gObj[sx].startingE())
			
		if zmodify:
			fp.write("\n".join([self.applyAxisShift(ln, 'z', zdelta).rstrip() for ln in self.gcode[startLine:endLine+1]]))
		else:
			fp.write("\n".join([ln.rstrip() for ln in self.gcode[startLine:endLine+1]]))
			
		fp.close()
		
		dlg = wx.MessageDialog(self, "G Code file\n" + path + "\nwritten.",
			'Save Layers Successful', wx.OK | wx.ICON_INFORMATION)
		dlg.ShowModal()
		dlg.Destroy()
		
	def applyZMod(self, ln, modflag):
		if not modflag:
			return ln
		
		return ln