예제 #1
0
파일: plater.py 프로젝트: jbernardis/plater
	def __init__(self, master, settings):
		self.master = master
		self.settings = settings
		Frame.__init__(self, master, relief=SUNKEN, bd=2)
		self.master.protocol('WM_DELETE_WINDOW', self.quitApp)
		self.objNumber = 1
		
		self.modified = False
		self.preview = True
		
		self.lbMap = []
		self.lbSelection = None
		
		self.stlFrame =  StlFrame(self, scale=settings.scale)
		self.stlFrame.grid(row=1, column=1, rowspan=4, sticky=N)
		self.lbFrame = LabelFrame(self, text=" File List ")
		self.lbFrame.grid(row=1, column=2, padx=5, pady=5, sticky=N)
		self.bFrame = Frame(self)
		self.bFrame.grid(row=2, column=2, padx=5, sticky=N)
		
		self.lbFiles = Listbox(self.lbFrame, width=60)
		self.lbFiles.grid(row=1,column=1,sticky=N+E+W+S)
		self.sbFiles = Scrollbar(self.lbFrame, command=self.lbFiles.yview, orient="vertical")
		self.sbFiles.grid(column=2, row=1, sticky=N+S+E)
		self.lbFiles.config(yscrollcommand=self.sbFiles.set)
		self.lbFiles.bind("<ButtonRelease>", self.doClickList)
		
		self.imgAdd = self.loadImage("add.gif")
		self.bAdd = Button(self.bFrame, image=self.imgAdd, width=48, height=48, command=self.doAdd)
		self.bAdd.grid(row=1, column=1, pady=2, padx=2, sticky=N)
		createToolTip(self.bAdd, "Add an STL file to the plate")

		self.imgClone = self.loadImage("clone.gif")		
		self.bClone = Button(self.bFrame, image=self.imgClone, width=48, height=48, command=self.doClone, state=DISABLED)
		self.bClone.grid(row=1, column=2, pady=2, padx=2, sticky=N)
		createToolTip(self.bClone, "Clone the selected object")
		
		self.imgDel = self.loadImage("del.gif")
		self.bDel = Button(self.bFrame, image=self.imgDel, width=48, height=48, command=self.doDelete, state=DISABLED)
		self.bDel.grid(row=1, column=3, pady=2, padx=2, sticky=N)
		createToolTip(self.bDel, "Delete the selected object")
		
		self.imgDelAll = self.loadImage("delall.gif")
		self.bDelAll = Button(self.bFrame, image=self.imgDelAll, width=48, height=48, command=self.doDelAll, state=DISABLED)
		self.bDelAll.grid(row=1, column=4, pady=2, padx=2, sticky=N)
		createToolTip(self.bDelAll, "Delete all objects from the plate")
		
		self.imgArrange = self.loadImage("arrange.gif")
		self.bArrange = Button(self.bFrame, image=self.imgArrange, width=48, height=48, command=self.doArrange, state=DISABLED)
		self.bArrange.grid(row=2, column=1, pady=2, padx=2, sticky=N)
		createToolTip(self.bArrange, "Arrange the objects to fit")
		
		self.imgExport = self.loadImage("export.gif")
		self.bExport = Button(self.bFrame, image=self.imgExport, width=48, height=48, command=self.doExport, state=DISABLED)
		self.bExport.grid(row=2, column=4, pady=2, padx=2, sticky=N)
		createToolTip(self.bExport, "Save the plate to an STL file")
		
		self.cbPrev = IntVar()
		if self.settings.preview:
			self.cbPrev.set(1)
		else:
			self.cbPrev.set(0)
			
		self.cbPreview = Checkbutton(
            self.bFrame, text="Preview",
            variable=self.cbPrev,
            command=self.doCbPreview)
		self.cbPreview.grid(row=3, column=2, pady=5)
