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://lulzbot.com/cura'), i)
190 i = helpMenu.Append(-1, _("Report a problem..."))
191 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/alephobjects/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' or profile.getMachineSetting('machine_type') == 'lulzbot_mini' or profile.getMachineSetting('machine_type') == 'lulzbot_TAZ':
369 self.bedLevelWizardMenuItem.Enable(False)
370 self.headOffsetWizardMenuItem.Enable(False)
372 self.bedLevelWizardMenuItem.Enable(True)
373 self.headOffsetWizardMenuItem.Enable(False)
374 if int(profile.getMachineSetting('extruder_amount')) < 2:
375 self.headOffsetWizardMenuItem.Enable(False)
376 self.scene.updateProfileToControls()
377 self.scene._scene.pushFree()
379 def onOneAtATimeSwitch(self, e):
380 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
381 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
382 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)
383 self.scene.updateProfileToControls()
384 self.scene._scene.pushFree()
385 self.scene.sceneUpdated()
387 def OnPreferences(self, e):
388 prefDialog = preferencesDialog.preferencesDialog(self)
392 wx.CallAfter(prefDialog.Show)
394 def OnMachineSettings(self, e):
395 prefDialog = preferencesDialog.machineSettingsDialog(self)
400 def OnDropFiles(self, files):
401 self.scene.loadFiles(files)
403 def OnModelMRU(self, e):
404 fileNum = e.GetId() - self.ID_MRU_MODEL1
405 path = self.modelFileHistory.GetHistoryFile(fileNum)
407 self.modelFileHistory.AddFileToHistory(path) # move up the list
408 self.config.SetPath("/ModelMRU")
409 self.modelFileHistory.Save(self.config)
412 profile.putPreference('lastFile', path)
414 self.scene.loadFiles(filelist)
416 def addToModelMRU(self, file):
417 self.modelFileHistory.AddFileToHistory(file)
418 self.config.SetPath("/ModelMRU")
419 self.modelFileHistory.Save(self.config)
422 def OnProfileMRU(self, e):
423 fileNum = e.GetId() - self.ID_MRU_PROFILE1
424 path = self.profileFileHistory.GetHistoryFile(fileNum)
426 self.profileFileHistory.AddFileToHistory(path) # move up the list
427 self.config.SetPath("/ProfileMRU")
428 self.profileFileHistory.Save(self.config)
431 profile.loadProfile(path)
432 self.updateProfileToAllControls()
434 def addToProfileMRU(self, file):
435 self.profileFileHistory.AddFileToHistory(file)
436 self.config.SetPath("/ProfileMRU")
437 self.profileFileHistory.Save(self.config)
440 def updateProfileToAllControls(self):
441 self.scene.updateProfileToControls()
442 self.normalSettingsPanel.updateProfileToControls()
443 self.simpleSettingsPanel.updateProfileToControls()
445 def reloadSettingPanels(self):
446 self.leftSizer.Detach(self.simpleSettingsPanel)
447 self.leftSizer.Detach(self.normalSettingsPanel)
448 self.simpleSettingsPanel.Destroy()
449 self.normalSettingsPanel.Destroy()
450 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
451 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
452 self.leftSizer.Add(self.simpleSettingsPanel, 1)
453 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
454 self.updateSliceMode()
455 self.updateProfileToAllControls()
457 def updateMachineMenu(self):
458 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
459 for item in self.machineMenu.GetMenuItems():
460 self.machineMenu.RemoveItem(item)
462 #Add a menu item for each machine configuration.
463 for n in xrange(0, profile.getMachineCount()):
464 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
465 if n == int(profile.getPreferenceFloat('active_machine')):
467 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
469 self.machineMenu.AppendSeparator()
470 i = self.machineMenu.Append(-1, _("Add new machine..."))
471 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
472 i = self.machineMenu.Append(-1, _("Machine settings..."))
473 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
475 #Add tools for machines.
476 self.machineMenu.AppendSeparator()
478 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
479 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
481 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
482 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
484 def OnLoadProfile(self, e):
485 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)
486 dlg.SetWildcard("ini files (*.ini)|*.ini")
487 if dlg.ShowModal() == wx.ID_OK:
488 profileFile = dlg.GetPath()
489 profile.loadProfile(profileFile)
490 self.updateProfileToAllControls()
492 # Update the Profile MRU
493 self.addToProfileMRU(profileFile)
496 def OnLoadProfileFromGcode(self, e):
497 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)
498 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
499 if dlg.ShowModal() == wx.ID_OK:
500 gcodeFile = dlg.GetPath()
501 f = open(gcodeFile, 'r')
504 if line.startswith(';CURA_PROFILE_STRING:'):
505 profile.setProfileFromString(line[line.find(':')+1:].strip())
506 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
507 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
510 self.updateProfileToAllControls()
512 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)
515 def OnSaveProfile(self, e):
516 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
517 dlg.SetWildcard("ini files (*.ini)|*.ini")
518 if dlg.ShowModal() == wx.ID_OK:
519 profileFile = dlg.GetPath()
520 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
521 profileFile += '.ini'
522 profile.saveProfile(profileFile)
525 def OnResetProfile(self, e):
526 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)
527 result = dlg.ShowModal() == wx.ID_YES
530 profile.resetProfile()
531 self.updateProfileToAllControls()
533 def OnSimpleSwitch(self, e):
534 profile.putPreference('startMode', 'Simple')
535 self.updateSliceMode()
537 def OnNormalSwitch(self, e):
538 profile.putPreference('startMode', 'Normal')
539 self.updateSliceMode()
541 def OnDefaultMarlinFirmware(self, e):
542 firmwareInstall.InstallFirmware(self)
544 def OnCustomFirmware(self, e):
545 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
546 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)
547 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
548 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
549 if dlg.ShowModal() == wx.ID_OK:
550 filename = dlg.GetPath()
552 if not(os.path.exists(filename)):
554 #For some reason my Ubuntu 10.10 crashes here.
555 firmwareInstall.InstallFirmware(self, filename)
557 def OnAddNewMachine(self, e):
559 configWizard.ConfigWizard(True)
561 self.reloadSettingPanels()
562 self.updateMachineMenu()
564 def OnSelectMachine(self, index):
565 profile.setActiveMachine(index)
566 self.reloadSettingPanels()
568 def OnBedLevelWizard(self, e):
569 configWizard.bedLevelWizard()
571 def OnHeadOffsetWizard(self, e):
572 configWizard.headOffsetWizard()
574 def OnExpertOpen(self, e):
575 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
579 def OnMinecraftImport(self, e):
580 mi = minecraftImport.minecraftImportWindow(self)
584 def OnPIDDebugger(self, e):
585 debugger = pidDebugger.debuggerWindow(self)
589 def OnAutoFirmwareUpdate(self, e):
590 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
591 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
592 if dlg.ShowModal() == wx.ID_OK:
593 filename = dlg.GetPath()
595 if not(os.path.exists(filename)):
597 #For some reason my Ubuntu 10.10 crashes here.
598 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
600 def onCopyProfileClipboard(self, e):
602 if not wx.TheClipboard.IsOpened():
603 wx.TheClipboard.Open()
604 clipData = wx.TextDataObject()
605 self.lastTriedClipboard = profile.getProfileString()
606 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
607 clipData.SetText(profileString)
608 wx.TheClipboard.SetData(clipData)
609 wx.TheClipboard.Close()
611 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
613 def OnCheckForUpdate(self, e):
614 newVersion = version.checkForNewerVersion()
615 if newVersion is not None:
616 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:
617 webbrowser.open(newVersion)
619 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
621 def OnAbout(self, e):
622 aboutBox = aboutWindow.aboutWindow()
626 def OnClose(self, e):
627 profile.saveProfile(profile.getDefaultProfilePath(), True)
629 # Save the window position, size & state from the preferences file
630 profile.putPreference('window_maximized', self.IsMaximized())
631 if not self.IsMaximized() and not self.IsIconized():
632 (posx, posy) = self.GetPosition()
633 profile.putPreference('window_pos_x', posx)
634 profile.putPreference('window_pos_y', posy)
635 (width, height) = self.GetSize()
636 profile.putPreference('window_width', width)
637 profile.putPreference('window_height', height)
639 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
640 isSimple = profile.getPreference('startMode') == 'Simple'
642 self.normalSashPos = self.splitter.GetSashPosition()
643 profile.putPreference('window_normal_sash', self.normalSashPos)
645 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
647 self.scene.OnPaint = lambda e : e
648 self.scene._engine.cleanup()
654 class normalSettingsPanel(configBase.configPanelBase):
655 "Main user interface window"
656 def __init__(self, parent, callback = None):
657 super(normalSettingsPanel, self).__init__(parent, callback)
660 self.nb = wx.Notebook(self)
661 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
662 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
664 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
665 self._addSettingsToPanels('basic', left, right)
666 self.SizeLabelWidths(left, right)
668 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
669 self._addSettingsToPanels('advanced', left, right)
670 self.SizeLabelWidths(left, right)
673 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
674 self.nb.AddPage(self.pluginPanel, _("Plugins"))
677 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
678 self.alterationPanel = None
680 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
681 self.nb.AddPage(self.alterationPanel, _("Start/End-GCode"))
683 self.Bind(wx.EVT_SIZE, self.OnSize)
685 self.nb.SetSize(self.GetSize())
686 self.UpdateSize(self.printPanel)
687 self.UpdateSize(self.advancedPanel)
689 def _addSettingsToPanels(self, category, left, right):
690 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
694 for title in profile.getSubCategoriesFor(category):
695 n += 1 + len(profile.getSettingsForCategory(category, title))
698 configBase.TitleRow(p, _(title))
699 for s in profile.getSettingsForCategory(category, title):
700 configBase.SettingRow(p, s.getName())
702 def SizeLabelWidths(self, left, right):
703 leftWidth = self.getLabelColumnWidth(left)
704 rightWidth = self.getLabelColumnWidth(right)
705 maxWidth = max(leftWidth, rightWidth)
706 self.setLabelColumnWidth(left, maxWidth)
707 self.setLabelColumnWidth(right, maxWidth)
710 # Make the size of the Notebook control the same size as this control
711 self.nb.SetSize(self.GetSize())
713 # Propegate the OnSize() event (just in case)
716 # Perform out resize magic
717 self.UpdateSize(self.printPanel)
718 self.UpdateSize(self.advancedPanel)
720 def UpdateSize(self, configPanel):
721 sizer = configPanel.GetSizer()
725 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
728 # if width(col1) > (best_width(col1) + best_width(col1)):
729 # switch to horizontal
732 col1 = configPanel.leftPanel
733 colSize1 = col1.GetSize()
734 colBestSize1 = col1.GetBestSize()
735 col2 = configPanel.rightPanel
736 colSize2 = col2.GetSize()
737 colBestSize2 = col2.GetBestSize()
739 orientation = sizer.GetOrientation()
741 if orientation == wx.HORIZONTAL:
742 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
744 sizer = wx.BoxSizer(wx.VERTICAL)
745 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
746 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
747 configPanel.SetSizer(sizer)
753 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
755 sizer = wx.BoxSizer(wx.HORIZONTAL)
756 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
757 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
758 configPanel.SetSizer(sizer)
764 def updateProfileToControls(self):
765 super(normalSettingsPanel, self).updateProfileToControls()
766 if self.alterationPanel is not None:
767 self.alterationPanel.updateProfileToControls()
768 self.pluginPanel.updateProfileToControls()