Beispiel #1
0
class GCodeDlg(wx.Frame):
	def __init__(self, parent, server, gcode, filenm, pname, settings, images, cbexit):
		wx.Frame.__init__(self, None, wx.ID_ANY, self.formatTitle(pname, filenm))
		self.SetBackgroundColour("white")
		self.Bind(wx.EVT_CLOSE, self.onClose)

		self.parent = parent
		self.server = server
		self.gcode = gcode
		self.filenm = filenm
		self.pname = pname
		self.settings = settings
		self.images = images
		self.exitDlg = cbexit
		self.nExtr = self.parent.nExtr
		self.layerCount = 0
		if self.gcode:
			self.sTotalTime = " / " + formatElapsed(self.gcode.getPrintTime())
			self.filament = self.gcode.getFilament()
		else:
			self.sTotalTime = ""
			self.filament = [[0.0, 0.0]]

		self.printPosition = 0
		self.followPrint = True

		self.lx = 0

		lbFont = wx.Font(10, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)

		self.gcf = GcFrame(self, self.pname, self.gcode, self.settings)
		self.stHeight = wx.StaticText(self, wx.ID_ANY, "")
		self.stHeight.SetFont(lbFont)
		self.stTime = wx.StaticText(self, wx.ID_ANY, "")
		self.stTime.SetFont(lbFont)
		self.stFilament = wx.StaticText(self, wx.ID_ANY, "")
		self.stFilament.SetFont(lbFont)
		self.slLayer = wx.Slider(self, wx.ID_ANY, 1, 1, 10, style=wx.SL_VERTICAL+wx.SL_LABELS+wx.SL_INVERSE)
		self.Bind(wx.EVT_SLIDER, self.onSlLayer, self.slLayer)
		self.bUp =  wx.BitmapButton(self, wx.ID_ANY, self.images.pngUp, size=BTNDIM, style=wx.NO_BORDER)
		self.bUp.SetBackgroundColour("white")
		self.Bind(wx.EVT_BUTTON, self.onBUp, self.bUp)
		self.bDown =  wx.BitmapButton(self, wx.ID_ANY, self.images.pngDown, size=BTNDIM, style=wx.NO_BORDER)
		self.bDown.SetBackgroundColour("white")
		self.Bind(wx.EVT_BUTTON, self.onBDown, self.bDown)

		self.setSliderRange()

		self.cbSync = wx.CheckBox(self, wx.ID_ANY, "Sync with print")
		self.cbSync.SetValue(True)
		self.Bind(wx.EVT_CHECKBOX, self.onCbSync, self.cbSync)
		self.cbPrintedOnly = wx.CheckBox(self, wx.ID_ANY, "Only show printed")
		self.cbPrintedOnly.SetValue(self.settings.getSetting("showprintedonly", self.pname, "False"))
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowPrintedOnly, self.cbPrintedOnly)
		self.cbShowPrev = wx.CheckBox(self, wx.ID_ANY, "Show previous layer")
		self.cbShowPrev.SetValue(self.settings.getSetting("showprevious", self.pname, "False"))
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowPrev, self.cbShowPrev)
		self.cbShowMoves = wx.CheckBox(self, wx.ID_ANY, "Show moves")
		self.cbShowMoves.SetValue(self.settings.getSetting("showmoves", self.pname, "False"))
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowMoves, self.cbShowMoves)
		self.cbShowRetr = wx.CheckBox(self, wx.ID_ANY, "Show retractions")
		self.cbShowRetr.SetValue(self.settings.getSetting("showretractions", self.pname, "False"))
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowRetr, self.cbShowRetr)
		self.cbShowRevRetr = wx.CheckBox(self, wx.ID_ANY, "Show reverse retractions")
		self.cbShowRevRetr.SetValue(self.settings.getSetting("showrevretractions", self.pname, "False"))
		self.Bind(wx.EVT_CHECKBOX, self.onCbShowRevRetr, self.cbShowRevRetr)

		sznavgc = wx.BoxSizer(wx.VERTICAL)
		sznavgc.Add(self.bUp, 0, wx.LEFT, 12 if os.name == 'posix' else 25)
		sznavgc.Add(self.slLayer, 1, wx.GROW)
		sznavgc.Add(self.bDown, 0, wx.LEFT, 12 if os.name == 'posix' else 25)

		szgcf = wx.BoxSizer(wx.VERTICAL)
		szgcf.Add(self.gcf)
		szgcf.AddSpacer(5)
		szgcf.Add(self.stHeight, 0, wx.ALIGN_CENTER)
		szgcf.AddSpacer(5)
		szgcf.Add(self.stTime, 0, wx.ALIGN_CENTER)
		szgcf.AddSpacer(5)
		szgcf.Add(self.stFilament, 0, wx.ALIGN_CENTER)

		szgc = wx.BoxSizer(wx.HORIZONTAL)
		szgc.AddSpacer(15)
		szgc.Add(szgcf)
		if os.name == 'posix':
			szgc.AddSpacer(10)
		szgc.Add(sznavgc, 1, wx.GROW)
		szgc.AddSpacer(15)

		szopt1 = wx.BoxSizer(wx.VERTICAL)
		szopt1.Add(self.cbSync, 1, wx.EXPAND)
		szopt1.Add(self.cbPrintedOnly, 1, wx.EXPAND)

		szopt2 = wx.BoxSizer(wx.VERTICAL)
		szopt2.Add(self.cbShowPrev, 1, wx.EXPAND)
		szopt2.Add(self.cbShowMoves, 1, wx.EXPAND)

		szopt3 = wx.BoxSizer(wx.VERTICAL)
		szopt3.Add(self.cbShowRetr, 1, wx.EXPAND)
		szopt3.Add(self.cbShowRevRetr, 1, wx.EXPAND)

		szoptions = wx.BoxSizer(wx.HORIZONTAL)
		szoptions.AddSpacer(20)
		szoptions.Add(szopt1, 1, wx.EXPAND)
		szoptions.AddSpacer(5)
		szoptions.Add(szopt2, 1, wx.EXPAND)
		szoptions.AddSpacer(5)
		szoptions.Add(szopt3, 1, wx.EXPAND)
		szoptions.AddSpacer(10)

		sz = wx.BoxSizer(wx.VERTICAL)
		sz.AddSpacer(10)
		sz.Add(szgc)
		sz.AddSpacer(5)
		sz.Add(szoptions)
		sz.AddSpacer(10)

		self.showLayerInfo()

		self.SetSizer(sz)
		self.Fit()
		self.Layout()

	@staticmethod
	def formatTitle(pname, filenm):

		if filenm is None:
			return "%s - GCode Monitor - (no file loaded)" % pname
		else:
			return "%s - GCode monitor - %s" % (pname, filenm)

	def reloadGCode(self, gcode, filenm):
		self.filenm = filenm
		self.SetTitle(self.formatTitle(self.pname, filenm))
		self.gcode = gcode
		if self.gcode:
			self.sTotalTime = " / " + formatElapsed(self.gcode.getPrintTime())
			self.filament = self.gcode.getFilament()
		else:
			self.sTotalTime = ""
			self.filament = 0.0

		self.setSliderRange()

		# send new gcode to gcf
		self.printPosition = 0
		self.gcf.loadGCode(gcode)
		self.showLayerInfo()

	def setSliderRange(self):
		n = self.gcode.layerCount()
		self.slLayer.SetRange(1, 10 if n == 0 else n)
		self.slLayer.SetValue(1)
		self.slLayer.Enable(n != 0)
		self.bUp.Enable(n != 0)
		self.bDown.Enable(n != 0)
		self.layerCount = n

	def setPrintPosition(self, pos):
		if pos is None:
			return False, None, None
		self.printPosition = 0 if pos is None else pos
		self.gcf.setPrintPosition(self.printPosition)

		pLayer, lpct = self.gcode.findLayerByOffset(self.printPosition)
		cLayer = self.gcf.getCurrentLayerNum()

		if not self.followPrint:
			return cLayer != pLayer, pLayer, lpct
		
		if cLayer != pLayer:
			self.gcf.setLayer(pLayer)
			self.slLayer.SetValue(pLayer+1)
			self.showLayerInfo()
			return True, pLayer, lpct

		return False, pLayer, lpct

	def showLayerInfo(self):
		l = self.gcf.getCurrentLayer()
		if l is None:
			lblHt = ""
			lblTime = ""
			lblFilament = ""
		else:
			lx = self.gcf.getCurrentLayerNum()
			lblHt, lblTime, lblFilament = self.formatLayerInfo(l, lx)
			
		self.stHeight.SetLabel(lblHt)
		self.stTime.SetLabel(lblTime)
		self.stFilament.SetLabel(lblFilament)
		
	def formatLayerInfo(self, l, lx):
		sHt = "Height: {:.2f}  Layer: {:d} / {:d}".format(l.getHeight(), lx+1, self.layerCount)
		
		sTm = "  Print time: {:s}{:s}".format(formatElapsed(l.getLayerTime()), self.sTotalTime)
		o = l.getOffsets()
		if self.printPosition < o[0]:
			lyrs = self.gcode.getLayersBetweenOffsets(self.printPosition, o[0])
			untilTime = 0.0
			for ly in lyrs:
				untilTime += ly.getLayerTime()
			sTm += " ({:s} until)".format(formatElapsed(untilTime))

		sFi = "  Filament: "
		lf = l.getFilament()
		for i in range(self.nExtr):
			if i > 0:
				sFi += " / "
				
			if self.nExtr > 1:
				if i > 0:
					sFi += " - "
				sFi += "{:d}: ".format(i)
			sFi += "{:.2f}mm ({:.2f}cm3) / {:.2f}mm".format(lf[i][0], lf[i][1], self.filament[i][0])
			
		return sHt, sTm, sFi

	def onSlLayer(self, _):
		self.followPrintOff()
		v = self.slLayer.GetValue()-1
		self.gcf.setLayer(v)
		self.showLayerInfo()

	def onBUp(self, _):
		v = self.slLayer.GetValue()
		if v < self.gcode.layerCount():
			self.followPrintOff()
			v += 1
			self.slLayer.SetValue(v)
			self.gcf.setLayer(v-1)
			self.showLayerInfo()

	def onBDown(self, _):
		v = self.slLayer.GetValue()
		if v > 1:
			self.followPrintOff()
			v -= 1
			self.slLayer.SetValue(v)
			self.gcf.setLayer(v-1)
			self.showLayerInfo()

	def followPrintOff(self):
		if self.followPrint:
			self.cbSync.SetValue(False)
			self.followPrint = False
			self.gcf.setFollowPrint(False)

	def onCbSync(self, _):
		self.followPrint = self.cbSync.GetValue()
		self.gcf.setFollowPrint(self.followPrint)

	def onCbShowPrintedOnly(self, _):
		v = self.cbPrintedOnly.GetValue()
		self.settings.setSetting("showprintedonly", str(v), self.pname)
		self.gcf.setShowPrintedOnly(v)

	def onCbShowPrev(self, _):
		v = self.cbShowPrev.GetValue()
		self.settings.setSetting("showprevious", str(v), self.pname)
		self.gcf.setShowPrevious(v)

	def onCbShowMoves(self, _):
		v = self.cbShowMoves.GetValue()
		self.settings.setSetting("showmoves", str(v), self.pname)
		self.gcf.setShowMoves(v)

	def onCbShowRetr(self, _):
		v = self.cbShowRetr.GetValue()
		self.settings.setSetting("showretractions", str(v), self.pname)
		self.gcf.setShowRetractions(v)

	def onCbShowRevRetr(self, _):
		v = self.cbShowRevRetr.GetValue()
		self.settings.setSetting("showrevretractions", str(v), self.pname)
		self.gcf.setShowRevRetractions(v)

	def onClose(self, _):
		self.exitDlg()
