1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
9 from Cura.gui import configBase
10 from Cura.gui import expertConfig
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 simpleMode
17 from Cura.gui import sceneView
18 from Cura.gui import aboutWindow
19 from Cura.gui.util import dropTarget
20 #from Cura.gui.tools import batchRun
21 from Cura.gui.tools import pidDebugger
22 from Cura.gui.tools import minecraftImport
23 from Cura.util import profile
24 from Cura.util import version
26 from Cura.util import meshLoader
29 #MacOS release currently lacks some wx components, like the Publisher.
30 from wx.lib.pubsub import Publisher
34 class mainWindow(wx.Frame):
36 super(mainWindow, self).__init__(None, title=_('Cura - ') + version.getVersion())
38 wx.EVT_CLOSE(self, self.OnClose)
40 # allow dropping any file, restrict later
41 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
43 # TODO: wxWidgets 2.9.4 has a bug when NSView does not register for dragged types when wx drop target is set. It was fixed in 2.9.5
44 if sys.platform.startswith('darwin'):
47 nswindow = objc.objc_object(c_void_p=self.MacGetTopLevelWindowRef())
48 view = nswindow.contentView()
49 view.registerForDraggedTypes_([u'NSFilenamesPboardType'])
53 self.normalModeOnlyItems = []
55 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
56 self.config = wx.FileConfig(appName="Cura",
57 localFilename=mruFile,
58 style=wx.CONFIG_USE_LOCAL_FILE)
60 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)]
61 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
62 self.config.SetPath("/ModelMRU")
63 self.modelFileHistory.Load(self.config)
65 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)]
66 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
67 self.config.SetPath("/ProfileMRU")
68 self.profileFileHistory.Load(self.config)
70 self.menubar = wx.MenuBar()
71 self.fileMenu = wx.Menu()
72 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
73 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
74 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
75 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
76 i = self.fileMenu.Append(-1, _("Reload platform\tF5"))
77 self.Bind(wx.EVT_MENU, lambda e: self.scene.reloadScene(e), i)
78 i = self.fileMenu.Append(-1, _("Clear platform"))
79 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
81 self.fileMenu.AppendSeparator()
82 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
83 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnPrintButton(1), i)
84 i = self.fileMenu.Append(-1, _("Save GCode..."))
85 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
86 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
87 self.Bind(wx.EVT_MENU, lambda e: self.scene._showEngineLog(), i)
89 self.fileMenu.AppendSeparator()
90 i = self.fileMenu.Append(-1, _("Open Profile..."))
91 self.normalModeOnlyItems.append(i)
92 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
93 i = self.fileMenu.Append(-1, _("Save Profile..."))
94 self.normalModeOnlyItems.append(i)
95 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
96 if version.isDevVersion():
97 i = self.fileMenu.Append(-1, "Save difference from default...")
98 self.normalModeOnlyItems.append(i)
99 self.Bind(wx.EVT_MENU, self.OnSaveDifferences, i)
100 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
101 self.normalModeOnlyItems.append(i)
102 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
103 self.fileMenu.AppendSeparator()
104 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
105 self.normalModeOnlyItems.append(i)
106 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
108 self.fileMenu.AppendSeparator()
109 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
110 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
111 i = self.fileMenu.Append(-1, _("Machine settings..."))
112 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
113 self.fileMenu.AppendSeparator()
116 modelHistoryMenu = wx.Menu()
117 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
118 self.modelFileHistory.UseMenu(modelHistoryMenu)
119 self.modelFileHistory.AddFilesToMenu()
120 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
123 profileHistoryMenu = wx.Menu()
124 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
125 self.profileFileHistory.UseMenu(profileHistoryMenu)
126 self.profileFileHistory.AddFilesToMenu()
127 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
129 self.fileMenu.AppendSeparator()
130 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
131 self.Bind(wx.EVT_MENU, self.OnQuit, i)
132 self.menubar.Append(self.fileMenu, '&' + _("File"))
134 toolsMenu = wx.Menu()
135 #i = toolsMenu.Append(-1, 'Batch run...')
136 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
137 #self.normalModeOnlyItems.append(i)
139 if minecraftImport.hasMinecraft():
140 i = toolsMenu.Append(-1, _("Minecraft map import..."))
141 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
143 if version.isDevVersion():
144 i = toolsMenu.Append(-1, _("PID Debugger..."))
145 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
146 i = toolsMenu.Append(-1, _("Auto Firmware Update..."))
147 self.Bind(wx.EVT_MENU, self.OnAutoFirmwareUpdate, i)
149 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
150 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
152 toolsMenu.AppendSeparator()
153 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
154 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
155 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
156 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
157 if profile.getPreference('oneAtATime') == 'True':
158 self.oneAtATime.Check(True)
160 self.allAtOnceItem.Check(True)
162 self.menubar.Append(toolsMenu, _("Tools"))
164 #Machine menu for machine configuration/tooling
165 self.machineMenu = wx.Menu()
166 self.updateMachineMenu()
168 self.menubar.Append(self.machineMenu, _("Machine"))
170 expertMenu = wx.Menu()
171 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
172 self.switchToQuickprintMenuItem = i
173 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
175 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
176 self.switchToNormalMenuItem = i
177 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
178 expertMenu.AppendSeparator()
180 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
181 self.normalModeOnlyItems.append(i)
182 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
183 expertMenu.AppendSeparator()
184 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
185 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
186 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
187 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
189 self.menubar.Append(expertMenu, _("Expert"))
192 i = helpMenu.Append(-1, _("Online documentation..."))
193 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://lulzbot.com/cura'), i)
194 i = helpMenu.Append(-1, _("Report a problem..."))
195 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/alephobjects/Cura/issues'), i)
196 #i = helpMenu.Append(-1, _("Check for update..."))
197 #self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
198 #i = helpMenu.Append(-1, _("Open YouMagine website..."))
199 #self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
200 i = helpMenu.Append(-1, _("About Cura..."))
201 self.Bind(wx.EVT_MENU, self.OnAbout, i)
202 self.menubar.Append(helpMenu, _("Help"))
203 self.SetMenuBar(self.menubar)
205 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
206 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
207 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
208 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
211 self.scene = sceneView.SceneView(self.rightPane)
214 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, self.scene.sceneUpdated)
215 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, self.scene.sceneUpdated)
217 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
218 self.leftSizer.Add(self.simpleSettingsPanel, 1)
219 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
220 self.leftPane.SetSizer(self.leftSizer)
222 #Main sizer, to position the preview window, buttons and tab control
223 sizer = wx.BoxSizer()
224 self.rightPane.SetSizer(sizer)
225 sizer.Add(self.scene, 1, flag=wx.EXPAND)
228 sizer = wx.BoxSizer(wx.VERTICAL)
230 sizer.Add(self.splitter, 1, wx.EXPAND)
234 self.updateProfileToAllControls()
236 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
238 self.simpleSettingsPanel.Show(False)
239 self.normalSettingsPanel.Show(False)
241 # Set default window size & position
242 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
245 #Timer set; used to check if profile is on the clipboard
246 self.timer = wx.Timer(self)
247 self.Bind(wx.EVT_TIMER, self.onTimer)
248 self.timer.Start(1000)
249 self.lastTriedClipboard = profile.getProfileString()
251 # Restore the window position, size & state from the preferences file
253 if profile.getPreference('window_maximized') == 'True':
256 posx = int(profile.getPreference('window_pos_x'))
257 posy = int(profile.getPreference('window_pos_y'))
258 width = int(profile.getPreference('window_width'))
259 height = int(profile.getPreference('window_height'))
260 if posx > 0 or posy > 0:
261 self.SetPosition((posx,posy))
262 if width > 0 and height > 0:
263 self.SetSize((width,height))
265 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
267 self.normalSashPos = 0
269 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
270 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
272 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
274 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
276 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
278 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
279 self.SetSize((800,600))
282 self.updateSliceMode()
283 self.scene.SetFocus()
284 self.dialogframe = None
285 if Publisher is not None:
286 Publisher().subscribe(self.onPluginUpdate, "pluginupdate")
288 def onPluginUpdate(self,msg): #receives commands from the plugin thread
289 cmd = str(msg.data).split(";")
290 if cmd[0] == "OpenPluginProgressWindow":
291 if len(cmd)==1: #no titel received
293 if len(cmd)<3: #no message text received
294 cmd.append("Plugin is executed...")
297 self.dialogframe = wx.Frame(self, -1, cmd[1],pos = ((wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X)-dialogwidth)/2,(wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)-dialogheight)/2), size=(dialogwidth,dialogheight), style = wx.STAY_ON_TOP)
298 self.dialogpanel = wx.Panel(self.dialogframe, -1, pos = (0,0), size = (dialogwidth,dialogheight))
299 self.dlgtext = wx.StaticText(self.dialogpanel, label = cmd[2], pos = (10,10), size = (280,40))
300 self.dlgbar = wx.Gauge(self.dialogpanel,-1, 100, pos = (10,50), size = (280,20), style = wx.GA_HORIZONTAL)
301 self.dialogframe.Show()
303 elif cmd[0] == "Progress":
305 if number <= 100 and self.dialogframe is not None:
306 self.dlgbar.SetValue(number)
308 self.dlgbar.SetValue(100)
309 elif cmd[0] == "ClosePluginProgressWindow":
310 self.dialogframe.Destroy()
311 self.dialogframe=None
313 print "Unknown Plugin update received: " + cmd[0]
315 def onTimer(self, e):
316 #Check if there is something in the clipboard
319 if not wx.TheClipboard.IsOpened():
320 if not wx.TheClipboard.Open():
322 do = wx.TextDataObject()
323 if wx.TheClipboard.GetData(do):
324 profileString = do.GetText()
325 wx.TheClipboard.Close()
327 startTag = "CURA_PROFILE_STRING:"
328 if startTag in profileString:
329 #print "Found correct syntax on clipboard"
330 profileString = profileString.replace("\n","").strip()
331 profileString = profileString[profileString.find(startTag)+len(startTag):]
332 if profileString != self.lastTriedClipboard:
334 self.lastTriedClipboard = profileString
335 profile.setProfileFromString(profileString)
336 self.scene.notification.message(_("Loaded new profile from clipboard."))
337 self.updateProfileToAllControls()
339 print "Unable to read from clipboard"
342 def updateSliceMode(self, changedMode = True):
343 isSimple = profile.getPreference('startMode') == 'Simple'
345 self.normalSettingsPanel.Show(not isSimple)
346 self.simpleSettingsPanel.Show(isSimple)
347 self.leftPane.Layout()
349 for i in self.normalModeOnlyItems:
350 i.Enable(not isSimple)
352 self.switchToQuickprintMenuItem.Check()
354 self.switchToNormalMenuItem.Check()
356 # Set splitter sash position & size
358 # Save normal mode sash (only if we changed mode from normal
361 self.normalSashPos = self.splitter.GetSashPosition()
363 # Change location of sash to width of quick mode pane
364 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
365 self.splitter.SetSashPosition(width, True)
368 self.splitter.SetSashSize(0)
370 # Only change the sash position if we changed mode from simple
372 self.splitter.SetSashPosition(self.normalSashPos, True)
374 self.splitter.SetSashSize(4)
375 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
376 if profile.getMachineSetting('machine_type') == 'ultimaker2' or profile.getMachineSetting('machine_type') == 'lulzbot_mini' or profile.getMachineSetting('machine_type') == 'lulzbot_TAZ':
377 self.bedLevelWizardMenuItem.Enable(False)
378 self.headOffsetWizardMenuItem.Enable(False)
380 self.bedLevelWizardMenuItem.Enable(True)
381 self.headOffsetWizardMenuItem.Enable(False)
382 if int(profile.getMachineSetting('extruder_amount')) < 2:
383 self.headOffsetWizardMenuItem.Enable(False)
384 self.scene.updateProfileToControls()
385 self.scene._scene.pushFree()
387 def onOneAtATimeSwitch(self, e):
388 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
389 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
390 wx.MessageBox(_('For "One at a time" printing, you need to have entered the correct head size and gantry height in the machine settings'), _('One at a time warning'), wx.OK | wx.ICON_WARNING)
391 self.scene.updateProfileToControls()
392 self.scene._scene.pushFree()
393 self.scene.sceneUpdated()
395 def OnPreferences(self, e):
396 prefDialog = preferencesDialog.preferencesDialog(self)
401 def OnMachineSettings(self, e):
402 prefDialog = preferencesDialog.machineSettingsDialog(self)
407 def OnDropFiles(self, files):
408 self.scene.loadFiles(files)
410 def OnModelMRU(self, e):
411 fileNum = e.GetId() - self.ID_MRU_MODEL1
412 path = self.modelFileHistory.GetHistoryFile(fileNum)
414 self.modelFileHistory.AddFileToHistory(path) # move up the list
415 self.config.SetPath("/ModelMRU")
416 self.modelFileHistory.Save(self.config)
419 profile.putPreference('lastFile', path)
421 self.scene.loadFiles(filelist)
423 def addToModelMRU(self, file):
424 self.modelFileHistory.AddFileToHistory(file)
425 self.config.SetPath("/ModelMRU")
426 self.modelFileHistory.Save(self.config)
429 def OnProfileMRU(self, e):
430 fileNum = e.GetId() - self.ID_MRU_PROFILE1
431 path = self.profileFileHistory.GetHistoryFile(fileNum)
433 self.profileFileHistory.AddFileToHistory(path) # move up the list
434 self.config.SetPath("/ProfileMRU")
435 self.profileFileHistory.Save(self.config)
438 profile.loadProfile(path)
439 self.updateProfileToAllControls()
441 def addToProfileMRU(self, file):
442 self.profileFileHistory.AddFileToHistory(file)
443 self.config.SetPath("/ProfileMRU")
444 self.profileFileHistory.Save(self.config)
447 def updateProfileToAllControls(self):
448 self.scene.updateProfileToControls()
449 self.normalSettingsPanel.updateProfileToControls()
450 self.simpleSettingsPanel.updateProfileToControls()
452 def reloadSettingPanels(self, changedSliceMode = False):
453 self.leftSizer.Detach(self.simpleSettingsPanel)
454 self.leftSizer.Detach(self.normalSettingsPanel)
455 self.simpleSettingsPanel.Destroy()
456 self.normalSettingsPanel.Destroy()
457 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
458 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
459 self.leftSizer.Add(self.simpleSettingsPanel, 1)
460 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
461 self.updateSliceMode(changedSliceMode)
462 self.updateProfileToAllControls()
464 def updateMachineMenu(self):
465 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
466 for item in self.machineMenu.GetMenuItems():
467 self.machineMenu.RemoveItem(item)
469 #Add a menu item for each machine configuration.
470 for n in xrange(0, profile.getMachineCount()):
471 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
472 if n == int(profile.getPreferenceFloat('active_machine')):
474 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
476 self.machineMenu.AppendSeparator()
477 i = self.machineMenu.Append(-1, _("Add new machine..."))
478 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
479 i = self.machineMenu.Append(-1, _("Machine settings..."))
480 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
482 #Add tools for machines.
483 self.machineMenu.AppendSeparator()
485 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
486 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
488 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
489 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
491 def OnLoadProfile(self, e):
492 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)
493 dlg.SetWildcard("ini files (*.ini)|*.ini")
494 if dlg.ShowModal() == wx.ID_OK:
495 profileFile = dlg.GetPath()
496 profile.loadProfile(profileFile)
497 self.updateProfileToAllControls()
499 # Update the Profile MRU
500 self.addToProfileMRU(profileFile)
503 def OnLoadProfileFromGcode(self, e):
504 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)
505 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
506 if dlg.ShowModal() == wx.ID_OK:
507 gcodeFile = dlg.GetPath()
508 f = open(gcodeFile, 'r')
511 if line.startswith(';CURA_PROFILE_STRING:'):
512 profile.setProfileFromString(line[line.find(':')+1:].strip())
513 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
514 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
517 self.updateProfileToAllControls()
519 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)
522 def OnSaveProfile(self, e):
523 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
524 dlg.SetWildcard("ini files (*.ini)|*.ini")
525 if dlg.ShowModal() == wx.ID_OK:
526 profile_filename = dlg.GetPath()
527 if not profile_filename.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
528 profile_filename += '.ini'
529 profile.saveProfile(profile_filename)
532 def OnSaveDifferences(self, e):
533 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
534 dlg.SetWildcard("ini files (*.ini)|*.ini")
535 if dlg.ShowModal() == wx.ID_OK:
536 profile_filename = dlg.GetPath()
537 if not profile_filename.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
538 profile_filename += '.ini'
539 profile.saveProfileDifferenceFromDefault(profile_filename)
542 def OnResetProfile(self, e):
543 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)
544 result = dlg.ShowModal() == wx.ID_YES
547 profile.resetProfile()
548 self.updateProfileToAllControls()
550 def OnSimpleSwitch(self, e):
551 profile.putPreference('startMode', 'Simple')
552 self.updateSliceMode()
554 def OnNormalSwitch(self, e):
555 profile.putPreference('startMode', 'Normal')
556 dlg = wx.MessageDialog(self, _("Copy the settings from quickprint to your full settings?\n(This will overwrite any full setting modifications you have)"), _("Profile copy"), wx.YES_NO | wx.ICON_QUESTION)
557 result = dlg.ShowModal() == wx.ID_YES
560 profile.resetProfile()
561 for k, v in self.simpleSettingsPanel.getSettingOverrides().items():
562 profile.putProfileSetting(k, v)
563 self.updateProfileToAllControls()
564 self.updateSliceMode()
566 def OnDefaultMarlinFirmware(self, e):
567 firmwareInstall.InstallFirmware(self)
569 def OnCustomFirmware(self, e):
570 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
571 wx.MessageBox(_("Warning: Installing a custom firmware does not guarantee that you machine will function correctly, and could damage your machine."), _("Firmware update"), wx.OK | wx.ICON_EXCLAMATION)
572 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
573 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
574 if dlg.ShowModal() == wx.ID_OK:
575 filename = dlg.GetPath()
577 if not(os.path.exists(filename)):
579 #For some reason my Ubuntu 10.10 crashes here.
580 firmwareInstall.InstallFirmware(self, filename)
582 def OnAddNewMachine(self, e):
584 wasSimple = profile.getPreference('startMode') == 'Simple'
585 configWizard.ConfigWizard(True)
586 isSimple = profile.getPreference('startMode') == 'Simple'
588 self.reloadSettingPanels(isSimple != wasSimple)
589 self.updateMachineMenu()
591 def OnSelectMachine(self, index):
592 profile.setActiveMachine(index)
593 self.reloadSettingPanels(False)
595 def OnBedLevelWizard(self, e):
596 configWizard.bedLevelWizard()
598 def OnHeadOffsetWizard(self, e):
599 configWizard.headOffsetWizard()
601 def OnExpertOpen(self, e):
602 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
606 def OnMinecraftImport(self, e):
607 mi = minecraftImport.minecraftImportWindow(self)
611 def OnPIDDebugger(self, e):
612 debugger = pidDebugger.debuggerWindow(self)
616 def OnAutoFirmwareUpdate(self, e):
617 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
618 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
619 if dlg.ShowModal() == wx.ID_OK:
620 filename = dlg.GetPath()
622 if not(os.path.exists(filename)):
624 #For some reason my Ubuntu 10.10 crashes here.
625 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
627 def onCopyProfileClipboard(self, e):
629 if not wx.TheClipboard.IsOpened():
630 wx.TheClipboard.Open()
631 clipData = wx.TextDataObject()
632 self.lastTriedClipboard = profile.getProfileString()
633 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
634 clipData.SetText(profileString)
635 wx.TheClipboard.SetData(clipData)
636 wx.TheClipboard.Close()
638 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
640 def OnCheckForUpdate(self, e):
641 newVersion = version.checkForNewerVersion()
642 if newVersion is not None:
643 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:
644 webbrowser.open(newVersion)
646 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
648 def OnAbout(self, e):
649 aboutBox = aboutWindow.aboutWindow(self)
654 def OnClose(self, e):
655 profile.saveProfile(profile.getDefaultProfilePath(), True)
657 # Save the window position, size & state from the preferences file
658 profile.putPreference('window_maximized', self.IsMaximized())
659 if not self.IsMaximized() and not self.IsIconized():
660 (posx, posy) = self.GetPosition()
661 profile.putPreference('window_pos_x', posx)
662 profile.putPreference('window_pos_y', posy)
663 (width, height) = self.GetSize()
664 profile.putPreference('window_width', width)
665 profile.putPreference('window_height', height)
667 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
668 isSimple = profile.getPreference('startMode') == 'Simple'
670 self.normalSashPos = self.splitter.GetSashPosition()
671 profile.putPreference('window_normal_sash', self.normalSashPos)
673 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
675 self.scene.OnPaint = lambda e : e
682 class normalSettingsPanel(configBase.configPanelBase):
683 "Main user interface window"
684 def __init__(self, parent, callback = None):
685 super(normalSettingsPanel, self).__init__(parent, callback)
688 self.nb = wx.Notebook(self)
689 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
690 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
692 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
693 self._addSettingsToPanels('basic', left, right)
694 self.SizeLabelWidths(left, right)
696 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
697 self._addSettingsToPanels('advanced', left, right)
698 self.SizeLabelWidths(left, right)
701 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
702 self.nb.AddPage(self.pluginPanel, _("Plugins"))
705 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
706 self.alterationPanel = None
708 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
709 self.nb.AddPage(self.alterationPanel, _("Start/End-GCode"))
711 self.Bind(wx.EVT_SIZE, self.OnSize)
713 self.nb.SetSize(self.GetSize())
714 self.UpdateSize(self.printPanel)
715 self.UpdateSize(self.advancedPanel)
717 def _addSettingsToPanels(self, category, left, right):
718 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
722 for title in profile.getSubCategoriesFor(category):
723 n += 1 + len(profile.getSettingsForCategory(category, title))
726 configBase.TitleRow(p, _(title))
727 for s in profile.getSettingsForCategory(category, title):
728 configBase.SettingRow(p, s.getName())
730 def SizeLabelWidths(self, left, right):
731 leftWidth = self.getLabelColumnWidth(left)
732 rightWidth = self.getLabelColumnWidth(right)
733 maxWidth = max(leftWidth, rightWidth)
734 self.setLabelColumnWidth(left, maxWidth)
735 self.setLabelColumnWidth(right, maxWidth)
738 # Make the size of the Notebook control the same size as this control
739 self.nb.SetSize(self.GetSize())
741 # Propegate the OnSize() event (just in case)
744 # Perform out resize magic
745 self.UpdateSize(self.printPanel)
746 self.UpdateSize(self.advancedPanel)
748 def UpdateSize(self, configPanel):
749 sizer = configPanel.GetSizer()
753 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
756 # if width(col1) > (best_width(col1) + best_width(col1)):
757 # switch to horizontal
760 col1 = configPanel.leftPanel
761 colSize1 = col1.GetSize()
762 colBestSize1 = col1.GetBestSize()
763 col2 = configPanel.rightPanel
764 colSize2 = col2.GetSize()
765 colBestSize2 = col2.GetBestSize()
767 orientation = sizer.GetOrientation()
769 if orientation == wx.HORIZONTAL:
770 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
772 sizer = wx.BoxSizer(wx.VERTICAL)
773 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
774 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
775 configPanel.SetSizer(sizer)
781 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
783 sizer = wx.BoxSizer(wx.HORIZONTAL)
784 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
785 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
786 configPanel.SetSizer(sizer)
792 def updateProfileToControls(self):
793 super(normalSettingsPanel, self).updateProfileToControls()
794 if self.alterationPanel is not None:
795 self.alterationPanel.updateProfileToControls()
796 self.pluginPanel.updateProfileToControls()