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 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
97 self.normalModeOnlyItems.append(i)
98 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
99 self.fileMenu.AppendSeparator()
100 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
101 self.normalModeOnlyItems.append(i)
102 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
104 self.fileMenu.AppendSeparator()
105 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
106 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
107 i = self.fileMenu.Append(-1, _("Machine settings..."))
108 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
109 self.fileMenu.AppendSeparator()
112 modelHistoryMenu = wx.Menu()
113 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
114 self.modelFileHistory.UseMenu(modelHistoryMenu)
115 self.modelFileHistory.AddFilesToMenu()
116 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
119 profileHistoryMenu = wx.Menu()
120 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
121 self.profileFileHistory.UseMenu(profileHistoryMenu)
122 self.profileFileHistory.AddFilesToMenu()
123 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
125 self.fileMenu.AppendSeparator()
126 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
127 self.Bind(wx.EVT_MENU, self.OnQuit, i)
128 self.menubar.Append(self.fileMenu, '&' + _("File"))
130 toolsMenu = wx.Menu()
131 #i = toolsMenu.Append(-1, 'Batch run...')
132 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
133 #self.normalModeOnlyItems.append(i)
135 if minecraftImport.hasMinecraft():
136 i = toolsMenu.Append(-1, _("Minecraft map import..."))
137 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
139 if version.isDevVersion():
140 i = toolsMenu.Append(-1, _("PID Debugger..."))
141 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
142 i = toolsMenu.Append(-1, _("Auto Firmware Update..."))
143 self.Bind(wx.EVT_MENU, self.OnAutoFirmwareUpdate, i)
145 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
146 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
148 toolsMenu.AppendSeparator()
149 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
150 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
151 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
152 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
153 if profile.getPreference('oneAtATime') == 'True':
154 self.oneAtATime.Check(True)
156 self.allAtOnceItem.Check(True)
158 self.menubar.Append(toolsMenu, _("Tools"))
160 #Machine menu for machine configuration/tooling
161 self.machineMenu = wx.Menu()
162 self.updateMachineMenu()
164 self.menubar.Append(self.machineMenu, _("Machine"))
166 expertMenu = wx.Menu()
167 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
168 self.switchToQuickprintMenuItem = i
169 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
171 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
172 self.switchToNormalMenuItem = i
173 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
174 expertMenu.AppendSeparator()
176 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
177 self.normalModeOnlyItems.append(i)
178 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
179 expertMenu.AppendSeparator()
180 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
181 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
182 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
183 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
185 self.menubar.Append(expertMenu, _("Expert"))
188 i = helpMenu.Append(-1, _("Online documentation..."))
189 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
190 i = helpMenu.Append(-1, _("Report a problem..."))
191 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
192 i = helpMenu.Append(-1, _("Check for update..."))
193 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
194 i = helpMenu.Append(-1, _("Open YouMagine website..."))
195 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
196 i = helpMenu.Append(-1, _("About Cura..."))
197 self.Bind(wx.EVT_MENU, self.OnAbout, i)
198 self.menubar.Append(helpMenu, _("Help"))
199 self.SetMenuBar(self.menubar)
201 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
202 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
203 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
204 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
207 self.scene = sceneView.SceneView(self.rightPane)
210 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, self.scene.sceneUpdated)
211 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, self.scene.sceneUpdated)
213 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
214 self.leftSizer.Add(self.simpleSettingsPanel, 1)
215 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
216 self.leftPane.SetSizer(self.leftSizer)
218 #Main sizer, to position the preview window, buttons and tab control
219 sizer = wx.BoxSizer()
220 self.rightPane.SetSizer(sizer)
221 sizer.Add(self.scene, 1, flag=wx.EXPAND)
224 sizer = wx.BoxSizer(wx.VERTICAL)
226 sizer.Add(self.splitter, 1, wx.EXPAND)
230 self.updateProfileToAllControls()
232 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
234 self.simpleSettingsPanel.Show(False)
235 self.normalSettingsPanel.Show(False)
237 # Set default window size & position
238 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
241 #Timer set; used to check if profile is on the clipboard
242 self.timer = wx.Timer(self)
243 self.Bind(wx.EVT_TIMER, self.onTimer)
244 self.timer.Start(1000)
245 self.lastTriedClipboard = profile.getProfileString()
247 # Restore the window position, size & state from the preferences file
249 if profile.getPreference('window_maximized') == 'True':
252 posx = int(profile.getPreference('window_pos_x'))
253 posy = int(profile.getPreference('window_pos_y'))
254 width = int(profile.getPreference('window_width'))
255 height = int(profile.getPreference('window_height'))
256 if posx > 0 or posy > 0:
257 self.SetPosition((posx,posy))
258 if width > 0 and height > 0:
259 self.SetSize((width,height))
261 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
263 self.normalSashPos = 0
265 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
266 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
268 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
270 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
272 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
274 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
275 self.SetSize((800,600))
278 self.updateSliceMode()
279 self.scene.SetFocus()
280 self.dialogframe = None
281 if Publisher is not None:
282 Publisher().subscribe(self.onPluginUpdate, "pluginupdate")
284 def onPluginUpdate(self,msg): #receives commands from the plugin thread
285 cmd = str(msg.data).split(";")
286 if cmd[0] == "OpenPluginProgressWindow":
287 if len(cmd)==1: #no titel received
289 if len(cmd)<3: #no message text received
290 cmd.append("Plugin is executed...")
293 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)
294 self.dialogpanel = wx.Panel(self.dialogframe, -1, pos = (0,0), size = (dialogwidth,dialogheight))
295 self.dlgtext = wx.StaticText(self.dialogpanel, label = cmd[2], pos = (10,10), size = (280,40))
296 self.dlgbar = wx.Gauge(self.dialogpanel,-1, 100, pos = (10,50), size = (280,20), style = wx.GA_HORIZONTAL)
297 self.dialogframe.Show()
299 elif cmd[0] == "Progress":
301 if number <= 100 and self.dialogframe is not None:
302 self.dlgbar.SetValue(number)
304 self.dlgbar.SetValue(100)
305 elif cmd[0] == "ClosePluginProgressWindow":
306 self.dialogframe.Destroy()
307 self.dialogframe=None
309 print "Unknown Plugin update received: " + cmd[0]
311 def onTimer(self, e):
312 #Check if there is something in the clipboard
315 if not wx.TheClipboard.IsOpened():
316 if not wx.TheClipboard.Open():
318 do = wx.TextDataObject()
319 if wx.TheClipboard.GetData(do):
320 profileString = do.GetText()
321 wx.TheClipboard.Close()
323 startTag = "CURA_PROFILE_STRING:"
324 if startTag in profileString:
325 #print "Found correct syntax on clipboard"
326 profileString = profileString.replace("\n","").strip()
327 profileString = profileString[profileString.find(startTag)+len(startTag):]
328 if profileString != self.lastTriedClipboard:
330 self.lastTriedClipboard = profileString
331 profile.setProfileFromString(profileString)
332 self.scene.notification.message("Loaded new profile from clipboard.")
333 self.updateProfileToAllControls()
335 print "Unable to read from clipboard"
338 def updateSliceMode(self):
339 isSimple = profile.getPreference('startMode') == 'Simple'
341 self.normalSettingsPanel.Show(not isSimple)
342 self.simpleSettingsPanel.Show(isSimple)
343 self.leftPane.Layout()
345 for i in self.normalModeOnlyItems:
346 i.Enable(not isSimple)
348 self.switchToQuickprintMenuItem.Check()
350 self.switchToNormalMenuItem.Check()
352 # Set splitter sash position & size
354 # Save normal mode sash
355 self.normalSashPos = self.splitter.GetSashPosition()
357 # Change location of sash to width of quick mode pane
358 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
359 self.splitter.SetSashPosition(width, True)
362 self.splitter.SetSashSize(0)
364 self.splitter.SetSashPosition(self.normalSashPos, True)
366 self.splitter.SetSashSize(4)
367 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
368 if profile.getMachineSetting('machine_type') == 'ultimaker2':
369 self.bedLevelWizardMenuItem.Enable(False)
370 self.headOffsetWizardMenuItem.Enable(False)
371 if int(profile.getMachineSetting('extruder_amount')) < 2:
372 self.headOffsetWizardMenuItem.Enable(False)
373 self.scene.updateProfileToControls()
374 self.scene._scene.pushFree()
376 def onOneAtATimeSwitch(self, e):
377 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
378 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
379 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)
380 self.scene.updateProfileToControls()
381 self.scene._scene.pushFree()
382 self.scene.sceneUpdated()
384 def OnPreferences(self, e):
385 prefDialog = preferencesDialog.preferencesDialog(self)
389 wx.CallAfter(prefDialog.Show)
391 def OnMachineSettings(self, e):
392 prefDialog = preferencesDialog.machineSettingsDialog(self)
397 def OnDropFiles(self, files):
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 configWizard.ConfigWizard(True)
558 self.reloadSettingPanels()
559 self.updateMachineMenu()
561 def OnSelectMachine(self, index):
562 profile.setActiveMachine(index)
563 self.reloadSettingPanels()
565 def OnBedLevelWizard(self, e):
566 configWizard.bedLevelWizard()
568 def OnHeadOffsetWizard(self, e):
569 configWizard.headOffsetWizard()
571 def OnExpertOpen(self, e):
572 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
576 def OnMinecraftImport(self, e):
577 mi = minecraftImport.minecraftImportWindow(self)
581 def OnPIDDebugger(self, e):
582 debugger = pidDebugger.debuggerWindow(self)
586 def OnAutoFirmwareUpdate(self, e):
587 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
588 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
589 if dlg.ShowModal() == wx.ID_OK:
590 filename = dlg.GetPath()
592 if not(os.path.exists(filename)):
594 #For some reason my Ubuntu 10.10 crashes here.
595 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
597 def onCopyProfileClipboard(self, e):
599 if not wx.TheClipboard.IsOpened():
600 wx.TheClipboard.Open()
601 clipData = wx.TextDataObject()
602 self.lastTriedClipboard = profile.getProfileString()
603 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
604 clipData.SetText(profileString)
605 wx.TheClipboard.SetData(clipData)
606 wx.TheClipboard.Close()
608 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
610 def OnCheckForUpdate(self, e):
611 newVersion = version.checkForNewerVersion()
612 if newVersion is not None:
613 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:
614 webbrowser.open(newVersion)
616 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
618 def OnAbout(self, e):
619 aboutBox = aboutWindow.aboutWindow()
623 def OnClose(self, e):
624 profile.saveProfile(profile.getDefaultProfilePath(), True)
626 # Save the window position, size & state from the preferences file
627 profile.putPreference('window_maximized', self.IsMaximized())
628 if not self.IsMaximized() and not self.IsIconized():
629 (posx, posy) = self.GetPosition()
630 profile.putPreference('window_pos_x', posx)
631 profile.putPreference('window_pos_y', posy)
632 (width, height) = self.GetSize()
633 profile.putPreference('window_width', width)
634 profile.putPreference('window_height', height)
636 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
637 isSimple = profile.getPreference('startMode') == 'Simple'
639 self.normalSashPos = self.splitter.GetSashPosition()
640 profile.putPreference('window_normal_sash', self.normalSashPos)
642 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
644 self.scene.OnPaint = lambda e : e
645 self.scene._engine.cleanup()
651 class normalSettingsPanel(configBase.configPanelBase):
652 "Main user interface window"
653 def __init__(self, parent, callback = None):
654 super(normalSettingsPanel, self).__init__(parent, callback)
657 self.nb = wx.Notebook(self)
658 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
659 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
661 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
662 self._addSettingsToPanels('basic', left, right)
663 self.SizeLabelWidths(left, right)
665 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
666 self._addSettingsToPanels('advanced', left, right)
667 self.SizeLabelWidths(left, right)
670 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
671 self.nb.AddPage(self.pluginPanel, _("Plugins"))
674 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
675 self.alterationPanel = None
677 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
678 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
680 self.Bind(wx.EVT_SIZE, self.OnSize)
682 self.nb.SetSize(self.GetSize())
683 self.UpdateSize(self.printPanel)
684 self.UpdateSize(self.advancedPanel)
686 def _addSettingsToPanels(self, category, left, right):
687 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
691 for title in profile.getSubCategoriesFor(category):
692 n += 1 + len(profile.getSettingsForCategory(category, title))
695 configBase.TitleRow(p, _(title))
696 for s in profile.getSettingsForCategory(category, title):
697 configBase.SettingRow(p, s.getName())
699 def SizeLabelWidths(self, left, right):
700 leftWidth = self.getLabelColumnWidth(left)
701 rightWidth = self.getLabelColumnWidth(right)
702 maxWidth = max(leftWidth, rightWidth)
703 self.setLabelColumnWidth(left, maxWidth)
704 self.setLabelColumnWidth(right, maxWidth)
707 # Make the size of the Notebook control the same size as this control
708 self.nb.SetSize(self.GetSize())
710 # Propegate the OnSize() event (just in case)
713 # Perform out resize magic
714 self.UpdateSize(self.printPanel)
715 self.UpdateSize(self.advancedPanel)
717 def UpdateSize(self, configPanel):
718 sizer = configPanel.GetSizer()
722 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
725 # if width(col1) > (best_width(col1) + best_width(col1)):
726 # switch to horizontal
729 col1 = configPanel.leftPanel
730 colSize1 = col1.GetSize()
731 colBestSize1 = col1.GetBestSize()
732 col2 = configPanel.rightPanel
733 colSize2 = col2.GetSize()
734 colBestSize2 = col2.GetBestSize()
736 orientation = sizer.GetOrientation()
738 if orientation == wx.HORIZONTAL:
739 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
741 sizer = wx.BoxSizer(wx.VERTICAL)
742 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
743 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
744 configPanel.SetSizer(sizer)
750 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
752 sizer = wx.BoxSizer(wx.HORIZONTAL)
753 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
754 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
755 configPanel.SetSizer(sizer)
761 def updateProfileToControls(self):
762 super(normalSettingsPanel, self).updateProfileToControls()
763 if self.alterationPanel is not None:
764 self.alterationPanel.updateProfileToControls()
765 self.pluginPanel.updateProfileToControls()