예제 #2
0
	def __init__(self, parent):
		wx.Frame.__init__(self, None, wx.ID_ANY, 'Plater', size=(600, 600))
		self.parent = parent
		self.log = self.parent.log
		self.t = 0
		self.seq = 0
		self.modified = False
		self.settings = Settings(cmdFolder)
		self.Show()
		ico = wx.Icon(os.path.join(cmdFolder, "images", "platerico.png"), wx.BITMAP_TYPE_PNG)
		self.SetIcon(ico)
		
		self.savedfile = None
		
		self.Bind(wx.EVT_CLOSE, self.onClose)
		self.Bind(EVT_SPLIT_UPDATE, self.splitUpdate)
		
		self.images = Images(os.path.join(cmdFolder, "images"))
		
		self.stlCanvas = StlFrame(self, self.settings)
		
		self.bAdd = wx.BitmapButton(self, wx.ID_ANY, self.images.pngAdd, size=BUTTONDIM)
		self.bAdd.SetToolTip("Add an STL file to the plate")
		self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd)
		
		self.bClone = wx.BitmapButton(self, wx.ID_ANY, self.images.pngClone, size=BUTTONDIM)
		self.bClone.SetToolTip("Add a copy of the selected object")
		self.Bind(wx.EVT_BUTTON, self.doClone, self.bClone)
		self.bClone.Enable(False)
		
		self.bDel = wx.BitmapButton(self, wx.ID_ANY, self.images.pngDel, size=BUTTONDIM)
		self.bDel.SetToolTip("Delete the selected object from the plate")
		self.Bind(wx.EVT_BUTTON, self.doDel, self.bDel)
		self.bDel.Enable(False)
		
		self.bDelall = wx.BitmapButton(self, wx.ID_ANY, self.images.pngDelall, size=BUTTONDIM)
		self.bDelall.SetToolTip("Delete ALL objects from the plate")
		self.Bind(wx.EVT_BUTTON, self.doDelall, self.bDelall)
		self.bDelall.Enable(False)
		
		self.bArrange = wx.BitmapButton(self, wx.ID_ANY, self.images.pngArrange, size=BUTTONDIM)
		self.bArrange.SetToolTip("Arrange the objects on the plate")
		self.Bind(wx.EVT_BUTTON, self.doArrange, self.bArrange)
		self.bArrange.Enable(False)
		
		self.bCenter = wx.BitmapButton(self, wx.ID_ANY, self.images.pngCenter, size=BUTTONDIM)
		self.bCenter.SetToolTip("Center the plate")
		self.Bind(wx.EVT_BUTTON, self.doCenter, self.bCenter)
		self.bCenter.Enable(False)
		
		self.bGrid = wx.BitmapButton(self, wx.ID_ANY, self.images.pngGrid, size=BUTTONDIM)
		self.bGrid.SetToolTip("Create a grid of the selected object")
		self.Bind(wx.EVT_BUTTON, self.doGrid, self.bGrid)
		self.bGrid.Enable(False)
		
		self.bMirror = wx.BitmapButton(self, wx.ID_ANY, self.images.pngMirror, size=BUTTONDIM)
		self.bMirror.SetToolTip("Mirror the selected object")
		self.Bind(wx.EVT_BUTTON, self.doMirror, self.bMirror)
		self.bMirror.Enable(False)
		
		self.bRotate = wx.BitmapButton(self, wx.ID_ANY, self.images.pngRotate, size=BUTTONDIM)
		self.bRotate.SetToolTip("Rotate the selected object")
		self.Bind(wx.EVT_BUTTON, self.doRotate, self.bRotate)
		self.bRotate.Enable(False)
		
		self.bTranslate = wx.BitmapButton(self, wx.ID_ANY, self.images.pngTranslate, size=BUTTONDIM)
		self.bTranslate.SetToolTip("Translate the selected object")
		self.Bind(wx.EVT_BUTTON, self.doTranslate, self.bTranslate)
		self.bTranslate.Enable(False)
		
		self.bScale = wx.BitmapButton(self, wx.ID_ANY, self.images.pngScale, size=BUTTONDIM)
		self.bScale.SetToolTip("Scale the selected object")
		self.Bind(wx.EVT_BUTTON, self.doScale, self.bScale)
		self.bScale.Enable(False)
		
		self.bSplit = wx.BitmapButton(self, wx.ID_ANY, self.images.pngSplit, size=BUTTONDIM)
		self.bSplit.SetToolTip("Split the selected object")
		self.Bind(wx.EVT_BUTTON, self.doSplit, self.bSplit)
		self.bSplit.Enable(False)
		
		self.bSaveAs = wx.BitmapButton(self, wx.ID_ANY, self.images.pngFilesaveas, size=BUTTONDIM)
		self.bSaveAs.SetToolTip("Save the plate to an STL file")
		self.Bind(wx.EVT_BUTTON, self.doSaveAs, self.bSaveAs)
		self.bSaveAs.Enable(False)
		
		self.bExport = wx.BitmapButton(self, wx.ID_ANY, self.images.pngExport, size=BUTTONDIM)
		self.bExport.SetToolTip("Export the plate to the toolbox")
		self.Bind(wx.EVT_BUTTON, self.doExport, self.bExport)
		self.bExport.Enable(False)
		
		self.bView = wx.BitmapButton(self, wx.ID_ANY, self.images.pngView, size=BUTTONDIM)
		self.bView.SetToolTip("View the currently selected object")
		self.Bind(wx.EVT_BUTTON, self.doView, self.bView)
		self.bView.Enable(False)
		
		self.bViewPlate = wx.BitmapButton(self, wx.ID_ANY, self.images.pngViewplate, size=BUTTONDIM)
		self.bViewPlate.SetToolTip("View the entire plate")
		self.Bind(wx.EVT_BUTTON, self.doViewPlate, self.bViewPlate)
		self.bViewPlate.Enable(False)
		
		self.cbPreview = wx.CheckBox(self, wx.ID_ANY, "3D Preview")
		self.cbPreview.SetValue(self.settings.preview)
		self.Bind(wx.EVT_CHECKBOX, self.onCbPreview, self.cbPreview)
		
		self.cbCenterOnArrange = wx.CheckBox(self, wx.ID_ANY, "Center Plate After Arrange")
		self.cbCenterOnArrange.SetValue(self.settings.centeronarrange)
		self.Bind(wx.EVT_CHECKBOX, self.onCbCenterOnArrange, self.cbCenterOnArrange)
		
		self.cbAutoExport = wx.CheckBox(self, wx.ID_ANY, "Automatically export when saving")
		self.cbAutoExport.SetValue(self.settings.autoexport)
		self.Bind(wx.EVT_CHECKBOX, self.onCbAutoExport, self.cbAutoExport)
		
		self.cbAutoEnqueue = wx.CheckBox(self, wx.ID_ANY, "Automatically enqueue the file when exporting")
		self.cbAutoEnqueue.SetValue(self.settings.autoenqueue)
		self.Bind(wx.EVT_CHECKBOX, self.onCbAutoEnqueue, self.cbAutoEnqueue)
		
		self.strategyList = ['column', 'row', 'spiral']
		self.rbStrategy = wx.RadioBox(
				self, -1, "Arrange Strategy", wx.DefaultPosition, wx.DefaultSize,
				self.strategyList, 1, wx.RA_SPECIFY_COLS
				)
		
		self.Bind(wx.EVT_RADIOBOX, self.onStrategy, self.rbStrategy)
		self.rbStrategy.SetToolTip("Choose the strategy for auto-arrange")
		if not self.settings.arrangestrategy in self.strategyList:
			self.settings.arrangestrategy = self.strategyList[0]
		self.rbStrategy.SetStringSelection(self.settings.arrangestrategy)
		
		self.scMargin = wx.SpinCtrl(self, wx.ID_ANY, "Arrange Margin", size=(40, -1))
		self.scMargin.SetRange(1,5)
		self.scMargin.SetValue(self.settings.arrangemargin)
		self.scMargin.SetToolTip("Choose the distance between objects")
		self.Bind(wx.EVT_SPINCTRL, self.onScMargin, self.scMargin)

		
		szFrame = wx.BoxSizer(wx.HORIZONTAL)
		szLeft = wx.BoxSizer(wx.VERTICAL)
		
		szCanvas = wx.BoxSizer(wx.VERTICAL)
		szCanvas.AddSpacer(20)
		szCanvas.Add(self.stlCanvas)
		szCanvas.AddSpacer(10)
		
		szLeft.Add(szCanvas)
		szRight = wx.BoxSizer(wx.VERTICAL)
		
		self.files = FileList(self)
		self.Bind(wx.EVT_LISTBOX, self.doFileSelect, self.files)
		szRight.AddSpacer(20)
		szRight.Add(self.files)
		szRight.AddSpacer(10)
		
		szBtn = wx.BoxSizer(wx.VERTICAL)
		szBtnLn1 = wx.BoxSizer(wx.HORIZONTAL)
		szBtnLn2 = wx.BoxSizer(wx.HORIZONTAL)
		szBtnLn3 = wx.BoxSizer(wx.HORIZONTAL)
		szOptions = wx.BoxSizer(wx.HORIZONTAL)
		szOptionsL = wx.BoxSizer(wx.VERTICAL)
		szOptionsR = wx.BoxSizer(wx.VERTICAL)
		
		szBtnLn1.Add(self.bAdd)
		szBtnLn1.Add(self.bDel)
		szBtnLn1.Add(self.bDelall)
		szBtnLn1.AddSpacer(BUTTONDIM[0])
		szBtnLn1.Add(self.bView)
		szBtnLn1.Add(self.bViewPlate)
		szBtnLn1.AddSpacer(BUTTONDIM[0])
		
		szBtnLn2.Add(self.bArrange)
		szBtnLn2.Add(self.bCenter)
		szBtnLn2.AddSpacer(BUTTONDIM[0])
		szBtnLn2.Add(self.bClone)
		szBtnLn2.Add(self.bGrid)
		szBtnLn2.AddSpacer(BUTTONDIM[0])
		szBtnLn2.Add(self.bSplit)
		szBtnLn2.AddSpacer(BUTTONDIM[0])
		
		szBtnLn3.Add(self.bMirror)
		szBtnLn3.Add(self.bRotate)
		szBtnLn3.Add(self.bTranslate)
		szBtnLn3.Add(self.bScale)
		szBtnLn3.AddSpacer(BUTTONDIM[0])
		szBtnLn3.Add(self.bSaveAs)
		szBtnLn3.Add(self.bExport)
		
		szBtn.Add(szBtnLn1)
		szBtn.Add(szBtnLn2)
		szBtn.Add(szBtnLn3)
		
		szRight.Add(szBtn, 1, wx.ALIGN_CENTER_HORIZONTAL)
		szRight.AddSpacer(5)
		
		szOptionsL.Add(self.cbPreview)
		szOptionsL.AddSpacer(5)
		szOptionsL.Add(self.cbCenterOnArrange)
		szOptionsL.AddSpacer(10)
		szOptionsL.Add(self.cbAutoExport)
		szOptionsL.AddSpacer(5)
		szOptionsL.Add(self.cbAutoEnqueue)

		szOptionsR.Add(self.rbStrategy)
		szOptionsR.AddSpacer(5)
		szMargin = wx.BoxSizer(wx.HORIZONTAL)
		szMargin.Add(wx.StaticText(self, wx.ID_ANY, "Arrange margin:"))
		szMargin.AddSpacer(5)
		szMargin.Add(self.scMargin)
		szOptionsR.Add(szMargin)
		
		szOptions.AddSpacer(10)
		szOptions.Add(szOptionsL)
		szOptions.AddSpacer(30)
		szOptions.Add(szOptionsR)
		szOptions.AddSpacer(10)
		
		szRight.Add(szOptions, 1, wx.ALIGN_CENTER_HORIZONTAL)
		#szRight.AddSpacer(10)

		szFrame.AddSpacer(20)		
		szFrame.Add(szLeft)
		szFrame.AddSpacer(20)		
		szFrame.Add(szRight)
		szFrame.AddSpacer(20)		
		
		self.SetSizer(szFrame)
		self.Layout()
		self.Fit()