Beispiel #2
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
Beispiel #3
0
class PrintMonitorDlg(wx.Frame):
    def __init__(self, parent, wparent, reprap, prtName):
        self.parent = parent
        self.wparent = wparent
        self.log = self.parent.log
        self.history = wparent.history
        self.reprap = reprap
        self.settings = self.parent.settings
        self.images = self.parent.images
        self.state = PrintState.idle
        self.oldState = None
        self.gcodeLoaded = False
        self.gcodeFile = None
        self.printerName = prtName
        self.layerMap = []
        self.okToImport = False
        self.importFile = None

        self.currentLayer = 0
        self.maxTool = 0
        self.eUsed = [0.0, 0.0, 0.0, 0.0]
        self.totalTime = 0
        self.totalTimeStr = ""
        self.layerTimes = []
        self.layerTimeStr = []
        self.layerRange = (0, 0)

        self.gObj = None
        self.printLayer = 0
        self.printPosition = None

        title = self.buildTitle()
        wx.Frame.__init__(self, wparent, wx.ID_ANY, title=title)
        self.Show()
        ico = wx.Icon(os.path.join(cmdFolder, "images", "printmon.png"),
                      wx.BITMAP_TYPE_PNG)
        self.SetIcon(ico)

        if self.settings.hassdcard:
            self.sdcard = SDCard(self.parent, self, self.reprap, self.log)
        else:
            self.sdcard = None

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

        ht = self.gcf.GetSize().Get()[1] - BUTTONDIM[1] * 2 - 20

        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)
        self.slLayers.Enable(False)

        self.cbShowMoves = wx.CheckBox(self, wx.ID_ANY, "Show moves")
        self.cbShowMoves.SetValue(self.settings.showmoves)
        self.Bind(wx.EVT_CHECKBOX, self.onShowMoves, self.cbShowMoves)

        self.cbShowPrevious = wx.CheckBox(self, wx.ID_ANY,
                                          "Show previous layer")
        self.cbShowPrevious.SetValue(self.settings.showprevious)
        self.Bind(wx.EVT_CHECKBOX, self.onShowPrevious, self.cbShowPrevious)

        self.cbToolPathOnly = wx.CheckBox(self, wx.ID_ANY,
                                          "Show tool paths only")
        self.cbToolPathOnly.SetValue(self.settings.toolpathonly)
        self.Bind(wx.EVT_CHECKBOX, self.onToolPathOnly, self.cbToolPathOnly)

        self.cbSyncPrint = wx.CheckBox(self, wx.ID_ANY, "Sync with print")
        self.cbSyncPrint.SetValue(True)
        self.Bind(wx.EVT_CHECKBOX, self.onSyncPrint, self.cbSyncPrint)

        self.bImport = wx.BitmapButton(self,
                                       wx.ID_ANY,
                                       self.images.pngImport,
                                       size=BUTTONDIM)
        self.bImport.SetToolTip("Import G Code file from toolbox")
        self.Bind(wx.EVT_BUTTON, self.onImport, self.bImport)

        self.bImportQ = wx.BitmapButton(self,
                                        wx.ID_ANY,
                                        self.images.pngNext,
                                        size=BUTTONDIM)
        self.Bind(wx.EVT_BUTTON, self.onImportFromQueue, self.bImportQ)

        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.onOpenFile, self.bOpen)

        self.Bind(wx.EVT_CLOSE, self.onClose)

        self.bPrint = PrintButton(self, self.images)
        self.bPrint.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.onPrint, self.bPrint)

        self.bPause = PauseButton(self, self.images)
        self.bPause.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.onPause, self.bPause)

        self.bSdPrintTo = wx.BitmapButton(self,
                                          wx.ID_ANY,
                                          self.images.pngSdprintto,
                                          size=(BUTTONDIMWIDE))
        self.bSdPrintTo.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.onSdPrintTo, self.bSdPrintTo)

        self.bSdPrintFrom = wx.BitmapButton(self,
                                            wx.ID_ANY,
                                            self.images.pngSdprintfrom,
                                            size=(BUTTONDIMWIDE))
        self.bSdPrintFrom.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.onSdPrintFrom, self.bSdPrintFrom)

        self.bSdDelete = wx.BitmapButton(self,
                                         wx.ID_ANY,
                                         self.images.pngSddelete,
                                         size=(BUTTONDIM))
        self.bSdDelete.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.onSdDelete, self.bSdDelete)

        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)

        szGcf = wx.BoxSizer(wx.HORIZONTAL)
        szGcf.AddSpacer(10)
        szGcf.Add(self.gcf)
        szGcf.Add(self.stLayerText, 1, wx.ALIGN_CENTER_HORIZONTAL, 1)
        szGcf.AddSpacer(10)

        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)

        szGcf.Add(szNav)
        szGcf.AddSpacer(10)

        szOpts = wx.BoxSizer(wx.HORIZONTAL)
        szOpts.AddSpacer(10)
        szOpts.Add(self.cbShowMoves)
        szOpts.AddSpacer(10)
        szOpts.Add(self.cbShowPrevious)
        szOpts.AddSpacer(10)
        szOpts.Add(self.cbToolPathOnly)
        szOpts.AddSpacer(10)
        szOpts.Add(self.cbSyncPrint)
        szOpts.AddSpacer(10)

        szBtn = wx.BoxSizer(wx.HORIZONTAL)
        szBtn.AddSpacer(10)
        szBtn.Add(self.bImport)
        szBtn.AddSpacer(10)
        szBtn.Add(self.bImportQ)
        szBtn.AddSpacer(10)
        szBtn.Add(self.bOpen)
        szBtn.AddSpacer(20)
        szBtn.Add(self.bPrint)
        szBtn.AddSpacer(10)
        szBtn.Add(self.bPause)
        if self.sdcard:
            szBtn.AddSpacer(20)
            szBtn.Add(self.bSdPrintTo)
            szBtn.AddSpacer(10)
            szBtn.Add(self.bSdPrintFrom)
            szBtn.AddSpacer(10)
            szBtn.Add(self.bSdDelete)

        szBtn.AddSpacer(10)

        szDlg = wx.BoxSizer(wx.VERTICAL)
        szDlg.AddSpacer(10)
        szDlg.Add(szGcf)
        szDlg.AddSpacer(10)
        szDlg.Add(szOpts)
        szDlg.AddSpacer(10)
        szDlg.Add(szBtn)
        szDlg.AddSpacer(10)

        self.SetSizer(szDlg)
        self.Fit()
        self.Layout()

        self.propDlg = PropertiesDlg(self, wparent, self.printerName)
        self.propDlg.Show()
        if not self.settings.propposition is None:
            self.propDlg.SetPosition(self.settings.propposition)

        self.enableButtonsByState()
        self.reprap.registerPositionHandler(self.updatePrintPosition)
        self.reprap.registerEventHandler(self.reprapEvent)
        self.reprap.registerSdEventHandler(self.sdcard)

    def show(self):
        self.Show()
        self.Raise()
        self.propDlg.Show()
        self.propDlg.Raise()

    def setLayerText(self, ht):
        if ht is None:
            htv = 0.0
        else:
            htv = ht
        self.stLayerText.SetLabel("Layer Height: %0.3f" % htv)

    def getStatusReport(self):
        r = self.propDlg.getStatusReport()
        r["PrintStatus"] = PrintState.label[self.state]
        return r

    def buildTitle(self):
        t = "%s print monitor" % self.printerName

        if self.gcodeLoaded:
            if len(self.gcodeFile) > 45:
                t += " - %s" % os.path.basename(self.gcodeFile)
            else:
                t += " - %s" % self.gcodeFile

        return t

    def rememberPositions(self):
        self.settings.propposition = self.propDlg.GetPosition()

    def isPrinting(self):
        return self.state in [
            PrintState.printing, PrintState.sdprintingto,
            PrintState.sdprintingfrom
        ]

    def onClose(self, evt):
        if self.isPrinting():
            dlg = wx.MessageDialog(self, 'Cannot exit with printing active',
                                   "Printer is active",
                                   wx.OK | wx.ICON_INFORMATION)
            dlg.ShowModal()
            dlg.Destroy()
            return
        self.terminate()

    def terminate(self):
        self.reprap.registerPositionHandler(None)
        self.reprap.registerEventHandler(None)
        self.parent.closePrintMon()
        self.propDlg.Destroy()
        self.Destroy()

    def onShowMoves(self, evt):
        v = self.cbShowMoves.GetValue()
        self.settings.showmoves = v
        self.gcf.setShowMoves(v)

    def onShowPrevious(self, evt):
        v = self.cbShowPrevious.GetValue()
        self.settings.showprevious = v
        self.gcf.setShowPrevious(v)

    def onToolPathOnly(self, evt):
        v = self.cbToolPathOnly.GetValue()
        self.settings.toolpathonly = v
        self.gcf.setToolPathsOnly(v)

    def onSyncPrint(self, evt):
        v = self.cbSyncPrint.GetValue()
        self.gcf.setSyncWithPrint(v)

    def onLayerScroll(self, evt):
        v = self.slLayers.GetValue()
        if v == self.currentLayer:
            return

        self.gcf.setLayer(v)
        self.changeLayer(v)

    def onUp(self, evt):
        lmax = self.slLayers.GetRange()[1]
        if self.currentLayer >= lmax:
            return

        v = self.currentLayer + 1
        self.gcf.setLayer(v)
        self.changeLayer(v)

    def onDown(self, evt):
        if self.currentLayer <= 0:
            return

        v = self.currentLayer - 1
        self.gcf.setLayer(v)
        self.changeLayer(v)

    def onImport(self, evt):
        fn = self.wparent.importGcFile()
        if fn is None:
            return

        self.loadGFile(fn)

    def onImportFromQueue(self, evt):
        fn = self.wparent.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 setImportFile(self, fn):
        self.importFile = fn
        if fn is None:
            self.bImport.Enable(False)
            self.bImport.SetToolTip("")
        else:
            self.bImport.Enable(self.bOpen.IsEnabled())
            self.bImport.SetToolTip("Import G Code file (%s)" % fn)

    def onOpenFile(self, evt):
        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.loadGCode(path)

        if self.gObj is None:
            lmax = 1
            self.slLayers.Enable(False)
            self.bUp.Enable(False)
            self.bDown.Enable(False)
        else:
            lmax = self.gObj.layerCount() - 1
            self.slLayers.Enable(True)
            self.bUp.Enable(True)
            self.bDown.Enable(True)

        self.slLayers.SetRange(0, lmax)
        self.slLayers.SetPageSize(int(lmax / 10))

        self.gcf.loadModel(self.gObj)
        self.changeLayer(0)

        self.state = PrintState.idle
        self.oldState = None
        self.enableButtonsByState()
        t = self.buildTitle()
        self.SetTitle(t)
        self.propDlg.setPrintStatus(PrintState.idle)

    def loadGCode(self, fn):
        def gnormal(s):
            if ";" in s:
                return s.split(";")[0].rstrip()
            else:
                return s.rstrip()

        self.gcodeFile = None
        self.gcodeLoaded = False
        self.gcode = []
        self.gObj = None
        self.maxLine = 0
        self.totalTime = 0
        self.totalTimeStr = ""
        self.layerTimes = []
        self.layerTimeStr = []
        self.propDlg.clearAllProperties()
        self.reprap.clearPrint()
        if fn is None:
            return

        try:
            gc = list(open(fn))
        except:
            self.log("Error opening file %s" % fn)
            self.gcode = []
            self.gObj = None
            self.gcodeLoaded = False
            return

        self.gcode = [s for s in map(gnormal, gc) if s.strip() != ""]
        self.gObj = self.buildModel()
        self.maxLine = self.gObj.getMaxLine()
        self.eUsed = self.gObj.getFilament()

        self.gcodeLoaded = True
        self.gcodeFile = pfn = fn
        if len(pfn) > 45:
            pfn = os.path.basename(fn)
        self.propDlg.setProperty(PropertyEnum.fileName, pfn)
        ftime = time.strftime('%y/%m/%d-%H:%M:%S',
                              time.localtime(os.path.getmtime(fn)))
        self.propDlg.setProperty(PropertyEnum.sliceTime, ftime)
        self.propDlg.setProperty(PropertyEnum.printEstimate, self.totalTimeStr)

        if self.settings.nextruders < self.maxTool + 1:
            self.log(
                "G Code file uses more tools (%d) than printer is equipped with (%d)"
                % (self.maxTool + 1, self.settings.nextruders))

        slCfg, filSiz, tempsHE, tempsBed = parseGCSuffix(gc)

        if tempsBed == "??":
            tBed = 0
        else:
            try:
                tBed = int(float(tempsBed))
            except:
                tBed = 0

        if tempsHE == "??":
            tHe = [0] * self.settings.nextruders
        else:
            try:
                x = [int(float(x)) for x in re.split(", *", tempsHE)
                     ] + [0] * self.settings.nextruders
                tHe = x[:self.settings.nextruders]
            except:
                tHe = [0] * self.settings.nextruders

        self.parent.registerGCodeTemps(tHe, tBed)

        self.propDlg.setProperty(PropertyEnum.slicerCfg, slCfg)
        self.propDlg.setProperty(PropertyEnum.filamentSize, filSiz)
        self.propDlg.setProperty(PropertyEnum.temperatures,
                                 "HE:%s  BED:%s" % (tempsHE, tempsBed))

    def updatePrintPosition(self, position):
        self.printLayer = self.getLayerByPosition(position)
        self.printPosition = position
        if self.state in [PrintState.printing, PrintState.sdprintingto]:
            posString = "%d/%d" % (position, self.maxLine)
            if self.maxLine != 0:
                pct = float(position) / float(self.maxLine) * 100.0
                posString += " (%.1f%%)" % pct
            self.propDlg.setProperty(PropertyEnum.position, posString)
            self.gcf.setPrintPosition(position)
            lx = self.gcf.getCurrentLayer()
            if lx != self.currentLayer:
                self.changeLayer(lx)

            layersSplit = self.sumLayerTimes(self.printLayer)
            layerSplit = self.partialPrintingLayer()

            self.elapsed = time.time() - self.startTime
            expected = layersSplit[0] + layerSplit[0]
            elapsedStr = "%s (expected: %s)" % (formatElapsed(
                self.elapsed), formatElapsed(expected))
            self.propDlg.setProperty(PropertyEnum.elapsed, elapsedStr)

            self.remaining = layersSplit[1] + layerSplit[1]
            self.propDlg.setProperty(PropertyEnum.remaining,
                                     formatElapsed(self.remaining))
            #TODO - probably don't need all the various time estimates when printing TO CD
            # BUT IT MAY BREAK LOGIC BELOW (update time until) that rely on these values

            newEta = time.time() + self.remaining
            revisedStr = time.strftime('%H:%M:%S', time.localtime(newEta))
            tdiff = newEta - self.origEta
            if tdiff < 0:
                revisedStr += "  (%s ahead of estimate)" % formatElapsed(
                    -tdiff)
            elif tdiff > 0:
                revisedStr += "  (%s behind estimate)" % formatElapsed(tdiff)
            self.propDlg.setProperty(PropertyEnum.revisedEta, revisedStr)
            self.updateTimeUntil()

        elif self.state == PrintState.sdprintingfrom:
            #TODO Need to convey print position when printing from SD card
            pass

    def getLayerByPosition(self, pos):
        for lx in range(len(self.layerMap)):
            if self.layerMap[lx][0] <= pos and pos <= self.layerMap[lx][1]:
                return lx

        return 0

    def partialPrintingLayer(self):
        f, l = self.gObj.getGCodeLines(self.printLayer)
        if f <= self.printPosition and self.printPosition <= l:
            done = self.printPosition - f
            todo = l - self.printPosition + 1
            total = l - f + 1

            lt = self.layerTimes[self.printLayer]
            pctDone = float(done) / float(total)
            pctToDo = float(todo) / float(total)
            return (pctDone * lt, pctToDo * lt)
        else:
            return (0.0, 0.0)

    def sumLayerTimes(self, lx):
        tBefore = sum(self.layerTimes[:lx])
        tAfter = sum(self.layerTimes[lx + 1:])
        return (tBefore, tAfter)

    def sumLayerRangeTime(self, slx, elx):
        return sum(self.layerTimes[slx:elx])

    def changeLayer(self, lx):
        self.currentLayer = lx
        self.slLayers.SetValue(lx)
        ht = self.gObj.getLayerHeight(lx)
        self.setLayerText(ht)
        if ht is None:
            self.propDlg.setProperty(PropertyEnum.layerNum,
                                     "%d / %d" % (lx, self.gObj.layerCount()))
        else:
            self.propDlg.setProperty(
                PropertyEnum.layerNum,
                "%d / %d (%.2f mm) " % (lx, self.gObj.layerCount(), ht))

        f, l = self.gObj.getGCodeLines(lx)
        if f is None:
            self.propDlg.setProperty(PropertyEnum.gCodeRange, "")
            self.layerRange = (0, 0)
        else:
            self.propDlg.setProperty(PropertyEnum.gCodeRange,
                                     "%d - %d" % (f, l))
            self.layerRange = (f, l)

        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)

        s = []
        for i in range(self.settings.nextruders):
            s.append("%.2f/%.2f    <: %.2f    >: %.2f" %
                     (le[i], self.eUsed[i], prior[i], after[i]))

        self.propDlg.setProperty(PropertyEnum.filamentUsed, s)

        self.propDlg.setProperty(
            PropertyEnum.layerPrintTime,
            "%s / %s" % (self.layerTimeStr[lx], self.totalTimeStr))

        self.updateTimeUntil()

    def updateTimeUntil(self):
        if self.currentLayer <= self.printLayer:
            self.propDlg.setProperty(PropertyEnum.timeUntil, "")
        elif self.printPosition is None:
            t = sum(self.layerTimes[:self.currentLayer])
            self.propDlg.setProperty(PropertyEnum.timeUntil, formatElapsed(t))
        else:
            t = sum(self.layerTimes[self.printLayer + 1:self.currentLayer]
                    ) + self.partialPrintingLayer()[1]
            self.propDlg.setProperty(PropertyEnum.timeUntil, formatElapsed(t))

    def reprapEvent(self, evt):
        if evt.event == RepRapEventEnum.PRINT_COMPLETE:
            # TODO - do I need special consideration here for print FROM SD
            if self.state == PrintState.sdprintingto:
                self.reprap.sendNow("M29 %s" % self.sdTargetFile)
                self.reprap.suspendTempProbe(False)
                self.setSDTargetFile(None)

            if self.state == PrintState.printing:
                self.history.addEvent(
                    PrintCompleted(self.history.addFile(self.gcodeFile), ""))

            self.state = PrintState.idle
            self.oldState = None
            self.propDlg.setPrintStatus(PrintState.idle)
            self.gcf.setPrintPosition(-1)
            self.printPosition = None
            self.printLayer = 0
            self.enableButtonsByState()
            self.elapsed = time.time() - self.startTime
            cmpTime = time.time()
            expCmpTime = self.origEta - self.startTime
            cmpTimeStr = time.strftime('%H:%M:%S', time.localtime(cmpTime))
            self.log("Print completed at %s" % (cmpTimeStr))
            self.log("Total print time of %s - expected print time %s" %
                     (formatElapsed(self.elapsed), formatElapsed(expCmpTime)))
            self.reprap.printComplete()
        elif evt.event == RepRapEventEnum.PRINT_STOPPED:
            if self.state != PrintState.paused:
                self.oldState = self.state
                self.state = PrintState.paused
                self.propDlg.setPrintStatus(PrintState.paused)
                self.enableButtonsByState()
            self.reprap.printStopped()
        elif evt.event == RepRapEventEnum.PRINT_STARTED:
            pass
        elif evt.event == RepRapEventEnum.PRINT_RESUMED:
            pass
        elif evt.event == RepRapEventEnum.PRINT_ERROR:
            self.log("Error communicating with printer")
        elif evt.event == RepRapEventEnum.PRINT_SENDGCODE:
            self.log(evt.msg)
        else:
            self.log("unknown reprap event: %s" % str(evt.event))

    def buildModel(self):
        cnc = CNC(self.settings.acceleration, self.settings.layerheight)
        if RECORD_TIMES:
            self.log("recording g code times in /tmp/gcodeTimes")
            fp = open("/tmp/gcodeTimes", "w")

        ln = -1
        for gl in self.gcode:
            ln += 1

            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")

                    if "P" in self.paramStr:
                        params["P"] = self._get_float("P")

            t = cnc.execute(p[0], params, ln)
            if RECORD_TIMES:
                fp.write("(%s) (%.3f)\n" % (gl, t))

        if RECORD_TIMES:
            fp.close()

        gobj = cnc.getGObject()
        gobj.setMaxLine(ln)
        self.maxTool = cnc.getMaxTool()
        self.totalTime, self.layerTimes = cnc.getTimes()
        self.layerMap = []
        for lx in range(len(gobj)):
            self.layerMap.append(gobj.getGCodeLines(lx))

        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:
            self.log("unable to parse float from (%s)" % self.paramStr)

    def enableButtonsByState(self):
        if self.state == PrintState.idle:
            self.bOpen.Enable(True)
            self.setImportFile(self.importFile)
            self.bImportQ.Enable(self.okToImport)
            if self.sdcard:
                self.bSdPrintTo.Enable(self.gcodeLoaded)
                self.bSdPrintFrom.Enable()
                self.bSdDelete.Enable()

            if self.gcodeLoaded:
                self.bPrint.Enable(True)
                self.bPrint.setPrint()
                self.bPause.Enable(False)
                self.bPause.setPause()
            else:
                self.bPrint.Enable(False)
                self.bPause.Enable(False)
        elif self.state in [PrintState.printing, PrintState.sdprintingto]:
            self.bImport.Enable(False)
            self.bImportQ.Enable(False)
            self.bOpen.Enable(False)
            self.bPrint.Enable(False)
            self.bPrint.setPrint()
            self.bPause.Enable(True)
            self.bPause.setPause()
            if self.sdcard:
                self.bSdPrintTo.Enable(False)
                self.bSdPrintFrom.Enable(False)
                self.bSdDelete.Enable(False)
        elif self.state == PrintState.sdprintingfrom:
            #TODO - what makes sense here
            pass
        elif self.state == PrintState.paused:
            self.bOpen.Enable(True)
            self.setImportFile(self.importFile)
            self.bImportQ.Enable(self.okToImport)
            self.bPrint.Enable(True)
            self.bPrint.setRestart()
            self.bPause.Enable(True)
            self.bPause.setResume()
            if self.sdcard:
                self.bSdPrintTo.Enable(self.gcodeLoaded)
                self.bSdPrintFrom.Enable()
                self.bSdDelete.Enable()

    def emulatePrintButton(self):
        if self.state in [
                PrintState.printing, PrintState.sdprintingto,
                PrintState.sdprintingfrom
        ]:
            self.log("Already printing")
        elif not self.bPrint.IsEnabled():
            self.log("Unable to print right now")
        else:
            self.onPrint(None)

    def reset(self):
        #TODO - cleanup if was sdprintingfrom
        self.state = PrintState.idle
        self.oldState = None
        self.reprap.suspendTempProbe(False)
        self.setSDTargetFile(None)
        self.propDlg.setPrintStatus(PrintState.idle)
        self.enableButtonsByState()

    def onPrint(self, evt):
        oldState = self.state
        self.state = PrintState.printing
        self.propDlg.setPrintStatus(PrintState.printing)
        self.enableButtonsByState()

        self.printPos = 0
        self.startTime = time.time()
        self.endTime = None
        self.origEta = self.startTime + self.totalTime
        self.elapsed = 0
        self.remaining = self.totalTime
        if oldState == PrintState.paused:
            action = "restarted"
            self.reprap.restartPrint(self.gcode)
        else:
            action = "started"
            self.reprap.startPrint(self.gcode)

        self.history.addEvent(
            PrintStarted(self.history.addFile(self.gcodeFile), ""))

        stime = time.strftime('%H:%M:%S', time.localtime(self.startTime))
        self.propDlg.setProperty(PropertyEnum.startTime, stime)
        self.propDlg.setProperty(
            PropertyEnum.origEta,
            time.strftime('%H:%M:%S', time.localtime(self.origEta)))
        self.propDlg.setProperty(PropertyEnum.elapsed,
                                 formatElapsed(self.elapsed))
        self.propDlg.setProperty(PropertyEnum.remaining,
                                 formatElapsed(self.remaining))
        self.propDlg.setProperty(PropertyEnum.revisedEta, "")
        self.log("Print %s at %s" % (action, stime))

    def onSdPrintFrom(self, evt):
        print "sd print from"

    def doSDPrintFrom(self, evt):
        self.printing = False
        self.paused = False
        self.sdpaused = False
        self.sdprintingfrom = True
        self.sdStartTime = time.time()
        #self.infoPane.setSDStartTime(self.sdStartTime)
        self.state = PrintState.printing
        #self.propDlg.setPrintStatus(PrintState.printing)
        self.enableButtonsByState()
        self.sdcard.startPrintFromSD()

    def cancelSDPrintFrom(self):
        self.sdprintingfrom = False
        self.printing = False
        self.paused = False
        self.state = PrintState.idle
        #self.propDlg.setPrintStatus(PrintState.printing)
        self.enableButtonsByState()

    def resumeSDPrintFrom(self, fn):
        #self.clearModel()
        self.reprap.sendNow("M23 " + fn[1].lower())
        self.reprap.sendNow("M24")
        self.sdprintingfrom = True
        #self.M27Timer.Start(M27Interval, True)
        self.sdpaused = False
        #self.infoPane.setMode(MODE_FROM_SD)
        self.enableButtonsByState()

    def onSdPrintTo(self, evt):
        self.sdcard.startPrintToSD()

    def resumeSDPrintTo(self, tfn):
        self.setSDTargetFile(tfn[1].lower())
        self.reprap.suspendTempProbe(True)
        self.reprap.sendNow("M28 %s" % self.sdTargetFile)
        self.printPos = 0
        self.startTime = time.time()
        self.endTime = None
        self.reprap.startPrint(self.gcode)

        self.origEta = self.startTime + self.totalTime
        self.elapsed = 0
        self.remaining = self.totalTime

        self.state = PrintState.sdprintingto

        stime = time.strftime('%H:%M:%S', time.localtime(self.startTime))
        self.propDlg.setProperty(PropertyEnum.startTime, stime)
        self.propDlg.setProperty(
            PropertyEnum.origEta,
            time.strftime('%H:%M:%S', time.localtime(self.origEta)))
        self.propDlg.setProperty(PropertyEnum.elapsed,
                                 formatElapsed(self.elapsed))
        self.propDlg.setProperty(PropertyEnum.remaining,
                                 formatElapsed(self.remaining))
        self.propDlg.setProperty(PropertyEnum.revisedEta, "")
        self.log("Print to SD: %s started at %s" % (self.sdTargetFile, stime))

        self.enableButtonsByState()

    def setSDTargetFile(self, tfn):
        self.sdTargetFile = tfn
        self.propDlg.setSDTargetFile(tfn)

    def onSdDelete(self, evt):
        self.sdcard.startDeleteFromSD()

    def emulatePauseButton(self):
        if not self.bPause.IsEnabled():
            self.log("Unable to pause right now")
        else:
            self.onPause(None)

    def onPause(self, evt):
        if self.state == PrintState.paused:
            self.state = self.oldState
            if self.state is None:
                self.state = PrintState.printing
            self.propDlg.setPrintStatus(self.state)
            self.enableButtonsByState()
            self.reprap.resumePrint()
        else:
            self.oldState = self.state
            self.state = PrintState.paused
            self.propDlg.setPrintStatus(PrintState.paused)
            self.enableButtonsByState()
            self.reprap.pausePrint()