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://daid.github.com/Cura'), i)
186 i = helpMenu.Append(-1, _("Report a problem..."))
187 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/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.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
204 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
206 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
207 self.leftSizer.Add(self.simpleSettingsPanel, 1)
208 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
209 self.leftPane.SetSizer(self.leftSizer)
212 self.scene = sceneView.SceneView(self.rightPane)
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':
364 self.bedLevelWizardMenuItem.Enable(False)
365 self.headOffsetWizardMenuItem.Enable(False)
366 if int(profile.getMachineSetting('extruder_amount')) < 2:
367 self.headOffsetWizardMenuItem.Enable(False)
368 self.scene.updateProfileToControls()
369 self.scene._scene.pushFree()
371 def onOneAtATimeSwitch(self, e):
372 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
373 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
374 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)
375 self.scene.updateProfileToControls()
376 self.scene._scene.pushFree()
377 self.scene.sceneUpdated()
379 def OnPreferences(self, e):
380 prefDialog = preferencesDialog.preferencesDialog(self)
384 wx.CallAfter(prefDialog.Show)
386 def OnMachineSettings(self, e):
387 prefDialog = preferencesDialog.machineSettingsDialog(self)
392 def OnDropFiles(self, files):
394 self.updateProfileToAllControls()
395 self.scene.loadFiles(files)
397 def OnModelMRU(self, e):
398 fileNum = e.GetId() - self.ID_MRU_MODEL1
399 path = self.modelFileHistory.GetHistoryFile(fileNum)
401 self.modelFileHistory.AddFileToHistory(path) # move up the list
402 self.config.SetPath("/ModelMRU")
403 self.modelFileHistory.Save(self.config)
406 profile.putPreference('lastFile', path)
408 self.scene.loadFiles(filelist)
410 def addToModelMRU(self, file):
411 self.modelFileHistory.AddFileToHistory(file)
412 self.config.SetPath("/ModelMRU")
413 self.modelFileHistory.Save(self.config)
416 def OnProfileMRU(self, e):
417 fileNum = e.GetId() - self.ID_MRU_PROFILE1
418 path = self.profileFileHistory.GetHistoryFile(fileNum)
420 self.profileFileHistory.AddFileToHistory(path) # move up the list
421 self.config.SetPath("/ProfileMRU")
422 self.profileFileHistory.Save(self.config)
425 profile.loadProfile(path)
426 self.updateProfileToAllControls()
428 def addToProfileMRU(self, file):
429 self.profileFileHistory.AddFileToHistory(file)
430 self.config.SetPath("/ProfileMRU")
431 self.profileFileHistory.Save(self.config)
434 def updateProfileToAllControls(self):
435 self.scene.updateProfileToControls()
436 self.normalSettingsPanel.updateProfileToControls()
437 self.simpleSettingsPanel.updateProfileToControls()
439 def reloadSettingPanels(self):
440 self.leftSizer.Detach(self.simpleSettingsPanel)
441 self.leftSizer.Detach(self.normalSettingsPanel)
442 self.simpleSettingsPanel.Destroy()
443 self.normalSettingsPanel.Destroy()
444 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
445 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
446 self.leftSizer.Add(self.simpleSettingsPanel, 1)
447 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
448 self.updateSliceMode()
449 self.updateProfileToAllControls()
451 def updateMachineMenu(self):
452 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
453 for item in self.machineMenu.GetMenuItems():
454 self.machineMenu.RemoveItem(item)
456 #Add a menu item for each machine configuration.
457 for n in xrange(0, profile.getMachineCount()):
458 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
459 if n == int(profile.getPreferenceFloat('active_machine')):
461 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
463 self.machineMenu.AppendSeparator()
464 i = self.machineMenu.Append(-1, _("Add new machine..."))
465 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
466 i = self.machineMenu.Append(-1, _("Machine settings..."))
467 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
469 #Add tools for machines.
470 self.machineMenu.AppendSeparator()
472 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
473 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
475 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
476 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
478 def OnLoadProfile(self, e):
479 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)
480 dlg.SetWildcard("ini files (*.ini)|*.ini")
481 if dlg.ShowModal() == wx.ID_OK:
482 profileFile = dlg.GetPath()
483 profile.loadProfile(profileFile)
484 self.updateProfileToAllControls()
486 # Update the Profile MRU
487 self.addToProfileMRU(profileFile)
490 def OnLoadProfileFromGcode(self, e):
491 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)
492 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
493 if dlg.ShowModal() == wx.ID_OK:
494 gcodeFile = dlg.GetPath()
495 f = open(gcodeFile, 'r')
498 if line.startswith(';CURA_PROFILE_STRING:'):
499 profile.setProfileFromString(line[line.find(':')+1:].strip())
500 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
501 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
504 self.updateProfileToAllControls()
506 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)
509 def OnSaveProfile(self, e):
510 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
511 dlg.SetWildcard("ini files (*.ini)|*.ini")
512 if dlg.ShowModal() == wx.ID_OK:
513 profileFile = dlg.GetPath()
514 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
515 profileFile += '.ini'
516 profile.saveProfile(profileFile)
519 def OnResetProfile(self, e):
520 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)
521 result = dlg.ShowModal() == wx.ID_YES
524 profile.resetProfile()
525 self.updateProfileToAllControls()
527 def OnSimpleSwitch(self, e):
528 profile.putPreference('startMode', 'Simple')
529 self.updateSliceMode()
531 def OnNormalSwitch(self, e):
532 profile.putPreference('startMode', 'Normal')
533 self.updateSliceMode()
535 def OnDefaultMarlinFirmware(self, e):
536 firmwareInstall.InstallFirmware(self)
538 def OnCustomFirmware(self, e):
539 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
540 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)
541 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
542 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
543 if dlg.ShowModal() == wx.ID_OK:
544 filename = dlg.GetPath()
546 if not(os.path.exists(filename)):
548 #For some reason my Ubuntu 10.10 crashes here.
549 firmwareInstall.InstallFirmware(self, filename)
551 def OnAddNewMachine(self, e):
553 profile.setActiveMachine(profile.getMachineCount())
554 configWizard.configWizard(True)
556 self.reloadSettingPanels()
557 self.updateMachineMenu()
559 def OnSelectMachine(self, index):
560 profile.setActiveMachine(index)
561 self.reloadSettingPanels()
563 def OnBedLevelWizard(self, e):
564 configWizard.bedLevelWizard()
566 def OnHeadOffsetWizard(self, e):
567 configWizard.headOffsetWizard()
569 def OnExpertOpen(self, e):
570 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
574 def OnMinecraftImport(self, e):
575 mi = minecraftImport.minecraftImportWindow(self)
579 def OnPIDDebugger(self, e):
580 debugger = pidDebugger.debuggerWindow(self)
584 def OnAutoFirmwareUpdate(self, e):
585 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
586 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
587 if dlg.ShowModal() == wx.ID_OK:
588 filename = dlg.GetPath()
590 if not(os.path.exists(filename)):
592 #For some reason my Ubuntu 10.10 crashes here.
593 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
595 def onCopyProfileClipboard(self, e):
597 if not wx.TheClipboard.IsOpened():
598 wx.TheClipboard.Open()
599 clipData = wx.TextDataObject()
600 self.lastTriedClipboard = profile.getProfileString()
601 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
602 clipData.SetText(profileString)
603 wx.TheClipboard.SetData(clipData)
604 wx.TheClipboard.Close()
606 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
608 def OnCheckForUpdate(self, e):
609 newVersion = version.checkForNewerVersion()
610 if newVersion is not None:
611 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:
612 webbrowser.open(newVersion)
614 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
616 def OnAbout(self, e):
617 aboutBox = aboutWindow.aboutWindow()
621 def OnClose(self, e):
622 profile.saveProfile(profile.getDefaultProfilePath(), True)
624 # Save the window position, size & state from the preferences file
625 profile.putPreference('window_maximized', self.IsMaximized())
626 if not self.IsMaximized() and not self.IsIconized():
627 (posx, posy) = self.GetPosition()
628 profile.putPreference('window_pos_x', posx)
629 profile.putPreference('window_pos_y', posy)
630 (width, height) = self.GetSize()
631 profile.putPreference('window_width', width)
632 profile.putPreference('window_height', height)
634 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
635 isSimple = profile.getPreference('startMode') == 'Simple'
637 self.normalSashPos = self.splitter.GetSashPosition()
638 profile.putPreference('window_normal_sash', self.normalSashPos)
640 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
642 self.scene.OnPaint = lambda e : e
643 self.scene._engine.cleanup()
649 class normalSettingsPanel(configBase.configPanelBase):
650 "Main user interface window"
651 def __init__(self, parent, callback = None):
652 super(normalSettingsPanel, self).__init__(parent, callback)
655 self.nb = wx.Notebook(self)
656 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
657 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
659 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
660 self._addSettingsToPanels('basic', left, right)
661 self.SizeLabelWidths(left, right)
663 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
664 self._addSettingsToPanels('advanced', left, right)
665 self.SizeLabelWidths(left, right)
668 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
669 self.nb.AddPage(self.pluginPanel, _("Plugins"))
672 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
673 self.alterationPanel = None
675 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
676 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
678 self.Bind(wx.EVT_SIZE, self.OnSize)
680 self.nb.SetSize(self.GetSize())
681 self.UpdateSize(self.printPanel)
682 self.UpdateSize(self.advancedPanel)
684 def _addSettingsToPanels(self, category, left, right):
685 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
689 for title in profile.getSubCategoriesFor(category):
690 n += 1 + len(profile.getSettingsForCategory(category, title))
693 configBase.TitleRow(p, _(title))
694 for s in profile.getSettingsForCategory(category, title):
695 configBase.SettingRow(p, s.getName())
697 def SizeLabelWidths(self, left, right):
698 leftWidth = self.getLabelColumnWidth(left)
699 rightWidth = self.getLabelColumnWidth(right)
700 maxWidth = max(leftWidth, rightWidth)
701 self.setLabelColumnWidth(left, maxWidth)
702 self.setLabelColumnWidth(right, maxWidth)
705 # Make the size of the Notebook control the same size as this control
706 self.nb.SetSize(self.GetSize())
708 # Propegate the OnSize() event (just in case)
711 # Perform out resize magic
712 self.UpdateSize(self.printPanel)
713 self.UpdateSize(self.advancedPanel)
715 def UpdateSize(self, configPanel):
716 sizer = configPanel.GetSizer()
720 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
723 # if width(col1) > (best_width(col1) + best_width(col1)):
724 # switch to horizontal
727 col1 = configPanel.leftPanel
728 colSize1 = col1.GetSize()
729 colBestSize1 = col1.GetBestSize()
730 col2 = configPanel.rightPanel
731 colSize2 = col2.GetSize()
732 colBestSize2 = col2.GetBestSize()
734 orientation = sizer.GetOrientation()
736 if orientation == wx.HORIZONTAL:
737 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
739 sizer = wx.BoxSizer(wx.VERTICAL)
740 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
741 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
742 configPanel.SetSizer(sizer)
748 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
750 sizer = wx.BoxSizer(wx.HORIZONTAL)
751 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
752 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
753 configPanel.SetSizer(sizer)
759 def updateProfileToControls(self):
760 super(normalSettingsPanel, self).updateProfileToControls()
761 if self.alterationPanel is not None:
762 self.alterationPanel.updateProfileToControls()
763 self.pluginPanel.updateProfileToControls()