예제 #3
0
파일: plater.py 프로젝트: jbernardis/plater
class App(Frame):
	def __init__(self, master, settings):
		self.master = master
		self.settings = settings
		Frame.__init__(self, master, relief=SUNKEN, bd=2)
		self.master.protocol('WM_DELETE_WINDOW', self.quitApp)
		self.objNumber = 1
		
		self.modified = False
		self.preview = True
		
		self.lbMap = []
		self.lbSelection = None
		
		self.stlFrame =  StlFrame(self, scale=settings.scale)
		self.stlFrame.grid(row=1, column=1, rowspan=4, sticky=N)
		self.lbFrame = LabelFrame(self, text=" File List ")
		self.lbFrame.grid(row=1, column=2, padx=5, pady=5, sticky=N)
		self.bFrame = Frame(self)
		self.bFrame.grid(row=2, column=2, padx=5, sticky=N)
		
		self.lbFiles = Listbox(self.lbFrame, width=60)
		self.lbFiles.grid(row=1,column=1,sticky=N+E+W+S)
		self.sbFiles = Scrollbar(self.lbFrame, command=self.lbFiles.yview, orient="vertical")
		self.sbFiles.grid(column=2, row=1, sticky=N+S+E)
		self.lbFiles.config(yscrollcommand=self.sbFiles.set)
		self.lbFiles.bind("<ButtonRelease>", self.doClickList)
		
		self.imgAdd = self.loadImage("add.gif")
		self.bAdd = Button(self.bFrame, image=self.imgAdd, width=48, height=48, command=self.doAdd)
		self.bAdd.grid(row=1, column=1, pady=2, padx=2, sticky=N)
		createToolTip(self.bAdd, "Add an STL file to the plate")

		self.imgClone = self.loadImage("clone.gif")		
		self.bClone = Button(self.bFrame, image=self.imgClone, width=48, height=48, command=self.doClone, state=DISABLED)
		self.bClone.grid(row=1, column=2, pady=2, padx=2, sticky=N)
		createToolTip(self.bClone, "Clone the selected object")
		
		self.imgDel = self.loadImage("del.gif")
		self.bDel = Button(self.bFrame, image=self.imgDel, width=48, height=48, command=self.doDelete, state=DISABLED)
		self.bDel.grid(row=1, column=3, pady=2, padx=2, sticky=N)
		createToolTip(self.bDel, "Delete the selected object")
		
		self.imgDelAll = self.loadImage("delall.gif")
		self.bDelAll = Button(self.bFrame, image=self.imgDelAll, width=48, height=48, command=self.doDelAll, state=DISABLED)
		self.bDelAll.grid(row=1, column=4, pady=2, padx=2, sticky=N)
		createToolTip(self.bDelAll, "Delete all objects from the plate")
		
		self.imgArrange = self.loadImage("arrange.gif")
		self.bArrange = Button(self.bFrame, image=self.imgArrange, width=48, height=48, command=self.doArrange, state=DISABLED)
		self.bArrange.grid(row=2, column=1, pady=2, padx=2, sticky=N)
		createToolTip(self.bArrange, "Arrange the objects to fit")
		
		self.imgExport = self.loadImage("export.gif")
		self.bExport = Button(self.bFrame, image=self.imgExport, width=48, height=48, command=self.doExport, state=DISABLED)
		self.bExport.grid(row=2, column=4, pady=2, padx=2, sticky=N)
		createToolTip(self.bExport, "Save the plate to an STL file")
		
		self.cbPrev = IntVar()
		if self.settings.preview:
			self.cbPrev.set(1)
		else:
			self.cbPrev.set(0)
			
		self.cbPreview = Checkbutton(
            self.bFrame, text="Preview",
            variable=self.cbPrev,
            command=self.doCbPreview)
		self.cbPreview.grid(row=3, column=2, pady=5)

	def doCbPreview(self):
		if self.cbPrev.get() == 0:
			self.preview = False
		else:
			self.preview = True	
			
		self.settings.preview = self.preview
		self.settings.setModified()
	
	def loadImage(self, fn):
		return PhotoImage(file=os.path.join(cmd_folder, "images", fn))

	def checkModified(self):
		if self.modified:
			if not askyesno("Exit?", "Lose unsaved changes?"):
				return True

		return False
		
	def quitApp(self):
		if self.checkModified():
			return False
		
		self.destroy()
		self.master.quit()
		return True
	
	def doAdd(self):
		while True:
			confirmed = False
			fn = askopenfilename(filetypes=[("STL files", "*.stl")], initialdir=self.settings.lastdirectory)
			if fn:
				if self.preview:
					subprocess.Popen([self.settings.viewer, fn])
					if askyesno("Confirm Add", "Add this object?"):
						confirmed = True
				else:
					confirmed = True
			else:
				return
					
			if confirmed:
				self.modified = True
				self.filename = fn
				dn = os.path.dirname(fn)
				if dn != self.settings.lastdirectory:
					self.settings.lastdirectory = dn
					self.settings.setModified()
				
				name = "OBJECT%03d" % self.objNumber
				index = self.lbFiles.index(END)
				self.lbFiles.insert(END, "%2d: %s" % (self.objNumber, fn))
				if self.lbSelection is not None:
					self.lbFiles.selection_clear(self.lbSelection)
				self.lbFiles.selection_set(index)
				self.lbSelection = index
				self.objNumber += 1
				stlFile = stltool.stl(filename=fn, name=name)
				self.lbMap.append(stlFile)
				self.stlFrame.addStl(stlFile, highlight=True)
				self.bDel.config(state=NORMAL)
				self.bDelAll.config(state=NORMAL)
				self.bExport.config(state=NORMAL)
				self.bArrange.config(state=NORMAL)
				self.bClone.config(state=NORMAL)
				return
			
	def doClone(self):
		stlObj = self.stlFrame.getSelectedStl()
		if stlObj is None:
			return
		
		name = "OBJECT%03d" % self.objNumber
		index = self.lbFiles.index(END)
		self.lbFiles.insert(END, "%2d: %s" % (self.objNumber, stlObj.filename))
		if self.lbSelection is not None:
			self.lbFiles.selection_clear(self.lbSelection)
		self.lbFiles.selection_set(index)
		self.lbSelection = index
		self.objNumber += 1
		s = stlObj.clone(name=name)
		self.lbMap.append(s)
		self.stlFrame.addStl(s, highlight=True)
			
	def doDelete(self):
		self.modified = True
		if self.lbSelection is not None:
			del self.lbMap[self.lbSelection]
			self.lbFiles.delete(self.lbSelection)
			self.lbSelection = None
			
		self.stlFrame.delStl()
		if self.stlFrame.countObjects() == 0:
			self.modified = False
			self.bDel.config(state=DISABLED)
			self.bDelAll.config(state=DISABLED)
			self.bExport.config(state=DISABLED)
			self.bArrange.config(state=DISABLED)
			self.bClone.config(state=DISABLED)
			
	def doDelAll(self):
		if askyesno("Confirm Delete All", "Are you sure you want to delete all objects?"):
			self.modified = False
			self.stlFrame.delAll()
			self.bDel.config(state=DISABLED)
			self.bDelAll.config(state=DISABLED)
			self.bExport.config(state=DISABLED)
			self.bArrange.config(state=DISABLED)
			self.bClone.config(state=DISABLED)
			self.lbFiles.delete(0, END)
			self.lbMap = []
			self.lbSelection = None
			self.objNumber = 1
		
	def doExport(self):
		self.stlFrame.commit()
		objs = self.stlFrame.getStls()
		facets = []
		for o in objs:
			facets.extend(o.facets)
			
		fn = asksaveasfilename(parent=self, title="Export to File", filetypes=[("STL files", "*.stl")], initialdir=self.settings.lastdirectory, defaultextension=".stl")
		if not fn:
			return
		
		stltool.emitstl(fn, facets=facets, objname="PlaterObject", binary=False)
		self.modified = False
		
	def setModified(self, flag=True):
		self.modified = flag
		
	def setSelection(self, stlObj):
		found = False
		for i in range(len(self.lbMap)):
			o = self.lbMap[i]
			if o.name == stlObj.name:
				found = True
				break
			
		if not found:
			return
		
		if self.lbSelection is not None:
			self.lbFiles.selection_clear(self.lbSelection)
		self.lbFiles.selection_set(i)
		self.lbSelection = i
		
	def doClickList(self, *arg):
		items = self.lbFiles.curselection()
		try:
			i = int(items[0])
		except:
			return
		
		if i < 0 or i >= len(self.lbMap):
			return	
		
		self.stlFrame.setSelectionByName(self.lbMap[i].name)
		
	def doArrange(self):
		self.stlFrame.arrange()
