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://daid.github.com/Cura'), i)
194 i = helpMenu.Append(-1, _("Report a problem..."))
195 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/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):
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
359 self.normalSashPos = self.splitter.GetSashPosition()
361 # Change location of sash to width of quick mode pane
362 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
363 self.splitter.SetSashPosition(width, True)
366 self.splitter.SetSashSize(0)
368 self.splitter.SetSashPosition(self.normalSashPos, True)
370 self.splitter.SetSashSize(4)
371 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
372 if profile.getMachineSetting('machine_type').startswith('ultimaker2'):
373 self.bedLevelWizardMenuItem.Enable(False)
374 self.headOffsetWizardMenuItem.Enable(False)
375 if int(profile.getMachineSetting('extruder_amount')) < 2:
376 self.headOffsetWizardMenuItem.Enable(False)
377 self.scene.updateProfileToControls()
378 self.scene._scene.pushFree()
380 def onOneAtATimeSwitch(self, e):
381 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
382 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
383 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)
384 self.scene.updateProfileToControls()
385 self.scene._scene.pushFree()
386 self.scene.sceneUpdated()
388 def OnPreferences(self, e):
389 prefDialog = preferencesDialog.preferencesDialog(self)
393 wx.CallAfter(prefDialog.Show)
395 def OnMachineSettings(self, e):
396 prefDialog = preferencesDialog.machineSettingsDialog(self)
401 def OnDropFiles(self, files):
402 self.scene.loadFiles(files)
404 def OnModelMRU(self, e):
405 fileNum = e.GetId() - self.ID_MRU_MODEL1
406 path = self.modelFileHistory.GetHistoryFile(fileNum)
408 self.modelFileHistory.AddFileToHistory(path) # move up the list
409 self.config.SetPath("/ModelMRU")
410 self.modelFileHistory.Save(self.config)
413 profile.putPreference('lastFile', path)
415 self.scene.loadFiles(filelist)
417 def addToModelMRU(self, file):
418 self.modelFileHistory.AddFileToHistory(file)
419 self.config.SetPath("/ModelMRU")
420 self.modelFileHistory.Save(self.config)
423 def OnProfileMRU(self, e):
424 fileNum = e.GetId() - self.ID_MRU_PROFILE1
425 path = self.profileFileHistory.GetHistoryFile(fileNum)
427 self.profileFileHistory.AddFileToHistory(path) # move up the list
428 self.config.SetPath("/ProfileMRU")
429 self.profileFileHistory.Save(self.config)
432 profile.loadProfile(path)
433 self.updateProfileToAllControls()
435 def addToProfileMRU(self, file):
436 self.profileFileHistory.AddFileToHistory(file)
437 self.config.SetPath("/ProfileMRU")
438 self.profileFileHistory.Save(self.config)
441 def updateProfileToAllControls(self):
442 self.scene.updateProfileToControls()
443 self.normalSettingsPanel.updateProfileToControls()
444 self.simpleSettingsPanel.updateProfileToControls()
446 def reloadSettingPanels(self):
447 self.leftSizer.Detach(self.simpleSettingsPanel)
448 self.leftSizer.Detach(self.normalSettingsPanel)
449 self.simpleSettingsPanel.Destroy()
450 self.normalSettingsPanel.Destroy()
451 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
452 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
453 self.leftSizer.Add(self.simpleSettingsPanel, 1)
454 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
455 self.updateSliceMode()
456 self.updateProfileToAllControls()
458 def updateMachineMenu(self):
459 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
460 for item in self.machineMenu.GetMenuItems():
461 self.machineMenu.RemoveItem(item)
463 #Add a menu item for each machine configuration.
464 for n in xrange(0, profile.getMachineCount()):
465 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
466 if n == int(profile.getPreferenceFloat('active_machine')):
468 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
470 self.machineMenu.AppendSeparator()
471 i = self.machineMenu.Append(-1, _("Add new machine..."))
472 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
473 i = self.machineMenu.Append(-1, _("Machine settings..."))
474 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
476 #Add tools for machines.
477 self.machineMenu.AppendSeparator()
479 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
480 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
482 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
483 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
485 def OnLoadProfile(self, e):
486 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)
487 dlg.SetWildcard("ini files (*.ini)|*.ini")
488 if dlg.ShowModal() == wx.ID_OK:
489 profileFile = dlg.GetPath()
490 profile.loadProfile(profileFile)
491 self.updateProfileToAllControls()
493 # Update the Profile MRU
494 self.addToProfileMRU(profileFile)
497 def OnLoadProfileFromGcode(self, e):
498 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)
499 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
500 if dlg.ShowModal() == wx.ID_OK:
501 gcodeFile = dlg.GetPath()
502 f = open(gcodeFile, 'r')
505 if line.startswith(';CURA_PROFILE_STRING:'):
506 profile.setProfileFromString(line[line.find(':')+1:].strip())
507 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
508 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
511 self.updateProfileToAllControls()
513 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)
516 def OnSaveProfile(self, e):
517 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
518 dlg.SetWildcard("ini files (*.ini)|*.ini")
519 if dlg.ShowModal() == wx.ID_OK:
520 profile_filename = dlg.GetPath()
521 if not profile_filename.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
522 profile_filename += '.ini'
523 profile.saveProfile(profile_filename)
526 def OnSaveDifferences(self, e):
527 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
528 dlg.SetWildcard("ini files (*.ini)|*.ini")
529 if dlg.ShowModal() == wx.ID_OK:
530 profile_filename = dlg.GetPath()
531 if not profile_filename.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
532 profile_filename += '.ini'
533 profile.saveProfileDifferenceFromDefault(profile_filename)
536 def OnResetProfile(self, e):
537 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)
538 result = dlg.ShowModal() == wx.ID_YES
541 profile.resetProfile()
542 self.updateProfileToAllControls()
544 def OnSimpleSwitch(self, e):
545 profile.putPreference('startMode', 'Simple')
546 self.updateSliceMode()
548 def OnNormalSwitch(self, e):
549 profile.putPreference('startMode', 'Normal')
550 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)
551 result = dlg.ShowModal() == wx.ID_YES
554 profile.resetProfile()
555 for k, v in self.simpleSettingsPanel.getSettingOverrides().items():
556 profile.putProfileSetting(k, v)
557 self.updateProfileToAllControls()
558 self.updateSliceMode()
560 def OnDefaultMarlinFirmware(self, e):
561 firmwareInstall.InstallFirmware(self)
563 def OnCustomFirmware(self, e):
564 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
565 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)
566 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
567 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
568 if dlg.ShowModal() == wx.ID_OK:
569 filename = dlg.GetPath()
571 if not(os.path.exists(filename)):
573 #For some reason my Ubuntu 10.10 crashes here.
574 firmwareInstall.InstallFirmware(self, filename)
576 def OnAddNewMachine(self, e):
578 configWizard.ConfigWizard(True)
580 self.reloadSettingPanels()
581 self.updateMachineMenu()
583 def OnSelectMachine(self, index):
584 profile.setActiveMachine(index)
585 self.reloadSettingPanels()
587 def OnBedLevelWizard(self, e):
588 configWizard.bedLevelWizard()
590 def OnHeadOffsetWizard(self, e):
591 configWizard.headOffsetWizard()
593 def OnExpertOpen(self, e):
594 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
598 def OnMinecraftImport(self, e):
599 mi = minecraftImport.minecraftImportWindow(self)
603 def OnPIDDebugger(self, e):
604 debugger = pidDebugger.debuggerWindow(self)
608 def OnAutoFirmwareUpdate(self, e):
609 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
610 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
611 if dlg.ShowModal() == wx.ID_OK:
612 filename = dlg.GetPath()
614 if not(os.path.exists(filename)):
616 #For some reason my Ubuntu 10.10 crashes here.
617 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
619 def onCopyProfileClipboard(self, e):
621 if not wx.TheClipboard.IsOpened():
622 wx.TheClipboard.Open()
623 clipData = wx.TextDataObject()
624 self.lastTriedClipboard = profile.getProfileString()
625 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
626 clipData.SetText(profileString)
627 wx.TheClipboard.SetData(clipData)
628 wx.TheClipboard.Close()
630 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
632 def OnCheckForUpdate(self, e):
633 newVersion = version.checkForNewerVersion()
634 if newVersion is not None:
635 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:
636 webbrowser.open(newVersion)
638 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
640 def OnAbout(self, e):
641 aboutBox = aboutWindow.aboutWindow()
645 def OnClose(self, e):
646 profile.saveProfile(profile.getDefaultProfilePath(), True)
648 # Save the window position, size & state from the preferences file
649 profile.putPreference('window_maximized', self.IsMaximized())
650 if not self.IsMaximized() and not self.IsIconized():
651 (posx, posy) = self.GetPosition()
652 profile.putPreference('window_pos_x', posx)
653 profile.putPreference('window_pos_y', posy)
654 (width, height) = self.GetSize()
655 profile.putPreference('window_width', width)
656 profile.putPreference('window_height', height)
658 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
659 isSimple = profile.getPreference('startMode') == 'Simple'
661 self.normalSashPos = self.splitter.GetSashPosition()
662 profile.putPreference('window_normal_sash', self.normalSashPos)
664 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
666 self.scene.OnPaint = lambda e : e
667 self.scene._engine.cleanup()
673 class normalSettingsPanel(configBase.configPanelBase):
674 "Main user interface window"
675 def __init__(self, parent, callback = None):
676 super(normalSettingsPanel, self).__init__(parent, callback)
679 self.nb = wx.Notebook(self)
680 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
681 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
683 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
684 self._addSettingsToPanels('basic', left, right)
685 self.SizeLabelWidths(left, right)
687 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
688 self._addSettingsToPanels('advanced', left, right)
689 self.SizeLabelWidths(left, right)
692 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
693 self.nb.AddPage(self.pluginPanel, _("Plugins"))
696 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
697 self.alterationPanel = None
699 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
700 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
702 self.Bind(wx.EVT_SIZE, self.OnSize)
704 self.nb.SetSize(self.GetSize())
705 self.UpdateSize(self.printPanel)
706 self.UpdateSize(self.advancedPanel)
708 def _addSettingsToPanels(self, category, left, right):
709 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
713 for title in profile.getSubCategoriesFor(category):
714 n += 1 + len(profile.getSettingsForCategory(category, title))
717 configBase.TitleRow(p, _(title))
718 for s in profile.getSettingsForCategory(category, title):
719 configBase.SettingRow(p, s.getName())
721 def SizeLabelWidths(self, left, right):
722 leftWidth = self.getLabelColumnWidth(left)
723 rightWidth = self.getLabelColumnWidth(right)
724 maxWidth = max(leftWidth, rightWidth)
725 self.setLabelColumnWidth(left, maxWidth)
726 self.setLabelColumnWidth(right, maxWidth)
729 # Make the size of the Notebook control the same size as this control
730 self.nb.SetSize(self.GetSize())
732 # Propegate the OnSize() event (just in case)
735 # Perform out resize magic
736 self.UpdateSize(self.printPanel)
737 self.UpdateSize(self.advancedPanel)
739 def UpdateSize(self, configPanel):
740 sizer = configPanel.GetSizer()
744 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
747 # if width(col1) > (best_width(col1) + best_width(col1)):
748 # switch to horizontal
751 col1 = configPanel.leftPanel
752 colSize1 = col1.GetSize()
753 colBestSize1 = col1.GetBestSize()
754 col2 = configPanel.rightPanel
755 colSize2 = col2.GetSize()
756 colBestSize2 = col2.GetBestSize()
758 orientation = sizer.GetOrientation()
760 if orientation == wx.HORIZONTAL:
761 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
763 sizer = wx.BoxSizer(wx.VERTICAL)
764 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
765 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
766 configPanel.SetSizer(sizer)
772 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
774 sizer = wx.BoxSizer(wx.HORIZONTAL)
775 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
776 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
777 configPanel.SetSizer(sizer)
783 def updateProfileToControls(self):
784 super(normalSettingsPanel, self).updateProfileToControls()
785 if self.alterationPanel is not None:
786 self.alterationPanel.updateProfileToControls()
787 self.pluginPanel.updateProfileToControls()