1 from __future__ import absolute_import
7 from Cura.gui import configBase
8 from Cura.gui import expertConfig
9 from Cura.gui import preview3d
10 from Cura.gui import sliceProgessPanel
11 from Cura.gui import alterationPanel
12 from Cura.gui import pluginPanel
13 from Cura.gui import preferencesDialog
14 from Cura.gui import configWizard
15 from Cura.gui import firmwareInstall
16 from Cura.gui import printWindow
17 from Cura.gui import simpleMode
18 from Cura.gui import projectPlanner
19 from Cura.gui.tools import batchRun
20 from Cura.gui import flatSlicerWindow
21 from Cura.gui.util import dropTarget
22 from Cura.gui.tools import minecraftImport
23 from Cura.util import validators
24 from Cura.util import profile
25 from Cura.util import version
26 from Cura.util import sliceRun
27 from Cura.util import meshLoader
29 class mainWindow(wx.Frame):
31 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
33 self.extruderCount = int(profile.getPreference('extruder_amount'))
35 wx.EVT_CLOSE(self, self.OnClose)
37 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
39 self.normalModeOnlyItems = []
41 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
42 self.config = wx.FileConfig(appName="Cura",
43 localFilename=mruFile,
44 style=wx.CONFIG_USE_LOCAL_FILE)
46 self.ID_MRU_MODEL1, self.ID_MRU_MODEL2, self.ID_MRU_MODEL3, self.ID_MRU_MODEL4, self.ID_MRU_MODEL5, self.ID_MRU_MODEL6, self.ID_MRU_MODEL7, self.ID_MRU_MODEL8, self.ID_MRU_MODEL9, self.ID_MRU_MODEL10 = [wx.NewId() for line in xrange(10)]
47 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
48 self.config.SetPath("/ModelMRU")
49 self.modelFileHistory.Load(self.config)
51 self.ID_MRU_PROFILE1, self.ID_MRU_PROFILE2, self.ID_MRU_PROFILE3, self.ID_MRU_PROFILE4, self.ID_MRU_PROFILE5, self.ID_MRU_PROFILE6, self.ID_MRU_PROFILE7, self.ID_MRU_PROFILE8, self.ID_MRU_PROFILE9, self.ID_MRU_PROFILE10 = [wx.NewId() for line in xrange(10)]
52 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
53 self.config.SetPath("/ProfileMRU")
54 self.profileFileHistory.Load(self.config)
56 self.menubar = wx.MenuBar()
57 self.fileMenu = wx.Menu()
58 i = self.fileMenu.Append(-1, 'Load model file...\tCTRL+L')
59 self.Bind(wx.EVT_MENU, lambda e: self._showModelLoadDialog(1), i)
60 i = self.fileMenu.Append(-1, 'Prepare print...\tCTRL+R')
61 self.Bind(wx.EVT_MENU, self.OnSlice, i)
62 i = self.fileMenu.Append(-1, 'Print...\tCTRL+P')
63 self.Bind(wx.EVT_MENU, self.OnPrint, i)
65 self.fileMenu.AppendSeparator()
66 i = self.fileMenu.Append(-1, 'Open Profile...')
67 self.normalModeOnlyItems.append(i)
68 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
69 i = self.fileMenu.Append(-1, 'Save Profile...')
70 self.normalModeOnlyItems.append(i)
71 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
72 i = self.fileMenu.Append(-1, 'Load Profile from GCode...')
73 self.normalModeOnlyItems.append(i)
74 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
75 self.fileMenu.AppendSeparator()
76 i = self.fileMenu.Append(-1, 'Reset Profile to default')
77 self.normalModeOnlyItems.append(i)
78 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
80 self.fileMenu.AppendSeparator()
81 i = self.fileMenu.Append(-1, 'Preferences...\tCTRL+,')
82 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
83 self.fileMenu.AppendSeparator()
86 modelHistoryMenu = wx.Menu()
87 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Model Files", modelHistoryMenu)
88 self.modelFileHistory.UseMenu(modelHistoryMenu)
89 self.modelFileHistory.AddFilesToMenu()
90 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
93 profileHistoryMenu = wx.Menu()
94 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Profile Files", profileHistoryMenu)
95 self.profileFileHistory.UseMenu(profileHistoryMenu)
96 self.profileFileHistory.AddFilesToMenu()
97 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
99 self.fileMenu.AppendSeparator()
100 i = self.fileMenu.Append(wx.ID_EXIT, 'Quit')
101 self.Bind(wx.EVT_MENU, self.OnQuit, i)
102 self.menubar.Append(self.fileMenu, '&File')
104 toolsMenu = wx.Menu()
105 i = toolsMenu.Append(-1, 'Switch to quickprint...')
106 self.switchToQuickprintMenuItem = i
107 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
108 i = toolsMenu.Append(-1, 'Switch to full settings...')
109 self.switchToNormalMenuItem = i
110 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
111 toolsMenu.AppendSeparator()
112 i = toolsMenu.Append(-1, 'Batch run...')
113 self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
114 i = toolsMenu.Append(-1, 'Project planner...')
115 self.Bind(wx.EVT_MENU, self.OnProjectPlanner, i)
116 # i = toolsMenu.Append(-1, 'Open SVG (2D) slicer...')
117 # self.Bind(wx.EVT_MENU, self.OnSVGSlicerOpen, i)
118 if minecraftImport.hasMinecraft():
119 i = toolsMenu.Append(-1, 'Minecraft import...')
120 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
121 self.menubar.Append(toolsMenu, 'Tools')
123 expertMenu = wx.Menu()
124 i = expertMenu.Append(-1, 'Open expert settings...')
125 self.normalModeOnlyItems.append(i)
126 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
127 expertMenu.AppendSeparator()
128 if firmwareInstall.getDefaultFirmware() is not None:
129 i = expertMenu.Append(-1, 'Install default Marlin firmware')
130 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
131 i = expertMenu.Append(-1, 'Install custom firmware')
132 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
133 expertMenu.AppendSeparator()
134 i = expertMenu.Append(-1, 'Run first run wizard...')
135 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
136 i = expertMenu.Append(-1, 'Run bed leveling wizard...')
137 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
138 self.menubar.Append(expertMenu, 'Expert')
141 i = helpMenu.Append(-1, 'Online documentation...')
142 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
143 i = helpMenu.Append(-1, 'Report a problem...')
144 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
145 i = helpMenu.Append(-1, 'Check for update...')
146 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
147 self.menubar.Append(helpMenu, 'Help')
148 self.SetMenuBar(self.menubar)
150 if profile.getPreference('lastFile') != '':
151 self.filelist = profile.getPreference('lastFile').split(';')
152 self.SetTitle('Cura - %s - %s' % (version.getVersion(), self.filelist[-1]))
155 self.progressPanelList = []
158 self.simpleSettingsPanel = simpleMode.simpleModePanel(self)
159 self.normalSettingsPanel = normalSettingsPanel(self)
162 self.preview3d = preview3d.previewPanel(self)
164 # load and slice buttons.
165 loadButton = wx.Button(self, -1, '&Load model')
166 sliceButton = wx.Button(self, -1, 'P&repare print')
167 printButton = wx.Button(self, -1, '&Print')
168 self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(1), loadButton)
169 self.Bind(wx.EVT_BUTTON, self.OnSlice, sliceButton)
170 self.Bind(wx.EVT_BUTTON, self.OnPrint, printButton)
172 if self.extruderCount > 1:
173 loadButton2 = wx.Button(self, -1, 'Load Dual')
174 self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(2), loadButton2)
175 if self.extruderCount > 2:
176 loadButton3 = wx.Button(self, -1, 'Load Triple')
177 self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(3), loadButton3)
178 if self.extruderCount > 3:
179 loadButton4 = wx.Button(self, -1, 'Load Quad')
180 self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(4), loadButton4)
182 #Also bind double clicking the 3D preview to load an STL file.
183 self.preview3d.glCanvas.Bind(wx.EVT_LEFT_DCLICK, lambda e: self._showModelLoadDialog(1), self.preview3d.glCanvas)
185 #Main sizer, to position the preview window, buttons and tab control
186 sizer = wx.GridBagSizer()
188 sizer.Add(self.preview3d, (0,1), span=(1,2+self.extruderCount), flag=wx.EXPAND)
189 sizer.AddGrowableCol(2 + self.extruderCount)
190 sizer.AddGrowableRow(0)
191 sizer.Add(loadButton, (1,1), flag=wx.RIGHT|wx.BOTTOM|wx.TOP, border=5)
192 if self.extruderCount > 1:
193 sizer.Add(loadButton2, (1,2), flag=wx.RIGHT|wx.BOTTOM|wx.TOP, border=5)
194 if self.extruderCount > 2:
195 sizer.Add(loadButton3, (1,3), flag=wx.RIGHT|wx.BOTTOM|wx.TOP, border=5)
196 if self.extruderCount > 3:
197 sizer.Add(loadButton4, (1,4), flag=wx.RIGHT|wx.BOTTOM|wx.TOP, border=5)
198 sizer.Add(sliceButton, (1,1+self.extruderCount), flag=wx.RIGHT|wx.BOTTOM|wx.TOP, border=5)
199 sizer.Add(printButton, (1,2+self.extruderCount), flag=wx.RIGHT|wx.BOTTOM|wx.TOP, border=5)
202 if len(self.filelist) > 0:
203 self.preview3d.loadModelFiles(self.filelist)
205 # Update the Model MRU
206 for idx in xrange(0, len(self.filelist)):
207 self.addToModelMRU(self.filelist[idx])
209 self.updateProfileToControls()
211 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
213 self.simpleSettingsPanel.Show(False)
214 self.normalSettingsPanel.Show(False)
215 self.updateSliceMode()
217 # Set default window size & position
218 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
221 # Restore the window position, size & state from the preferences file
223 if profile.getPreference('window_maximized') == 'True':
226 posx = int(profile.getPreference('window_pos_x'))
227 posy = int(profile.getPreference('window_pos_y'))
228 width = int(profile.getPreference('window_width'))
229 height = int(profile.getPreference('window_height'))
230 self.SetPosition((posx,posy))
231 self.SetSize((width,height))
237 def updateSliceMode(self):
238 isSimple = profile.getPreference('startMode') == 'Simple'
240 self.normalSettingsPanel.Show(not isSimple)
241 self.simpleSettingsPanel.Show(isSimple)
243 self.GetSizer().Detach(self.simpleSettingsPanel)
244 self.GetSizer().Detach(self.normalSettingsPanel)
246 self.GetSizer().Add(self.simpleSettingsPanel, (0,0), span=(1,1), flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=6)
248 self.GetSizer().Add(self.normalSettingsPanel, (0,0), span=(1,1), flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=6)
250 for i in self.normalModeOnlyItems:
251 i.Enable(not isSimple)
252 self.switchToQuickprintMenuItem.Enable(not isSimple)
253 self.switchToNormalMenuItem.Enable(isSimple)
255 self.normalSettingsPanel.Layout()
256 self.simpleSettingsPanel.Layout()
257 self.GetSizer().Layout()
260 def OnPreferences(self, e):
261 prefDialog = preferencesDialog.preferencesDialog(self)
263 prefDialog.Show(True)
265 def _showOpenDialog(self, title, wildcard = meshLoader.wildcardFilter()):
266 dlg=wx.FileDialog(self, title, os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
267 dlg.SetWildcard(wildcard)
268 if dlg.ShowModal() == wx.ID_OK:
269 filename = dlg.GetPath()
271 if not(os.path.exists(filename)):
273 profile.putPreference('lastFile', filename)
278 def _showModelLoadDialog(self, amount):
280 for i in xrange(0, amount):
281 filelist.append(self._showOpenDialog("Open file to print"))
282 if filelist[-1] == False:
284 self._loadModels(filelist)
286 def _loadModels(self, filelist):
287 self.filelist = filelist
288 self.SetTitle(filelist[-1] + ' - Cura - ' + version.getVersion())
289 profile.putPreference('lastFile', ';'.join(self.filelist))
290 self.preview3d.loadModelFiles(self.filelist, True)
291 self.preview3d.setViewMode("Normal")
293 # Update the Model MRU
294 for idx in xrange(0, len(self.filelist)):
295 self.addToModelMRU(self.filelist[idx])
297 def OnDropFiles(self, files):
298 self._loadModels(files)
300 def OnLoadModel(self, e):
301 self._showModelLoadDialog(1)
303 def OnLoadModel2(self, e):
304 self._showModelLoadDialog(2)
306 def OnLoadModel3(self, e):
307 self._showModelLoadDialog(3)
309 def OnLoadModel4(self, e):
310 self._showModelLoadDialog(4)
312 def OnSlice(self, e):
313 if len(self.filelist) < 1:
314 wx.MessageBox('You need to load a file before you can prepare it.', 'Print error', wx.OK | wx.ICON_INFORMATION)
316 isSimple = profile.getPreference('startMode') == 'Simple'
318 #save the current profile so we can put it back latter
319 oldProfile = profile.getGlobalProfileString()
320 self.simpleSettingsPanel.setupSlice()
321 #Create a progress panel and add it to the window. The progress panel will start the Skein operation.
322 spp = sliceProgessPanel.sliceProgessPanel(self, self, self.filelist)
323 self.sizer.Add(spp, (len(self.progressPanelList)+2,0), span=(1, 3 + self.extruderCount), flag=wx.EXPAND)
325 newSize = self.GetSize()
326 newSize.IncBy(0, spp.GetSize().GetHeight())
327 if newSize.GetWidth() < wx.GetDisplaySize()[0]:
328 self.SetSize(newSize)
329 self.progressPanelList.append(spp)
331 profile.loadGlobalProfileFromString(oldProfile)
333 def OnPrint(self, e):
334 if len(self.filelist) < 1:
335 wx.MessageBox('You need to load a file and prepare it before you can print.', 'Print error', wx.OK | wx.ICON_INFORMATION)
337 if not os.path.exists(sliceRun.getExportFilename(self.filelist[0])):
338 wx.MessageBox('You need to prepare a print before you can run the actual print.', 'Print error', wx.OK | wx.ICON_INFORMATION)
340 printWindow.printFile(sliceRun.getExportFilename(self.filelist[0]))
342 def OnModelMRU(self, e):
343 fileNum = e.GetId() - self.ID_MRU_MODEL1
344 path = self.modelFileHistory.GetHistoryFile(fileNum)
346 self.modelFileHistory.AddFileToHistory(path) # move up the list
347 self.config.SetPath("/ModelMRU")
348 self.modelFileHistory.Save(self.config)
352 self._loadModels(filelist)
354 def addToModelMRU(self, file):
355 self.modelFileHistory.AddFileToHistory(file)
356 self.config.SetPath("/ModelMRU")
357 self.modelFileHistory.Save(self.config)
360 def OnProfileMRU(self, e):
361 fileNum = e.GetId() - self.ID_MRU_PROFILE1
362 path = self.profileFileHistory.GetHistoryFile(fileNum)
364 self.profileFileHistory.AddFileToHistory(path) # move up the list
365 self.config.SetPath("/ProfileMRU")
366 self.profileFileHistory.Save(self.config)
369 profile.loadGlobalProfile(path)
370 self.updateProfileToControls()
372 def addToProfileMRU(self, file):
373 self.profileFileHistory.AddFileToHistory(file)
374 self.config.SetPath("/ProfileMRU")
375 self.profileFileHistory.Save(self.config)
378 def removeSliceProgress(self, spp):
379 self.progressPanelList.remove(spp)
380 newSize = self.GetSize()
381 newSize.IncBy(0, -spp.GetSize().GetHeight())
382 if newSize.GetWidth() < wx.GetDisplaySize()[0]:
383 self.SetSize(newSize)
385 self.sizer.Detach(spp)
386 for spp in self.progressPanelList:
387 self.sizer.Detach(spp)
389 for spp in self.progressPanelList:
390 self.sizer.Add(spp, (i,0), span=(1,3 + self.extruderCount), flag=wx.EXPAND)
394 def updateProfileToControls(self):
395 self.preview3d.updateProfileToControls()
396 self.normalSettingsPanel.updateProfileToControls()
397 self.simpleSettingsPanel.updateProfileToControls()
399 def OnLoadProfile(self, e):
400 dlg=wx.FileDialog(self, "Select profile file to load", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
401 dlg.SetWildcard("ini files (*.ini)|*.ini")
402 if dlg.ShowModal() == wx.ID_OK:
403 profileFile = dlg.GetPath()
404 profile.loadGlobalProfile(profileFile)
405 self.updateProfileToControls()
407 # Update the Profile MRU
408 self.addToProfileMRU(profileFile)
411 def OnLoadProfileFromGcode(self, e):
412 dlg=wx.FileDialog(self, "Select gcode file to load profile from", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
413 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
414 if dlg.ShowModal() == wx.ID_OK:
415 gcodeFile = dlg.GetPath()
416 f = open(gcodeFile, 'r')
419 if line.startswith(';CURA_PROFILE_STRING:'):
420 profile.loadGlobalProfileFromString(line[line.find(':')+1:].strip())
423 self.updateProfileToControls()
425 wx.MessageBox('No profile found in GCode file.\nThis feature only works with GCode files made by Cura 12.07 or newer.', 'Profile load error', wx.OK | wx.ICON_INFORMATION)
428 def OnSaveProfile(self, e):
429 dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
430 dlg.SetWildcard("ini files (*.ini)|*.ini")
431 if dlg.ShowModal() == wx.ID_OK:
432 profileFile = dlg.GetPath()
433 profile.saveGlobalProfile(profileFile)
436 def OnResetProfile(self, e):
437 dlg = wx.MessageDialog(self, 'This will reset all profile settings to defaults.\nUnless you have saved your current profile, all settings will be lost!\nDo you really want to reset?', 'Profile reset', wx.YES_NO | wx.ICON_QUESTION)
438 result = dlg.ShowModal() == wx.ID_YES
441 profile.resetGlobalProfile()
442 self.updateProfileToControls()
444 def OnBatchRun(self, e):
445 br = batchRun.batchRunWindow(self)
449 def OnSimpleSwitch(self, e):
450 profile.putPreference('startMode', 'Simple')
451 self.updateSliceMode()
453 def OnNormalSwitch(self, e):
454 profile.putPreference('startMode', 'Normal')
455 self.updateSliceMode()
457 def OnDefaultMarlinFirmware(self, e):
458 firmwareInstall.InstallFirmware()
460 def OnCustomFirmware(self, e):
461 if profile.getPreference('machine_type') == 'ultimaker':
462 wx.MessageBox('Warning: Installing a custom firmware does not garantee that you machine will function correctly, and could damage your machine.', 'Firmware update', wx.OK | wx.ICON_EXCLAMATION)
463 dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
464 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
465 if dlg.ShowModal() == wx.ID_OK:
466 filename = dlg.GetPath()
467 if not(os.path.exists(filename)):
469 #For some reason my Ubuntu 10.10 crashes here.
470 firmwareInstall.InstallFirmware(filename)
472 def OnFirstRunWizard(self, e):
473 configWizard.configWizard()
474 self.updateProfileToControls()
476 def OnBedLevelWizard(self, e):
477 configWizard.bedLevelWizard()
479 def OnExpertOpen(self, e):
480 ecw = expertConfig.expertConfigWindow()
484 def OnProjectPlanner(self, e):
485 pp = projectPlanner.projectPlanner()
489 def OnMinecraftImport(self, e):
490 mi = minecraftImport.minecraftImportWindow(self)
494 def OnSVGSlicerOpen(self, e):
495 svgSlicer = flatSlicerWindow.flatSlicerWindow()
499 def OnCheckForUpdate(self, e):
500 newVersion = version.checkForNewerVersion()
501 if newVersion is not None:
502 if wx.MessageBox('A new version of Cura is available, would you like to download?', 'New version available', wx.YES_NO | wx.ICON_INFORMATION) == wx.YES:
503 webbrowser.open(newVersion)
505 wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
507 def OnClose(self, e):
508 profile.saveGlobalProfile(profile.getDefaultProfilePath())
510 # Save the window position, size & state from the preferences file
511 profile.putPreference('window_maximized', self.IsMaximized())
512 if not self.IsMaximized():
513 (posx, posy) = self.GetPosition()
514 profile.putPreference('window_pos_x', posx)
515 profile.putPreference('window_pos_y', posy)
516 (width, height) = self.GetSize()
517 profile.putPreference('window_width', width)
518 profile.putPreference('window_height', height)
525 class normalSettingsPanel(configBase.configPanelBase):
526 "Main user interface window"
527 def __init__(self, parent):
528 super(normalSettingsPanel, self).__init__(parent)
531 nb = wx.Notebook(self)
532 self.SetSizer(wx.BoxSizer(wx.VERTICAL))
533 self.GetSizer().Add(nb, 1)
535 (left, right) = self.CreateConfigTab(nb, 'Print config')
537 configBase.TitleRow(left, "Quality")
538 c = configBase.SettingRow(left, "Layer height (mm)", 'layer_height', '0.2', 'Layer height in millimeters.\n0.2 is a good value for quick prints.\n0.1 gives high quality prints.')
539 validators.validFloat(c, 0.0001)
540 validators.warningAbove(c, lambda : (float(profile.getProfileSetting('nozzle_size')) * 80.0 / 100.0), "Thicker layers then %.2fmm (80%% nozzle size) usually give bad results and are not recommended.")
541 c = configBase.SettingRow(left, "Wall thickness (mm)", 'wall_thickness', '0.8', 'Thickness of the walls.\nThis is used in combination with the nozzle size to define the number\nof perimeter lines and the thickness of those perimeter lines.')
542 validators.validFloat(c, 0.0001)
543 validators.wallThicknessValidator(c)
544 c = configBase.SettingRow(left, "Enable retraction", 'retraction_enable', False, 'Retract the filament when the nozzle is moving over a none-printed area. Details about the retraction can be configured in the advanced tab.')
546 configBase.TitleRow(left, "Fill")
547 c = configBase.SettingRow(left, "Bottom/Top thickness (mm)", 'solid_layer_thickness', '0.6', 'This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value.\nHaving this value a multiply of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.')
548 validators.validFloat(c, 0.0)
549 c = configBase.SettingRow(left, "Fill Density (%)", 'fill_density', '20', 'This controls how densily filled the insides of your print will be. For a solid part use 100%, for an empty part use 0%. A value around 20% is usually enough')
550 validators.validFloat(c, 0.0, 100.0)
552 configBase.TitleRow(right, "Speed && Temperature")
553 c = configBase.SettingRow(right, "Print speed (mm/s)", 'print_speed', '50', 'Speed at which printing happens. A well adjusted Ultimaker can reach 150mm/s, but for good quality prints you want to print slower. Printing speed depends on a lot of factors. So you will be experimenting with optimal settings for this.')
554 validators.validFloat(c, 1.0)
555 validators.warningAbove(c, 150.0, "It is highly unlikely that your machine can achieve a printing speed above 150mm/s")
556 validators.printSpeedValidator(c)
558 #configBase.TitleRow(right, "Temperature")
559 c = configBase.SettingRow(right, "Printing temperature", 'print_temperature', '0', 'Temperature used for printing. Set at 0 to pre-heat yourself')
560 validators.validFloat(c, 0.0, 340.0)
561 validators.warningAbove(c, 260.0, "Temperatures above 260C could damage your machine, be careful!")
562 if profile.getPreference('has_heated_bed') == 'True':
563 c = configBase.SettingRow(right, "Bed temperature", 'print_bed_temperature', '0', 'Temperature used for the heated printer bed. Set at 0 to pre-heat yourself')
564 validators.validFloat(c, 0.0, 340.0)
566 configBase.TitleRow(right, "Support structure")
567 c = configBase.SettingRow(right, "Support type", 'support', ['None', 'Exterior Only', 'Everywhere'], 'Type of support structure build.\n"Exterior only" is the most commonly used support setting.\n\nNone does not do any support.\nExterior only only creates support where the support structure will touch the build platform.\nEverywhere creates support even on the insides of the model.')
568 c = configBase.SettingRow(right, "Add raft", 'enable_raft', False, 'A raft is a few layers of lines below the bottom of the object. It prevents warping. Full raft settings can be found in the expert settings.\nFor PLA this is usually not required. But if you print with ABS it is almost required.')
569 if int(profile.getPreference('extruder_amount')) > 1:
570 c = configBase.SettingRow(right, "Support dual extrusion", 'support_dual_extrusion', False, 'Print the support material with the 2nd extruder in a dual extrusion setup. The primary extruder will be used for normal material, while the second extruder is used to print support material.')
572 configBase.TitleRow(right, "Filament")
573 c = configBase.SettingRow(right, "Diameter (mm)", 'filament_diameter', '2.89', 'Diameter of your filament, as accurately as possible.\nIf you cannot measure this value you will have to callibrate it, a higher number means less extrusion, a smaller number generates more extrusion.')
574 validators.validFloat(c, 1.0)
575 validators.warningAbove(c, 3.5, "Are you sure your filament is that thick? Normal filament is around 3mm or 1.75mm.")
576 c = configBase.SettingRow(right, "Packing Density", 'filament_density', '1.00', 'Packing density of your filament. This should be 1.00 for PLA and 0.85 for ABS')
577 validators.validFloat(c, 0.5, 1.5)
579 (left, right) = self.CreateConfigTab(nb, 'Advanced config')
581 configBase.TitleRow(left, "Machine size")
582 c = configBase.SettingRow(left, "Nozzle size (mm)", 'nozzle_size', '0.4', 'The nozzle size is very important, this is used to calculate the line width of the infill, and used to calculate the amount of outside wall lines and thickness for the wall thickness you entered in the print settings.')
583 validators.validFloat(c, 0.1, 10.0)
585 configBase.TitleRow(left, "Skirt")
586 c = configBase.SettingRow(left, "Line count", 'skirt_line_count', '1', 'The skirt is a line drawn around the object at the first layer. This helps to prime your extruder, and to see if the object fits on your platform.\nSetting this to 0 will disable the skirt. Multiple skirt lines can help priming your extruder better for small objects.')
587 validators.validInt(c, 0, 10)
588 c = configBase.SettingRow(left, "Start distance (mm)", 'skirt_gap', '6.0', 'The distance between the skirt and the first layer.\nThis is the minimal distance, multiple skirt lines will be put outwards from this distance.')
589 validators.validFloat(c, 0.0)
591 configBase.TitleRow(left, "Retraction")
592 c = configBase.SettingRow(left, "Minimum travel (mm)", 'retraction_min_travel', '5.0', 'Minimum amount of travel needed for a retraction to happen at all. To make sure you do not get a lot of retractions in a small area')
593 validators.validFloat(c, 0.0)
594 c = configBase.SettingRow(left, "Speed (mm/s)", 'retraction_speed', '40.0', 'Speed at which the filament is retracted, a higher retraction speed works better. But a very high retraction speed can lead to filament grinding.')
595 validators.validFloat(c, 0.1)
596 c = configBase.SettingRow(left, "Distance (mm)", 'retraction_amount', '0.0', 'Amount of retraction, set at 0 for no retraction at all. A value of 2.0mm seems to generate good results.')
597 validators.validFloat(c, 0.0)
598 c = configBase.SettingRow(left, "Extra length on start (mm)", 'retraction_extra', '0.0', 'Extra extrusion amount when restarting after a retraction, to better "Prime" your extruder after retraction.')
599 validators.validFloat(c, 0.0)
601 configBase.TitleRow(right, "Speed")
602 c = configBase.SettingRow(right, "Travel speed (mm/s)", 'travel_speed', '150', 'Speed at which travel moves are done, a high quality build Ultimaker can reach speeds of 250mm/s. But some machines might miss steps then.')
603 validators.validFloat(c, 1.0)
604 validators.warningAbove(c, 300.0, "It is highly unlikely that your machine can achieve a travel speed above 300mm/s")
605 c = configBase.SettingRow(right, "Max Z speed (mm/s)", 'max_z_speed', '1.0', 'Speed at which Z moves are done. When you Z axis is properly lubercated you can increase this for less Z blob.')
606 validators.validFloat(c, 0.5)
607 c = configBase.SettingRow(right, "Bottom layer speed (mm/s)", 'bottom_layer_speed', '25', 'Print speed for the bottom layer, you want to print the first layer slower so it sticks better to the printer bed.')
608 validators.validFloat(c, 0.0)
610 configBase.TitleRow(right, "Cool")
611 c = configBase.SettingRow(right, "Minimal layer time (sec)", 'cool_min_layer_time', '10', 'Minimum time spend in a layer, gives the layer time to cool down before the next layer is put on top. If the layer will be placed down too fast the printer will slow down to make sure it has spend atleast this amount of seconds printing this layer.')
612 validators.validFloat(c, 0.0)
613 c = configBase.SettingRow(right, "Enable cooling fan", 'fan_enabled', True, 'Enable the cooling fan during the print. The extra cooling from the cooling fan is essensial during faster prints.')
615 configBase.TitleRow(right, "Quality")
616 c = configBase.SettingRow(right, "Initial layer thickness (mm)", 'bottom_thickness', '0.0', 'Layer thickness of the bottom layer. A thicker bottom layer makes sticking to the bed easier. Set to 0.0 to have the bottom layer thickness the same as the other layers.')
617 validators.validFloat(c, 0.0)
618 validators.warningAbove(c, lambda : (float(profile.getProfileSetting('nozzle_size')) * 3.0 / 4.0), "A bottom layer of more then %.2fmm (3/4 nozzle size) usually give bad results and is not recommended.")
619 c = configBase.SettingRow(right, "Duplicate outlines", 'enable_skin', False, 'Skin prints the outer lines of the prints twice, each time with half the thickness. This gives the illusion of a higher print quality.')
622 self.pluginPanel = pluginPanel.pluginPanel(nb)
623 if len(self.pluginPanel.pluginList) > 0:
624 nb.AddPage(self.pluginPanel, "Plugins")
626 self.pluginPanel.Show(False)
629 self.alterationPanel = alterationPanel.alterationPanel(nb)
630 nb.AddPage(self.alterationPanel, "Start/End-GCode")
632 def updateProfileToControls(self):
633 super(normalSettingsPanel, self).updateProfileToControls()
634 self.alterationPanel.updateProfileToControls()
635 self.pluginPanel.updateProfileToControls()