예제 #4
0
class PlaterDlg(wx.Frame):
	def __init__(self, parent):
		wx.Frame.__init__(self, None, wx.ID_ANY, 'Plater', size=(600, 600))
		self.parent = parent
		self.log = self.parent.log
		self.t = 0
		self.seq = 0
		self.modified = False
		self.settings = Settings(cmdFolder)
		self.Show()
		ico = wx.Icon(os.path.join(cmdFolder, "images", "platerico.png"), wx.BITMAP_TYPE_PNG)
		self.SetIcon(ico)
		
		self.savedfile = None
		
		self.Bind(wx.EVT_CLOSE, self.onClose)
		self.Bind(EVT_SPLIT_UPDATE, self.splitUpdate)
		
		self.images = Images(os.path.join(cmdFolder, "images"))
		
		self.stlCanvas = StlFrame(self, self.settings)
		
		self.bAdd = wx.BitmapButton(self, wx.ID_ANY, self.images.pngAdd, size=BUTTONDIM)
		self.bAdd.SetToolTip("Add an STL file to the plate")
		self.Bind(wx.EVT_BUTTON, self.doAdd, self.bAdd)
		
		self.bClone = wx.BitmapButton(self, wx.ID_ANY, self.images.pngClone, size=BUTTONDIM)
		self.bClone.SetToolTip("Add a copy of the selected object")
		self.Bind(wx.EVT_BUTTON, self.doClone, self.bClone)
		self.bClone.Enable(False)
		
		self.bDel = wx.BitmapButton(self, wx.ID_ANY, self.images.pngDel, size=BUTTONDIM)
		self.bDel.SetToolTip("Delete the selected object from the plate")
		self.Bind(wx.EVT_BUTTON, self.doDel, self.bDel)
		self.bDel.Enable(False)
		
		self.bDelall = wx.BitmapButton(self, wx.ID_ANY, self.images.pngDelall, size=BUTTONDIM)
		self.bDelall.SetToolTip("Delete ALL objects from the plate")
		self.Bind(wx.EVT_BUTTON, self.doDelall, self.bDelall)
		self.bDelall.Enable(False)
		
		self.bArrange = wx.BitmapButton(self, wx.ID_ANY, self.images.pngArrange, size=BUTTONDIM)
		self.bArrange.SetToolTip("Arrange the objects on the plate")
		self.Bind(wx.EVT_BUTTON, self.doArrange, self.bArrange)
		self.bArrange.Enable(False)
		
		self.bCenter = wx.BitmapButton(self, wx.ID_ANY, self.images.pngCenter, size=BUTTONDIM)
		self.bCenter.SetToolTip("Center the plate")
		self.Bind(wx.EVT_BUTTON, self.doCenter, self.bCenter)
		self.bCenter.Enable(False)
		
		self.bGrid = wx.BitmapButton(self, wx.ID_ANY, self.images.pngGrid, size=BUTTONDIM)
		self.bGrid.SetToolTip("Create a grid of the selected object")
		self.Bind(wx.EVT_BUTTON, self.doGrid, self.bGrid)
		self.bGrid.Enable(False)
		
		self.bMirror = wx.BitmapButton(self, wx.ID_ANY, self.images.pngMirror, size=BUTTONDIM)
		self.bMirror.SetToolTip("Mirror the selected object")
		self.Bind(wx.EVT_BUTTON, self.doMirror, self.bMirror)
		self.bMirror.Enable(False)
		
		self.bRotate = wx.BitmapButton(self, wx.ID_ANY, self.images.pngRotate, size=BUTTONDIM)
		self.bRotate.SetToolTip("Rotate the selected object")
		self.Bind(wx.EVT_BUTTON, self.doRotate, self.bRotate)
		self.bRotate.Enable(False)
		
		self.bTranslate = wx.BitmapButton(self, wx.ID_ANY, self.images.pngTranslate, size=BUTTONDIM)
		self.bTranslate.SetToolTip("Translate the selected object")
		self.Bind(wx.EVT_BUTTON, self.doTranslate, self.bTranslate)
		self.bTranslate.Enable(False)
		
		self.bScale = wx.BitmapButton(self, wx.ID_ANY, self.images.pngScale, size=BUTTONDIM)
		self.bScale.SetToolTip("Scale the selected object")
		self.Bind(wx.EVT_BUTTON, self.doScale, self.bScale)
		self.bScale.Enable(False)
		
		self.bSplit = wx.BitmapButton(self, wx.ID_ANY, self.images.pngSplit, size=BUTTONDIM)
		self.bSplit.SetToolTip("Split the selected object")
		self.Bind(wx.EVT_BUTTON, self.doSplit, self.bSplit)
		self.bSplit.Enable(False)
		
		self.bSaveAs = wx.BitmapButton(self, wx.ID_ANY, self.images.pngFilesaveas, size=BUTTONDIM)
		self.bSaveAs.SetToolTip("Save the plate to an STL file")
		self.Bind(wx.EVT_BUTTON, self.doSaveAs, self.bSaveAs)
		self.bSaveAs.Enable(False)
		
		self.bExport = wx.BitmapButton(self, wx.ID_ANY, self.images.pngExport, size=BUTTONDIM)
		self.bExport.SetToolTip("Export the plate to the toolbox")
		self.Bind(wx.EVT_BUTTON, self.doExport, self.bExport)
		self.bExport.Enable(False)
		
		self.bView = wx.BitmapButton(self, wx.ID_ANY, self.images.pngView, size=BUTTONDIM)
		self.bView.SetToolTip("View the currently selected object")
		self.Bind(wx.EVT_BUTTON, self.doView, self.bView)
		self.bView.Enable(False)
		
		self.bViewPlate = wx.BitmapButton(self, wx.ID_ANY, self.images.pngViewplate, size=BUTTONDIM)
		self.bViewPlate.SetToolTip("View the entire plate")
		self.Bind(wx.EVT_BUTTON, self.doViewPlate, self.bViewPlate)
		self.bViewPlate.Enable(False)
		
		self.cbPreview = wx.CheckBox(self, wx.ID_ANY, "3D Preview")
		self.cbPreview.SetValue(self.settings.preview)
		self.Bind(wx.EVT_CHECKBOX, self.onCbPreview, self.cbPreview)
		
		self.cbCenterOnArrange = wx.CheckBox(self, wx.ID_ANY, "Center Plate After Arrange")
		self.cbCenterOnArrange.SetValue(self.settings.centeronarrange)
		self.Bind(wx.EVT_CHECKBOX, self.onCbCenterOnArrange, self.cbCenterOnArrange)
		
		self.cbAutoExport = wx.CheckBox(self, wx.ID_ANY, "Automatically export when saving")
		self.cbAutoExport.SetValue(self.settings.autoexport)
		self.Bind(wx.EVT_CHECKBOX, self.onCbAutoExport, self.cbAutoExport)
		
		self.cbAutoEnqueue = wx.CheckBox(self, wx.ID_ANY, "Automatically enqueue the file when exporting")
		self.cbAutoEnqueue.SetValue(self.settings.autoenqueue)
		self.Bind(wx.EVT_CHECKBOX, self.onCbAutoEnqueue, self.cbAutoEnqueue)
		
		self.strategyList = ['column', 'row', 'spiral']
		self.rbStrategy = wx.RadioBox(
				self, -1, "Arrange Strategy", wx.DefaultPosition, wx.DefaultSize,
				self.strategyList, 1, wx.RA_SPECIFY_COLS
				)
		
		self.Bind(wx.EVT_RADIOBOX, self.onStrategy, self.rbStrategy)
		self.rbStrategy.SetToolTip("Choose the strategy for auto-arrange")
		if not self.settings.arrangestrategy in self.strategyList:
			self.settings.arrangestrategy = self.strategyList[0]
		self.rbStrategy.SetStringSelection(self.settings.arrangestrategy)
		
		self.scMargin = wx.SpinCtrl(self, wx.ID_ANY, "Arrange Margin", size=(40, -1))
		self.scMargin.SetRange(1,5)
		self.scMargin.SetValue(self.settings.arrangemargin)
		self.scMargin.SetToolTip("Choose the distance between objects")
		self.Bind(wx.EVT_SPINCTRL, self.onScMargin, self.scMargin)

		
		szFrame = wx.BoxSizer(wx.HORIZONTAL)
		szLeft = wx.BoxSizer(wx.VERTICAL)
		
		szCanvas = wx.BoxSizer(wx.VERTICAL)
		szCanvas.AddSpacer(20)
		szCanvas.Add(self.stlCanvas)
		szCanvas.AddSpacer(10)
		
		szLeft.Add(szCanvas)
		szRight = wx.BoxSizer(wx.VERTICAL)
		
		self.files = FileList(self)
		self.Bind(wx.EVT_LISTBOX, self.doFileSelect, self.files)
		szRight.AddSpacer(20)
		szRight.Add(self.files)
		szRight.AddSpacer(10)
		
		szBtn = wx.BoxSizer(wx.VERTICAL)
		szBtnLn1 = wx.BoxSizer(wx.HORIZONTAL)
		szBtnLn2 = wx.BoxSizer(wx.HORIZONTAL)
		szBtnLn3 = wx.BoxSizer(wx.HORIZONTAL)
		szOptions = wx.BoxSizer(wx.HORIZONTAL)
		szOptionsL = wx.BoxSizer(wx.VERTICAL)
		szOptionsR = wx.BoxSizer(wx.VERTICAL)
		
		szBtnLn1.Add(self.bAdd)
		szBtnLn1.Add(self.bDel)
		szBtnLn1.Add(self.bDelall)
		szBtnLn1.AddSpacer(BUTTONDIM[0])
		szBtnLn1.Add(self.bView)
		szBtnLn1.Add(self.bViewPlate)
		szBtnLn1.AddSpacer(BUTTONDIM[0])
		
		szBtnLn2.Add(self.bArrange)
		szBtnLn2.Add(self.bCenter)
		szBtnLn2.AddSpacer(BUTTONDIM[0])
		szBtnLn2.Add(self.bClone)
		szBtnLn2.Add(self.bGrid)
		szBtnLn2.AddSpacer(BUTTONDIM[0])
		szBtnLn2.Add(self.bSplit)
		szBtnLn2.AddSpacer(BUTTONDIM[0])
		
		szBtnLn3.Add(self.bMirror)
		szBtnLn3.Add(self.bRotate)
		szBtnLn3.Add(self.bTranslate)
		szBtnLn3.Add(self.bScale)
		szBtnLn3.AddSpacer(BUTTONDIM[0])
		szBtnLn3.Add(self.bSaveAs)
		szBtnLn3.Add(self.bExport)
		
		szBtn.Add(szBtnLn1)
		szBtn.Add(szBtnLn2)
		szBtn.Add(szBtnLn3)
		
		szRight.Add(szBtn, 1, wx.ALIGN_CENTER_HORIZONTAL)
		szRight.AddSpacer(5)
		
		szOptionsL.Add(self.cbPreview)
		szOptionsL.AddSpacer(5)
		szOptionsL.Add(self.cbCenterOnArrange)
		szOptionsL.AddSpacer(10)
		szOptionsL.Add(self.cbAutoExport)
		szOptionsL.AddSpacer(5)
		szOptionsL.Add(self.cbAutoEnqueue)

		szOptionsR.Add(self.rbStrategy)
		szOptionsR.AddSpacer(5)
		szMargin = wx.BoxSizer(wx.HORIZONTAL)
		szMargin.Add(wx.StaticText(self, wx.ID_ANY, "Arrange margin:"))
		szMargin.AddSpacer(5)
		szMargin.Add(self.scMargin)
		szOptionsR.Add(szMargin)
		
		szOptions.AddSpacer(10)
		szOptions.Add(szOptionsL)
		szOptions.AddSpacer(30)
		szOptions.Add(szOptionsR)
		szOptions.AddSpacer(10)
		
		szRight.Add(szOptions, 1, wx.ALIGN_CENTER_HORIZONTAL)
		#szRight.AddSpacer(10)

		szFrame.AddSpacer(20)		
		szFrame.Add(szLeft)
		szFrame.AddSpacer(20)		
		szFrame.Add(szRight)
		szFrame.AddSpacer(20)		
		
		self.SetSizer(szFrame)
		self.Layout()
		self.Fit()
		
	def enableButtons(self):
		v = (self.files.countFiles() > 0)
		ud = self.files.getSelection()
		
		self.bAdd.Enable(True)
		self.bClone.Enable(v and not ud is None)
		self.bGrid.Enable(v and not ud is None)
		self.bDel.Enable(v and not ud is None)
		self.bView.Enable(v and not ud is None)
		self.bMirror.Enable(v and not ud is None)
		self.bRotate.Enable(v and not ud is None)
		self.bTranslate.Enable(v and not ud is None)
		self.bScale.Enable(v and not ud is None)
		self.bSplit.Enable(v and not ud is None)
		self.bDelall.Enable(v)
		self.bArrange.Enable(v)
		self.bCenter.Enable(v)
		self.bSaveAs.Enable(v)
		self.bExport.Enable(v and not self.settings.autoexport and self.savedfile is not None)
		self.bViewPlate.Enable(v)
		
	def disableButtons(self):
		self.bAdd.Enable(False)
		self.bClone.Enable(False)
		self.bGrid.Enable(False)
		self.bDel.Enable(False)
		self.bView.Enable(False)
		self.bMirror.Enable(False)
		self.bRotate.Enable(False)
		self.bTranslate.Enable(False)
		self.bScale.Enable(False)
		self.bSplit.Enable(False)
		self.bDelall.Enable(False)
		self.bArrange.Enable(False)
		self.bCenter.Enable(False)
		self.bSaveAs.Enable(False)
		self.bExport.Enable(False)
		self.bViewPlate.Enable(False)
		
	def onScMargin(self, evt):
		self.settings.arrangemargin = self.scMargin.GetValue()

	def onCbPreview(self, evt):
		self.settings.preview = self.cbPreview.GetValue()
		
	def onStrategy(self, evt):
		self.settings.arrangestrategy = self.strategyList[evt.GetInt()]

	def onCbCenterOnArrange(self, evt):
		self.settings.centeronarrange = self.cbCenterOnArrange.GetValue()

	def doAdd(self, evt):
		wildcard = "STL (*.stl)|*.stl;*.STL|"	 \
			"All files (*.*)|*.*"
			
		dlg = wx.FileDialog(
			self, message="Choose an STL 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.settings.lastdirectory = os.path.dirname(path)
		
		if self.settings.preview:
			stlObj = stl(filename = path)
			dlg = StlViewer(self, stlObj, path, True, self.images, self.settings)
			rc = dlg.ShowModal()
			dlg.Destroy()
			dlg = None
		
		if not self.settings.preview or rc == wx.ID_OK:
			ud = UserData(path, stlObj, self.seq)
			self.files.addFile(ud)
			self.stlCanvas.addHull(stlObj, self.seq)
			self.seq += 1
			self.modified = True
			self.enableButtons()
			
	def setFilesSelection(self, seq):
		self.files.setSelection(seq)
		self.enableButtons()
		
	def setHullSelection(self, seq):
		self.stlCanvas.setSelection(seq)
		self.enableButtons()
		
	def doClone(self, evt):
		self.clone()
		
	def clone(self):
		self.stlCanvas.commitDeltas(None)
		ud = self.files.getSelection()
		if ud is None:
			return None
		
		mySeq = self.seq
		self.seq += 1
		udNew = ud.clone(mySeq)
		
		self.files.addFile(udNew)
		self.stlCanvas.addHull(udNew.getStlObj(), mySeq)
			
		self.modified = True
		self.enableButtons()
		return mySeq
		
	def doDel(self, evt):
		fx = self.files.getSelectionIndex()
		if fx == wx.NOT_FOUND:
			return
		
		ud = self.files.getUserDataByIndex(fx)
		if not ud is None:
			self.stlCanvas.delHull(ud.getSeqNbr())
			
		self.files.delFileByIndex(fx)
		if self.files.countFiles() > 0:
			self.modified = True
		else:
			self.modified = False
			self.seq = 0

		self.enableButtons()
		
	def doDelall(self, evt):
		dlg = wx.MessageDialog(self,
			"This will delete ALL items.\nAre you sure you want to do this?",
			"Confirm Delete All",
			wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
		rc = dlg.ShowModal()
		dlg.Destroy()
		if rc == wx.ID_YES:
			self.modified = False
			self.files.delAll()
			self.stlCanvas.delAllHulls()
			self.seq = 0
			self.enableButtons()
		
	def doArrange(self, evt):
		self.stlCanvas.arrange()
		self.modified = True
		
	def findUserDataBySeq(self, seq):
		return self.files.findUserDataBySeq(seq)
		
	def doMirror(self, evt):
		dlg = MirrorDlg(self, self.stlCanvas, self.images, wx.GetMousePosition())
		dlg.ShowModal()
		dlg.Destroy()
			
	def doRotate(self, evt):
		dlg = RotateDlg(self, self.stlCanvas, self.images, wx.GetMousePosition())
		dlg.ShowModal()
		dlg.Destroy()
			
	def doTranslate(self, evt):
		dlg = TranslateDlg(self, self.stlCanvas, self.images, wx.GetMousePosition())
		dlg.ShowModal()
		dlg.Destroy()
			
	def doScale(self, evt):
		dlg = ScaleDlg(self, self.stlCanvas, self.images, wx.GetMousePosition())
		dlg.ShowModal()
		dlg.Destroy()
		
	def doCenter(self, evt):
		self.stlCanvas.centerPlate()
		self.modified = True
		
	def doGrid(self, evt):
		ud = self.files.getSelection()
		if ud is None:
			return
		
		masterSeq = ud.getSeqNbr()
		dlg = GridParamsDlg(self, self.images, wx.GetMousePosition())
		rc = dlg.ShowModal()
		if rc == wx.ID_OK:
			rows, cols = dlg.getValues()
			
		dlg.Destroy()
		if rc != wx.ID_OK:
			return
		
		copies = rows * cols - 1
		if copies == 1:
			return
		
		self.modified = True
		
		seqNbrs = [masterSeq]
		for i in range(copies):
			seqNbrs.append(self.clone())
			
		self.stlCanvas.gridArrange(seqNbrs, rows, cols)
		
	def doSplit(self, evt):
		ud = self.files.getSelection()
		if ud is None:
			return
	
		self.disableButtons()	
		obj = ud.getStlObj()
		self.part = 0
		self.partfn = ud.getFn()
		self.splitter = SplitThread(self, obj)
		self.splitter.Start()
		
	def splitUpdate(self, evt):
		finished = False
		if evt.state == SPLIT_LAST_OBJECT:
			finished = True
			if self.part == 0:
				dlg = wx.MessageDialog(self,
					"Object consists of a single mesh",
					"Cannot Split",
					wx.OK | wx.ICON_EXCLAMATION)
				dlg.ShowModal()
				dlg.Destroy()
				self.splitter = None
				self.enableButtons()
				return
			
		nf = evt.facets
		if self.part == 0:
			ud = self.files.getSelection()
			ud.getStlObj().setFacets(nf)
			ud.setPart(self.part)
			self.files.refreshFilesList(ud.getSeqNbr())
			self.stlCanvas.refreshHull(ud.getSeqNbr())
		else:
			nobj = stl(filename=None)
			nobj.setFacets(nf)
			ud = UserData(self.partfn, nobj, self.seq)
			ud.setPart(self.part)
			self.files.addFile(ud)
			self.stlCanvas.addHull(nobj, self.seq)
			self.seq += 1
			
		self.part += 1
		
		if finished:
			self.modified = True
			self.splitter = None
			self.enableButtons()
		else:
			self.disableButtons()
			
	def onCbAutoEnqueue(self, evt):
		self.settings.autoenqueue = self.cbAutoEnqueue.GetValue()
		
	def onCbAutoExport(self, evt):
		self.settings.autoexport = self.cbAutoExport.GetValue()
		self.bExport.Enable(not self.settings.autoexport and self.savedfile is not None)

	def doExport(self, evt):
		self.parent.exportStlFile(self.savedfile, True, self.settings.autoenqueue)
		
	def doSaveAs(self, evt):
		wildcard = "STL (*.stl)|*.stl;*.STL"
		dlg = wx.FileDialog(
			self, message="Save file as ...", defaultDir=self.settings.lastdirectory, 
			defaultFile="", wildcard=wildcard, style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
			)
		
		rc = dlg.ShowModal()
		if rc == wx.ID_OK:
			path = dlg.GetPath().encode('ascii','ignore')
			if os.path.splitext(path)[1].lower() != ".stl":
				path += ".stl"
			
		dlg.Destroy()
		if rc != wx.ID_OK:
			return
		
		self.stlCanvas.commitDeltas(None)
		objs = self.files.getStlObjects()
		facets = []
		for o in objs:
			facets.extend(o.facets)
			
		self.savedfile = path
		
		emitstl(path, facets=facets, objname="PlaterObject", binary=False)
		self.modified = False
		
		dlg = wx.MessageDialog(self,
			"File '%s' written" % path,
			"Save",
			wx.OK | wx.ICON_INFORMATION)
		
		self.parent.exportStlFile(self.savedfile, self.settings.autoexport, self.settings.autoenqueue)

		dlg.ShowModal()
		dlg.Destroy()
		self.enableButtons()

	def doView(self, evt):
		self.viewObject()
		
	def viewObject(self):
		self.stlCanvas.commitDeltas(None)
		ud = self.files.getSelection()
		dlg = StlViewer(self, ud.getStlObj(), ud.getName(), False, self.images, self.settings)
		dlg.ShowModal()
		dlg.Destroy()
		
	def doViewPlate(self, evt):
		self.stlCanvas.commitDeltas(None)
		objs = self.files.getStlObjects()
		
		plateStl = StlProxy()
		plateStl.facets = []
		for o in objs:
			plateStl.facets.extend(o.facets)
		dlg = StlViewer(self, plateStl, "Plate", False, self.images, self.settings)
		dlg.ShowModal()
		dlg.Destroy()

		
	def doFileSelect(self, evt):
		self.enableButtons()
			
	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.platerClosed()
		else:
			self.parent.platerClosed()