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
28 from wx.lib.pubsub import Publisher
30 class mainWindow(wx.Frame):
32 super(mainWindow, self).__init__(None, title=_('Cura - ') + version.getVersion())
34 wx.EVT_CLOSE(self, self.OnClose)
36 # allow dropping any file, restrict later
37 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
39 # 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
40 if sys.platform.startswith('darwin'):
43 nswindow = objc.objc_object(c_void_p=self.MacGetTopLevelWindowRef())
44 view = nswindow.contentView()
45 view.registerForDraggedTypes_([u'NSFilenamesPboardType'])
49 self.normalModeOnlyItems = []
51 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
52 self.config = wx.FileConfig(appName="Cura",
53 localFilename=mruFile,
54 style=wx.CONFIG_USE_LOCAL_FILE)
56 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)]
57 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
58 self.config.SetPath("/ModelMRU")
59 self.modelFileHistory.Load(self.config)
61 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)]
62 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
63 self.config.SetPath("/ProfileMRU")
64 self.profileFileHistory.Load(self.config)
66 self.menubar = wx.MenuBar()
67 self.fileMenu = wx.Menu()
68 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
69 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
70 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
71 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
72 i = self.fileMenu.Append(-1, _("Reload platform\tF5"))
73 self.Bind(wx.EVT_MENU, lambda e: self.scene.reloadScene(e), i)
74 i = self.fileMenu.Append(-1, _("Clear platform"))
75 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
77 self.fileMenu.AppendSeparator()
78 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
79 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnPrintButton(1), i)
80 i = self.fileMenu.Append(-1, _("Save GCode..."))
81 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
82 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
83 self.Bind(wx.EVT_MENU, lambda e: self.scene._showEngineLog(), i)
85 self.fileMenu.AppendSeparator()
86 i = self.fileMenu.Append(-1, _("Open Profile..."))
87 self.normalModeOnlyItems.append(i)
88 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
89 i = self.fileMenu.Append(-1, _("Save Profile..."))
90 self.normalModeOnlyItems.append(i)
91 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
92 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
93 self.normalModeOnlyItems.append(i)
94 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
95 self.fileMenu.AppendSeparator()
96 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
97 self.normalModeOnlyItems.append(i)
98 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
100 self.fileMenu.AppendSeparator()
101 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
102 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
103 i = self.fileMenu.Append(-1, _("Machine settings..."))
104 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
105 self.fileMenu.AppendSeparator()
108 modelHistoryMenu = wx.Menu()
109 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
110 self.modelFileHistory.UseMenu(modelHistoryMenu)
111 self.modelFileHistory.AddFilesToMenu()
112 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
115 profileHistoryMenu = wx.Menu()
116 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
117 self.profileFileHistory.UseMenu(profileHistoryMenu)
118 self.profileFileHistory.AddFilesToMenu()
119 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
121 self.fileMenu.AppendSeparator()
122 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
123 self.Bind(wx.EVT_MENU, self.OnQuit, i)
124 self.menubar.Append(self.fileMenu, '&' + _("File"))
126 toolsMenu = wx.Menu()
127 #i = toolsMenu.Append(-1, 'Batch run...')
128 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
129 #self.normalModeOnlyItems.append(i)
131 if minecraftImport.hasMinecraft():
132 i = toolsMenu.Append(-1, _("Minecraft map import..."))
133 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
135 if version.isDevVersion():
136 i = toolsMenu.Append(-1, _("PID Debugger..."))
137 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
138 i = toolsMenu.Append(-1, _("Auto Firmware Update..."))
139 self.Bind(wx.EVT_MENU, self.OnAutoFirmwareUpdate, i)
141 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
142 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
144 toolsMenu.AppendSeparator()
145 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
146 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
147 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
148 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
149 if profile.getPreference('oneAtATime') == 'True':
150 self.oneAtATime.Check(True)
152 self.allAtOnceItem.Check(True)
154 self.menubar.Append(toolsMenu, _("Tools"))
156 #Machine menu for machine configuration/tooling
157 self.machineMenu = wx.Menu()
158 self.updateMachineMenu()
160 self.menubar.Append(self.machineMenu, _("Machine"))
162 expertMenu = wx.Menu()
163 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
164 self.switchToQuickprintMenuItem = i
165 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
167 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
168 self.switchToNormalMenuItem = i
169 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
170 expertMenu.AppendSeparator()
172 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
173 self.normalModeOnlyItems.append(i)
174 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
175 expertMenu.AppendSeparator()
176 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
177 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
178 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
179 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
181 self.menubar.Append(expertMenu, _("Expert"))
184 i = helpMenu.Append(-1, _("Online documentation..."))
185 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://lulzbot.com/cura'), i)
186 i = helpMenu.Append(-1, _("Report a problem..."))
187 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/alephobjects/Cura/issues'), i)
188 #i = helpMenu.Append(-1, _("Check for update..."))
189 #self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
190 #i = helpMenu.Append(-1, _("Open YouMagine website..."))
191 #self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
192 i = helpMenu.Append(-1, _("About Cura..."))
193 self.Bind(wx.EVT_MENU, self.OnAbout, i)
194 self.menubar.Append(helpMenu, _("Help"))
195 self.SetMenuBar(self.menubar)
197 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
198 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
199 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
200 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
203 self.scene = sceneView.SceneView(self.rightPane)
206 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, self.scene.sceneUpdated)
207 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, self.scene.sceneUpdated)
209 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
210 self.leftSizer.Add(self.simpleSettingsPanel, 1)
211 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
212 self.leftPane.SetSizer(self.leftSizer)
214 #Main sizer, to position the preview window, buttons and tab control
215 sizer = wx.BoxSizer()
216 self.rightPane.SetSizer(sizer)
217 sizer.Add(self.scene, 1, flag=wx.EXPAND)
220 sizer = wx.BoxSizer(wx.VERTICAL)
222 sizer.Add(self.splitter, 1, wx.EXPAND)
226 self.updateProfileToAllControls()
228 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
230 self.simpleSettingsPanel.Show(False)
231 self.normalSettingsPanel.Show(False)
233 # Set default window size & position
234 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
237 #Timer set; used to check if profile is on the clipboard
238 self.timer = wx.Timer(self)
239 self.Bind(wx.EVT_TIMER, self.onTimer)
240 self.timer.Start(1000)
241 self.lastTriedClipboard = profile.getProfileString()
243 # Restore the window position, size & state from the preferences file
245 if profile.getPreference('window_maximized') == 'True':
248 posx = int(profile.getPreference('window_pos_x'))
249 posy = int(profile.getPreference('window_pos_y'))
250 width = int(profile.getPreference('window_width'))
251 height = int(profile.getPreference('window_height'))
252 if posx > 0 or posy > 0:
253 self.SetPosition((posx,posy))
254 if width > 0 and height > 0:
255 self.SetSize((width,height))
257 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
259 self.normalSashPos = 0
261 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
262 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
264 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
266 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
268 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
270 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
271 self.SetSize((800,600))
274 self.updateSliceMode()
275 self.scene.SetFocus()
276 self.dialogframe = None
277 Publisher().subscribe(self.onPluginUpdate, "pluginupdate")
279 def onPluginUpdate(self,msg): #receives commands from the plugin thread
280 cmd = str(msg.data).split(";")
281 if cmd[0] == "OpenPluginProgressWindow":
282 if len(cmd)==1: #no titel received
284 if len(cmd)<3: #no message text received
285 cmd.append("Plugin is executed...")
288 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)
289 self.dialogpanel = wx.Panel(self.dialogframe, -1, pos = (0,0), size = (dialogwidth,dialogheight))
290 self.dlgtext = wx.StaticText(self.dialogpanel, label = cmd[2], pos = (10,10), size = (280,40))
291 self.dlgbar = wx.Gauge(self.dialogpanel,-1, 100, pos = (10,50), size = (280,20), style = wx.GA_HORIZONTAL)
292 self.dialogframe.Show()
294 elif cmd[0] == "Progress":
296 if number <= 100 and self.dialogframe is not None:
297 self.dlgbar.SetValue(number)
299 self.dlgbar.SetValue(100)
300 elif cmd[0] == "ClosePluginProgressWindow":
301 self.dialogframe.Destroy()
302 self.dialogframe=None
304 print "Unknown Plugin update received: " + cmd[0]
306 def onTimer(self, e):
307 #Check if there is something in the clipboard
310 if not wx.TheClipboard.IsOpened():
311 if not wx.TheClipboard.Open():
313 do = wx.TextDataObject()
314 if wx.TheClipboard.GetData(do):
315 profileString = do.GetText()
316 wx.TheClipboard.Close()
318 startTag = "CURA_PROFILE_STRING:"
319 if startTag in profileString:
320 #print "Found correct syntax on clipboard"
321 profileString = profileString.replace("\n","").strip()
322 profileString = profileString[profileString.find(startTag)+len(startTag):]
323 if profileString != self.lastTriedClipboard:
325 self.lastTriedClipboard = profileString
326 profile.setProfileFromString(profileString)
327 self.scene.notification.message(_("Loaded new profile from clipboard."))
328 self.updateProfileToAllControls()
330 print "Unable to read from clipboard"
333 def updateSliceMode(self):
334 isSimple = profile.getPreference('startMode') == 'Simple'
336 self.normalSettingsPanel.Show(not isSimple)
337 self.simpleSettingsPanel.Show(isSimple)
338 self.leftPane.Layout()
340 for i in self.normalModeOnlyItems:
341 i.Enable(not isSimple)
343 self.switchToQuickprintMenuItem.Check()
345 self.switchToNormalMenuItem.Check()
347 # Set splitter sash position & size
349 # Save normal mode sash
350 self.normalSashPos = self.splitter.GetSashPosition()
352 # Change location of sash to width of quick mode pane
353 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
354 self.splitter.SetSashPosition(width, True)
357 self.splitter.SetSashSize(0)
359 self.splitter.SetSashPosition(self.normalSashPos, True)
361 self.splitter.SetSashSize(4)
362 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
363 if profile.getMachineSetting('machine_type') == 'ultimaker2' or profile.getMachineSetting('machine_type') == 'lulzbot_mini' or profile.getMachineSetting('machine_type') == 'lulzbot_TAZ':
364 self.bedLevelWizardMenuItem.Enable(False)
365 self.headOffsetWizardMenuItem.Enable(False)
367 self.bedLevelWizardMenuItem.Enable(True)
368 self.headOffsetWizardMenuItem.Enable(False)
369 if int(profile.getMachineSetting('extruder_amount')) < 2:
370 self.headOffsetWizardMenuItem.Enable(False)
371 self.scene.updateProfileToControls()
372 self.scene._scene.pushFree()
374 def onOneAtATimeSwitch(self, e):
375 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
376 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
377 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)
378 self.scene.updateProfileToControls()
379 self.scene._scene.pushFree()
380 self.scene.sceneUpdated()
382 def OnPreferences(self, e):
383 prefDialog = preferencesDialog.preferencesDialog(self)
387 wx.CallAfter(prefDialog.Show)
389 def OnMachineSettings(self, e):
390 prefDialog = preferencesDialog.machineSettingsDialog(self)
395 def OnDropFiles(self, files):
397 self.updateProfileToAllControls()
398 self.scene.loadFiles(files)
400 def OnModelMRU(self, e):
401 fileNum = e.GetId() - self.ID_MRU_MODEL1
402 path = self.modelFileHistory.GetHistoryFile(fileNum)
404 self.modelFileHistory.AddFileToHistory(path) # move up the list
405 self.config.SetPath("/ModelMRU")
406 self.modelFileHistory.Save(self.config)
409 profile.putPreference('lastFile', path)
411 self.scene.loadFiles(filelist)
413 def addToModelMRU(self, file):
414 self.modelFileHistory.AddFileToHistory(file)
415 self.config.SetPath("/ModelMRU")
416 self.modelFileHistory.Save(self.config)
419 def OnProfileMRU(self, e):
420 fileNum = e.GetId() - self.ID_MRU_PROFILE1
421 path = self.profileFileHistory.GetHistoryFile(fileNum)
423 self.profileFileHistory.AddFileToHistory(path) # move up the list
424 self.config.SetPath("/ProfileMRU")
425 self.profileFileHistory.Save(self.config)
428 profile.loadProfile(path)
429 self.updateProfileToAllControls()
431 def addToProfileMRU(self, file):
432 self.profileFileHistory.AddFileToHistory(file)
433 self.config.SetPath("/ProfileMRU")
434 self.profileFileHistory.Save(self.config)
437 def updateProfileToAllControls(self):
438 self.scene.updateProfileToControls()
439 self.normalSettingsPanel.updateProfileToControls()
440 self.simpleSettingsPanel.updateProfileToControls()
442 def reloadSettingPanels(self):
443 self.leftSizer.Detach(self.simpleSettingsPanel)
444 self.leftSizer.Detach(self.normalSettingsPanel)
445 self.simpleSettingsPanel.Destroy()
446 self.normalSettingsPanel.Destroy()
447 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
448 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
449 self.leftSizer.Add(self.simpleSettingsPanel, 1)
450 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
451 self.updateSliceMode()
452 self.updateProfileToAllControls()
454 def updateMachineMenu(self):
455 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
456 for item in self.machineMenu.GetMenuItems():
457 self.machineMenu.RemoveItem(item)
459 #Add a menu item for each machine configuration.
460 for n in xrange(0, profile.getMachineCount()):
461 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
462 if n == int(profile.getPreferenceFloat('active_machine')):
464 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
466 self.machineMenu.AppendSeparator()
467 i = self.machineMenu.Append(-1, _("Add new machine..."))
468 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
469 i = self.machineMenu.Append(-1, _("Machine settings..."))
470 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
472 #Add tools for machines.
473 self.machineMenu.AppendSeparator()
475 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
476 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
478 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
479 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
481 def OnLoadProfile(self, e):
482 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)
483 dlg.SetWildcard("ini files (*.ini)|*.ini")
484 if dlg.ShowModal() == wx.ID_OK:
485 profileFile = dlg.GetPath()
486 profile.loadProfile(profileFile)
487 self.updateProfileToAllControls()
489 # Update the Profile MRU
490 self.addToProfileMRU(profileFile)
493 def OnLoadProfileFromGcode(self, e):
494 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)
495 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
496 if dlg.ShowModal() == wx.ID_OK:
497 gcodeFile = dlg.GetPath()
498 f = open(gcodeFile, 'r')
501 if line.startswith(';CURA_PROFILE_STRING:'):
502 profile.setProfileFromString(line[line.find(':')+1:].strip())
503 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
504 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
507 self.updateProfileToAllControls()
509 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)
512 def OnSaveProfile(self, e):
513 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
514 dlg.SetWildcard("ini files (*.ini)|*.ini")
515 if dlg.ShowModal() == wx.ID_OK:
516 profileFile = dlg.GetPath()
517 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
518 profileFile += '.ini'
519 profile.saveProfile(profileFile)
522 def OnResetProfile(self, e):
523 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)
524 result = dlg.ShowModal() == wx.ID_YES
527 profile.resetProfile()
528 self.updateProfileToAllControls()
530 def OnSimpleSwitch(self, e):
531 profile.putPreference('startMode', 'Simple')
532 self.updateSliceMode()
534 def OnNormalSwitch(self, e):
535 profile.putPreference('startMode', 'Normal')
536 self.updateSliceMode()
538 def OnDefaultMarlinFirmware(self, e):
539 firmwareInstall.InstallFirmware(self)
541 def OnCustomFirmware(self, e):
542 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
543 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)
544 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
545 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
546 if dlg.ShowModal() == wx.ID_OK:
547 filename = dlg.GetPath()
549 if not(os.path.exists(filename)):
551 #For some reason my Ubuntu 10.10 crashes here.
552 firmwareInstall.InstallFirmware(self, filename)
554 def OnAddNewMachine(self, e):
556 profile.setActiveMachine(profile.getMachineCount())
557 configWizard.configWizard(True)
559 self.reloadSettingPanels()
560 self.updateMachineMenu()
562 def OnSelectMachine(self, index):
563 profile.setActiveMachine(index)
564 self.reloadSettingPanels()
566 def OnBedLevelWizard(self, e):
567 configWizard.bedLevelWizard()
569 def OnHeadOffsetWizard(self, e):
570 configWizard.headOffsetWizard()
572 def OnExpertOpen(self, e):
573 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
577 def OnMinecraftImport(self, e):
578 mi = minecraftImport.minecraftImportWindow(self)
582 def OnPIDDebugger(self, e):
583 debugger = pidDebugger.debuggerWindow(self)
587 def OnAutoFirmwareUpdate(self, e):
588 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
589 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
590 if dlg.ShowModal() == wx.ID_OK:
591 filename = dlg.GetPath()
593 if not(os.path.exists(filename)):
595 #For some reason my Ubuntu 10.10 crashes here.
596 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
598 def onCopyProfileClipboard(self, e):
600 if not wx.TheClipboard.IsOpened():
601 wx.TheClipboard.Open()
602 clipData = wx.TextDataObject()
603 self.lastTriedClipboard = profile.getProfileString()
604 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
605 clipData.SetText(profileString)
606 wx.TheClipboard.SetData(clipData)
607 wx.TheClipboard.Close()
609 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
611 def OnCheckForUpdate(self, e):
612 newVersion = version.checkForNewerVersion()
613 if newVersion is not None:
614 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:
615 webbrowser.open(newVersion)
617 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
619 def OnAbout(self, e):
620 aboutBox = aboutWindow.aboutWindow()
624 def OnClose(self, e):
625 profile.saveProfile(profile.getDefaultProfilePath(), True)
627 # Save the window position, size & state from the preferences file
628 profile.putPreference('window_maximized', self.IsMaximized())
629 if not self.IsMaximized() and not self.IsIconized():
630 (posx, posy) = self.GetPosition()
631 profile.putPreference('window_pos_x', posx)
632 profile.putPreference('window_pos_y', posy)
633 (width, height) = self.GetSize()
634 profile.putPreference('window_width', width)
635 profile.putPreference('window_height', height)
637 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
638 isSimple = profile.getPreference('startMode') == 'Simple'
640 self.normalSashPos = self.splitter.GetSashPosition()
641 profile.putPreference('window_normal_sash', self.normalSashPos)
643 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
645 self.scene.OnPaint = lambda e : e
646 self.scene._engine.cleanup()
652 class normalSettingsPanel(configBase.configPanelBase):
653 "Main user interface window"
654 def __init__(self, parent, callback = None):
655 super(normalSettingsPanel, self).__init__(parent, callback)
658 self.nb = wx.Notebook(self)
659 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
660 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
662 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
663 self._addSettingsToPanels('basic', left, right)
664 self.SizeLabelWidths(left, right)
666 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
667 self._addSettingsToPanels('advanced', left, right)
668 self.SizeLabelWidths(left, right)
671 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
672 self.nb.AddPage(self.pluginPanel, _("Plugins"))
675 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
676 self.alterationPanel = None
678 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
679 self.nb.AddPage(self.alterationPanel, _("Start/End-GCode"))
681 self.Bind(wx.EVT_SIZE, self.OnSize)
683 self.nb.SetSize(self.GetSize())
684 self.UpdateSize(self.printPanel)
685 self.UpdateSize(self.advancedPanel)
687 def _addSettingsToPanels(self, category, left, right):
688 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
692 for title in profile.getSubCategoriesFor(category):
693 n += 1 + len(profile.getSettingsForCategory(category, title))
696 configBase.TitleRow(p, _(title))
697 for s in profile.getSettingsForCategory(category, title):
698 configBase.SettingRow(p, s.getName())
700 def SizeLabelWidths(self, left, right):
701 leftWidth = self.getLabelColumnWidth(left)
702 rightWidth = self.getLabelColumnWidth(right)
703 maxWidth = max(leftWidth, rightWidth)
704 self.setLabelColumnWidth(left, maxWidth)
705 self.setLabelColumnWidth(right, maxWidth)
708 # Make the size of the Notebook control the same size as this control
709 self.nb.SetSize(self.GetSize())
711 # Propegate the OnSize() event (just in case)
714 # Perform out resize magic
715 self.UpdateSize(self.printPanel)
716 self.UpdateSize(self.advancedPanel)
718 def UpdateSize(self, configPanel):
719 sizer = configPanel.GetSizer()
723 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
726 # if width(col1) > (best_width(col1) + best_width(col1)):
727 # switch to horizontal
730 col1 = configPanel.leftPanel
731 colSize1 = col1.GetSize()
732 colBestSize1 = col1.GetBestSize()
733 col2 = configPanel.rightPanel
734 colSize2 = col2.GetSize()
735 colBestSize2 = col2.GetBestSize()
737 orientation = sizer.GetOrientation()
739 if orientation == wx.HORIZONTAL:
740 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
742 sizer = wx.BoxSizer(wx.VERTICAL)
743 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
744 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
745 configPanel.SetSizer(sizer)
751 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
753 sizer = wx.BoxSizer(wx.HORIZONTAL)
754 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
755 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
756 configPanel.SetSizer(sizer)
762 def updateProfileToControls(self):
763 super(normalSettingsPanel, self).updateProfileToControls()
764 if self.alterationPanel is not None:
765 self.alterationPanel.updateProfileToControls()
766 self.pluginPanel.updateProfileToControls()