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, changedMode = True):
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 (only if we changed mode from normal
357 self.normalSashPos = self.splitter.GetSashPosition()
359 # Change location of sash to width of quick mode pane
360 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
361 self.splitter.SetSashPosition(width, True)
364 self.splitter.SetSashSize(0)
366 # Only change the sash position if we changed mode from simple
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') == 'ultimaker2' or profile.getMachineSetting('machine_type') == 'lulzbot_mini' or profile.getMachineSetting('machine_type') == 'lulzbot_TAZ':
373 self.bedLevelWizardMenuItem.Enable(False)
374 self.headOffsetWizardMenuItem.Enable(False)
376 self.bedLevelWizardMenuItem.Enable(True)
377 self.headOffsetWizardMenuItem.Enable(False)
378 if int(profile.getMachineSetting('extruder_amount')) < 2:
379 self.headOffsetWizardMenuItem.Enable(False)
380 self.scene.updateProfileToControls()
381 self.scene._scene.pushFree()
383 def onOneAtATimeSwitch(self, e):
384 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
385 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
386 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)
387 self.scene.updateProfileToControls()
388 self.scene._scene.pushFree()
389 self.scene.sceneUpdated()
391 def OnPreferences(self, e):
392 prefDialog = preferencesDialog.preferencesDialog(self)
397 def OnMachineSettings(self, e):
398 prefDialog = preferencesDialog.machineSettingsDialog(self)
403 def OnDropFiles(self, files):
404 self.scene.loadFiles(files)
406 def OnModelMRU(self, e):
407 fileNum = e.GetId() - self.ID_MRU_MODEL1
408 path = self.modelFileHistory.GetHistoryFile(fileNum)
410 self.modelFileHistory.AddFileToHistory(path) # move up the list
411 self.config.SetPath("/ModelMRU")
412 self.modelFileHistory.Save(self.config)
415 profile.putPreference('lastFile', path)
417 self.scene.loadFiles(filelist)
419 def addToModelMRU(self, file):
420 self.modelFileHistory.AddFileToHistory(file)
421 self.config.SetPath("/ModelMRU")
422 self.modelFileHistory.Save(self.config)
425 def OnProfileMRU(self, e):
426 fileNum = e.GetId() - self.ID_MRU_PROFILE1
427 path = self.profileFileHistory.GetHistoryFile(fileNum)
429 self.profileFileHistory.AddFileToHistory(path) # move up the list
430 self.config.SetPath("/ProfileMRU")
431 self.profileFileHistory.Save(self.config)
434 profile.loadProfile(path)
435 self.updateProfileToAllControls()
437 def addToProfileMRU(self, file):
438 self.profileFileHistory.AddFileToHistory(file)
439 self.config.SetPath("/ProfileMRU")
440 self.profileFileHistory.Save(self.config)
443 def updateProfileToAllControls(self):
444 self.scene.updateProfileToControls()
445 self.normalSettingsPanel.updateProfileToControls()
446 self.simpleSettingsPanel.updateProfileToControls()
448 def reloadSettingPanels(self, changedSliceMode = False):
449 self.leftSizer.Detach(self.simpleSettingsPanel)
450 self.leftSizer.Detach(self.normalSettingsPanel)
451 self.simpleSettingsPanel.Destroy()
452 self.normalSettingsPanel.Destroy()
453 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
454 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
455 self.leftSizer.Add(self.simpleSettingsPanel, 1)
456 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
457 self.updateSliceMode(changedSliceMode)
458 self.updateProfileToAllControls()
460 def updateMachineMenu(self):
461 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
462 for item in self.machineMenu.GetMenuItems():
463 self.machineMenu.RemoveItem(item)
465 #Add a menu item for each machine configuration.
466 for n in xrange(0, profile.getMachineCount()):
467 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
468 if n == int(profile.getPreferenceFloat('active_machine')):
470 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
472 self.machineMenu.AppendSeparator()
473 i = self.machineMenu.Append(-1, _("Add new machine..."))
474 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
475 i = self.machineMenu.Append(-1, _("Machine settings..."))
476 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
478 #Add tools for machines.
479 self.machineMenu.AppendSeparator()
481 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
482 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
484 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
485 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
487 def OnLoadProfile(self, e):
488 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)
489 dlg.SetWildcard("ini files (*.ini)|*.ini")
490 if dlg.ShowModal() == wx.ID_OK:
491 profileFile = dlg.GetPath()
492 profile.loadProfile(profileFile)
493 self.updateProfileToAllControls()
495 # Update the Profile MRU
496 self.addToProfileMRU(profileFile)
499 def OnLoadProfileFromGcode(self, e):
500 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)
501 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
502 if dlg.ShowModal() == wx.ID_OK:
503 gcodeFile = dlg.GetPath()
504 f = open(gcodeFile, 'r')
507 if line.startswith(';CURA_PROFILE_STRING:'):
508 profile.setProfileFromString(line[line.find(':')+1:].strip())
509 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
510 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
513 self.updateProfileToAllControls()
515 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)
518 def OnSaveProfile(self, e):
519 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
520 dlg.SetWildcard("ini files (*.ini)|*.ini")
521 if dlg.ShowModal() == wx.ID_OK:
522 profileFile = dlg.GetPath()
523 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
524 profileFile += '.ini'
525 profile.saveProfile(profileFile)
528 def OnResetProfile(self, e):
529 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)
530 result = dlg.ShowModal() == wx.ID_YES
533 profile.resetProfile()
534 self.updateProfileToAllControls()
536 def OnSimpleSwitch(self, e):
537 profile.putPreference('startMode', 'Simple')
538 self.updateSliceMode()
540 def OnNormalSwitch(self, e):
541 profile.putPreference('startMode', 'Normal')
542 self.updateSliceMode()
544 def OnDefaultMarlinFirmware(self, e):
545 firmwareInstall.InstallFirmware(self)
547 def OnCustomFirmware(self, e):
548 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
549 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)
550 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
551 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
552 if dlg.ShowModal() == wx.ID_OK:
553 filename = dlg.GetPath()
555 if not(os.path.exists(filename)):
557 #For some reason my Ubuntu 10.10 crashes here.
558 firmwareInstall.InstallFirmware(self, filename)
560 def OnAddNewMachine(self, e):
562 wasSimple = profile.getPreference('startMode') == 'Simple'
563 configWizard.ConfigWizard(True)
564 isSimple = profile.getPreference('startMode') == 'Simple'
566 self.reloadSettingPanels(isSimple != wasSimple)
567 self.updateMachineMenu()
569 def OnSelectMachine(self, index):
570 profile.setActiveMachine(index)
571 self.reloadSettingPanels(False)
573 def OnBedLevelWizard(self, e):
574 configWizard.bedLevelWizard()
576 def OnHeadOffsetWizard(self, e):
577 configWizard.headOffsetWizard()
579 def OnExpertOpen(self, e):
580 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
584 def OnMinecraftImport(self, e):
585 mi = minecraftImport.minecraftImportWindow(self)
589 def OnPIDDebugger(self, e):
590 debugger = pidDebugger.debuggerWindow(self)
594 def OnAutoFirmwareUpdate(self, e):
595 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
596 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
597 if dlg.ShowModal() == wx.ID_OK:
598 filename = dlg.GetPath()
600 if not(os.path.exists(filename)):
602 #For some reason my Ubuntu 10.10 crashes here.
603 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
605 def onCopyProfileClipboard(self, e):
607 if not wx.TheClipboard.IsOpened():
608 wx.TheClipboard.Open()
609 clipData = wx.TextDataObject()
610 self.lastTriedClipboard = profile.getProfileString()
611 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
612 clipData.SetText(profileString)
613 wx.TheClipboard.SetData(clipData)
614 wx.TheClipboard.Close()
616 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
618 def OnCheckForUpdate(self, e):
619 newVersion = version.checkForNewerVersion()
620 if newVersion is not None:
621 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:
622 webbrowser.open(newVersion)
624 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
626 def OnAbout(self, e):
627 aboutBox = aboutWindow.aboutWindow(self)
632 def OnClose(self, e):
633 profile.saveProfile(profile.getDefaultProfilePath(), True)
635 # Save the window position, size & state from the preferences file
636 profile.putPreference('window_maximized', self.IsMaximized())
637 if not self.IsMaximized() and not self.IsIconized():
638 (posx, posy) = self.GetPosition()
639 profile.putPreference('window_pos_x', posx)
640 profile.putPreference('window_pos_y', posy)
641 (width, height) = self.GetSize()
642 profile.putPreference('window_width', width)
643 profile.putPreference('window_height', height)
645 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
646 isSimple = profile.getPreference('startMode') == 'Simple'
648 self.normalSashPos = self.splitter.GetSashPosition()
649 profile.putPreference('window_normal_sash', self.normalSashPos)
651 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
653 self.scene.OnPaint = lambda e : e
660 class normalSettingsPanel(configBase.configPanelBase):
661 "Main user interface window"
662 def __init__(self, parent, callback = None):
663 super(normalSettingsPanel, self).__init__(parent, callback)
666 self.nb = wx.Notebook(self)
667 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
668 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
670 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
671 self._addSettingsToPanels('basic', left, right)
672 self.SizeLabelWidths(left, right)
674 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
675 self._addSettingsToPanels('advanced', left, right)
676 self.SizeLabelWidths(left, right)
679 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
680 self.nb.AddPage(self.pluginPanel, _("Plugins"))
683 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
684 self.alterationPanel = None
686 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
687 self.nb.AddPage(self.alterationPanel, _("Start/End-GCode"))
689 self.Bind(wx.EVT_SIZE, self.OnSize)
691 self.nb.SetSize(self.GetSize())
692 self.UpdateSize(self.printPanel)
693 self.UpdateSize(self.advancedPanel)
695 def _addSettingsToPanels(self, category, left, right):
696 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
700 for title in profile.getSubCategoriesFor(category):
701 n += 1 + len(profile.getSettingsForCategory(category, title))
704 configBase.TitleRow(p, _(title))
705 for s in profile.getSettingsForCategory(category, title):
706 configBase.SettingRow(p, s.getName())
708 def SizeLabelWidths(self, left, right):
709 leftWidth = self.getLabelColumnWidth(left)
710 rightWidth = self.getLabelColumnWidth(right)
711 maxWidth = max(leftWidth, rightWidth)
712 self.setLabelColumnWidth(left, maxWidth)
713 self.setLabelColumnWidth(right, maxWidth)
716 # Make the size of the Notebook control the same size as this control
717 self.nb.SetSize(self.GetSize())
719 # Propegate the OnSize() event (just in case)
722 # Perform out resize magic
723 self.UpdateSize(self.printPanel)
724 self.UpdateSize(self.advancedPanel)
726 def UpdateSize(self, configPanel):
727 sizer = configPanel.GetSizer()
731 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
734 # if width(col1) > (best_width(col1) + best_width(col1)):
735 # switch to horizontal
738 col1 = configPanel.leftPanel
739 colSize1 = col1.GetSize()
740 colBestSize1 = col1.GetBestSize()
741 col2 = configPanel.rightPanel
742 colSize2 = col2.GetSize()
743 colBestSize2 = col2.GetBestSize()
745 orientation = sizer.GetOrientation()
747 if orientation == wx.HORIZONTAL:
748 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
750 sizer = wx.BoxSizer(wx.VERTICAL)
751 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
752 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
753 configPanel.SetSizer(sizer)
759 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
761 sizer = wx.BoxSizer(wx.HORIZONTAL)
762 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
763 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
764 configPanel.SetSizer(sizer)
770 def updateProfileToControls(self):
771 super(normalSettingsPanel, self).updateProfileToControls()
772 if self.alterationPanel is not None:
773 self.alterationPanel.updateProfileToControls()
774 self.pluginPanel.updateProfileToControls()