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 i = expertMenu.Append(-1, _("Run first run wizard..."))
177 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
178 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
179 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
180 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
181 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
183 self.menubar.Append(expertMenu, _("Expert"))
186 i = helpMenu.Append(-1, _("Online documentation..."))
187 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://lulzbot.com/cura'), i)
188 i = helpMenu.Append(-1, _("Report a problem..."))
189 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/alephobjects/Cura/issues'), i)
190 #i = helpMenu.Append(-1, _("Check for update..."))
191 #self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
192 #i = helpMenu.Append(-1, _("Open YouMagine website..."))
193 #self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
194 i = helpMenu.Append(-1, _("About Cura..."))
195 self.Bind(wx.EVT_MENU, self.OnAbout, i)
196 self.menubar.Append(helpMenu, _("Help"))
197 self.SetMenuBar(self.menubar)
199 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
200 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
201 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
202 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
205 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
206 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
208 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
209 self.leftSizer.Add(self.simpleSettingsPanel, 1)
210 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
211 self.leftPane.SetSizer(self.leftSizer)
214 self.scene = sceneView.SceneView(self.rightPane)
216 #Main sizer, to position the preview window, buttons and tab control
217 sizer = wx.BoxSizer()
218 self.rightPane.SetSizer(sizer)
219 sizer.Add(self.scene, 1, flag=wx.EXPAND)
222 sizer = wx.BoxSizer(wx.VERTICAL)
224 sizer.Add(self.splitter, 1, wx.EXPAND)
228 self.updateProfileToAllControls()
230 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
232 self.simpleSettingsPanel.Show(False)
233 self.normalSettingsPanel.Show(False)
235 # Set default window size & position
236 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
239 #Timer set; used to check if profile is on the clipboard
240 self.timer = wx.Timer(self)
241 self.Bind(wx.EVT_TIMER, self.onTimer)
242 self.timer.Start(1000)
243 self.lastTriedClipboard = profile.getProfileString()
245 # Restore the window position, size & state from the preferences file
247 if profile.getPreference('window_maximized') == 'True':
250 posx = int(profile.getPreference('window_pos_x'))
251 posy = int(profile.getPreference('window_pos_y'))
252 width = int(profile.getPreference('window_width'))
253 height = int(profile.getPreference('window_height'))
254 if posx > 0 or posy > 0:
255 self.SetPosition((posx,posy))
256 if width > 0 and height > 0:
257 self.SetSize((width,height))
259 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
261 self.normalSashPos = 0
263 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
264 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
266 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
268 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
270 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
272 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
273 self.SetSize((800,600))
276 self.updateSliceMode()
277 self.scene.SetFocus()
278 self.dialogframe = None
279 Publisher().subscribe(self.onPluginUpdate, "pluginupdate")
281 def onPluginUpdate(self,msg): #receives commands from the plugin thread
282 cmd = str(msg.data).split(";")
283 if cmd[0] == "OpenPluginProgressWindow":
284 if len(cmd)==1: #no titel received
286 if len(cmd)<3: #no message text received
287 cmd.append("Plugin is executed...")
290 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)
291 self.dialogpanel = wx.Panel(self.dialogframe, -1, pos = (0,0), size = (dialogwidth,dialogheight))
292 self.dlgtext = wx.StaticText(self.dialogpanel, label = cmd[2], pos = (10,10), size = (280,40))
293 self.dlgbar = wx.Gauge(self.dialogpanel,-1, 100, pos = (10,50), size = (280,20), style = wx.GA_HORIZONTAL)
294 self.dialogframe.Show()
296 elif cmd[0] == "Progress":
298 if number <= 100 and self.dialogframe is not None:
299 self.dlgbar.SetValue(number)
301 self.dlgbar.SetValue(100)
302 elif cmd[0] == "ClosePluginProgressWindow":
303 self.dialogframe.Destroy()
304 self.dialogframe=None
306 print "Unknown Plugin update received: " + cmd[0]
308 def onTimer(self, e):
309 #Check if there is something in the clipboard
312 if not wx.TheClipboard.IsOpened():
313 if not wx.TheClipboard.Open():
315 do = wx.TextDataObject()
316 if wx.TheClipboard.GetData(do):
317 profileString = do.GetText()
318 wx.TheClipboard.Close()
320 startTag = "CURA_PROFILE_STRING:"
321 if startTag in profileString:
322 #print "Found correct syntax on clipboard"
323 profileString = profileString.replace("\n","").strip()
324 profileString = profileString[profileString.find(startTag)+len(startTag):]
325 if profileString != self.lastTriedClipboard:
327 self.lastTriedClipboard = profileString
328 profile.setProfileFromString(profileString)
329 self.scene.notification.message(_("Loaded new profile from clipboard."))
330 self.updateProfileToAllControls()
332 print "Unable to read from clipboard"
335 def updateSliceMode(self):
336 isSimple = profile.getPreference('startMode') == 'Simple'
338 self.normalSettingsPanel.Show(not isSimple)
339 self.simpleSettingsPanel.Show(isSimple)
340 self.leftPane.Layout()
342 for i in self.normalModeOnlyItems:
343 i.Enable(not isSimple)
345 self.switchToQuickprintMenuItem.Check()
347 self.switchToNormalMenuItem.Check()
349 # Set splitter sash position & size
351 # Save normal mode sash
352 self.normalSashPos = self.splitter.GetSashPosition()
354 # Change location of sash to width of quick mode pane
355 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
356 self.splitter.SetSashPosition(width, True)
359 self.splitter.SetSashSize(0)
361 self.splitter.SetSashPosition(self.normalSashPos, True)
363 self.splitter.SetSashSize(4)
364 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
365 if profile.getMachineSetting('machine_type') == 'ultimaker2' or profile.getMachineSetting('machine_type') == 'lulzbot_mini' or profile.getMachineSetting('machine_type') == 'lulzbot_TAZ':
366 self.bedLevelWizardMenuItem.Enable(False)
367 self.headOffsetWizardMenuItem.Enable(False)
369 self.bedLevelWizardMenuItem.Enable(True)
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):
399 self.updateProfileToAllControls()
400 self.scene.loadFiles(files)
402 def OnModelMRU(self, e):
403 fileNum = e.GetId() - self.ID_MRU_MODEL1
404 path = self.modelFileHistory.GetHistoryFile(fileNum)
406 self.modelFileHistory.AddFileToHistory(path) # move up the list
407 self.config.SetPath("/ModelMRU")
408 self.modelFileHistory.Save(self.config)
411 profile.putPreference('lastFile', path)
413 self.scene.loadFiles(filelist)
415 def addToModelMRU(self, file):
416 self.modelFileHistory.AddFileToHistory(file)
417 self.config.SetPath("/ModelMRU")
418 self.modelFileHistory.Save(self.config)
421 def OnProfileMRU(self, e):
422 fileNum = e.GetId() - self.ID_MRU_PROFILE1
423 path = self.profileFileHistory.GetHistoryFile(fileNum)
425 self.profileFileHistory.AddFileToHistory(path) # move up the list
426 self.config.SetPath("/ProfileMRU")
427 self.profileFileHistory.Save(self.config)
430 profile.loadProfile(path)
431 self.updateProfileToAllControls()
433 def addToProfileMRU(self, file):
434 self.profileFileHistory.AddFileToHistory(file)
435 self.config.SetPath("/ProfileMRU")
436 self.profileFileHistory.Save(self.config)
439 def updateProfileToAllControls(self):
440 self.scene.updateProfileToControls()
441 self.normalSettingsPanel.updateProfileToControls()
442 self.simpleSettingsPanel.updateProfileToControls()
444 def reloadSettingPanels(self):
445 self.leftSizer.Detach(self.simpleSettingsPanel)
446 self.leftSizer.Detach(self.normalSettingsPanel)
447 self.simpleSettingsPanel.Destroy()
448 self.normalSettingsPanel.Destroy()
449 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
450 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
451 self.leftSizer.Add(self.simpleSettingsPanel, 1)
452 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
453 self.updateSliceMode()
454 self.updateProfileToAllControls()
456 def updateMachineMenu(self):
457 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
458 for item in self.machineMenu.GetMenuItems():
459 self.machineMenu.RemoveItem(item)
461 #Add a menu item for each machine configuration.
462 for n in xrange(0, profile.getMachineCount()):
463 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
464 if n == int(profile.getPreferenceFloat('active_machine')):
466 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
468 self.machineMenu.AppendSeparator()
470 i = self.machineMenu.Append(-1, _("Machine settings..."))
471 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
473 #Add tools for machines.
474 self.machineMenu.AppendSeparator()
476 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
477 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
479 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
480 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
482 def OnLoadProfile(self, e):
483 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)
484 dlg.SetWildcard("ini files (*.ini)|*.ini")
485 if dlg.ShowModal() == wx.ID_OK:
486 profileFile = dlg.GetPath()
487 profile.loadProfile(profileFile)
488 self.updateProfileToAllControls()
490 # Update the Profile MRU
491 self.addToProfileMRU(profileFile)
494 def OnLoadProfileFromGcode(self, e):
495 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)
496 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
497 if dlg.ShowModal() == wx.ID_OK:
498 gcodeFile = dlg.GetPath()
499 f = open(gcodeFile, 'r')
502 if line.startswith(';CURA_PROFILE_STRING:'):
503 profile.setProfileFromString(line[line.find(':')+1:].strip())
504 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
505 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
508 self.updateProfileToAllControls()
510 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)
513 def OnSaveProfile(self, e):
514 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
515 dlg.SetWildcard("ini files (*.ini)|*.ini")
516 if dlg.ShowModal() == wx.ID_OK:
517 profileFile = dlg.GetPath()
518 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
519 profileFile += '.ini'
520 profile.saveProfile(profileFile)
523 def OnResetProfile(self, e):
524 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)
525 result = dlg.ShowModal() == wx.ID_YES
528 profile.resetProfile()
529 self.updateProfileToAllControls()
531 def OnSimpleSwitch(self, e):
532 profile.putPreference('startMode', 'Simple')
533 self.updateSliceMode()
535 def OnNormalSwitch(self, e):
536 profile.putPreference('startMode', 'Normal')
537 self.updateSliceMode()
539 def OnDefaultMarlinFirmware(self, e):
540 firmwareInstall.InstallFirmware(self)
542 def OnCustomFirmware(self, e):
543 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
544 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)
545 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
546 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
547 if dlg.ShowModal() == wx.ID_OK:
548 filename = dlg.GetPath()
550 if not(os.path.exists(filename)):
552 #For some reason my Ubuntu 10.10 crashes here.
553 firmwareInstall.InstallFirmware(self, filename)
555 def OnFirstRunWizard(self, e):
557 configWizard.configWizard()
559 self.reloadSettingPanels()
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()