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 class mainWindow(wx.Frame):
30 super(mainWindow, self).__init__(None, title=_('Cura - ') + version.getVersion())
32 wx.EVT_CLOSE(self, self.OnClose)
34 # allow dropping any file, restrict later
35 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
37 # 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
38 if sys.platform.startswith('darwin'):
41 nswindow = objc.objc_object(c_void_p=self.MacGetTopLevelWindowRef())
42 view = nswindow.contentView()
43 view.registerForDraggedTypes_([u'NSFilenamesPboardType'])
47 self.normalModeOnlyItems = []
49 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
50 self.config = wx.FileConfig(appName="Cura",
51 localFilename=mruFile,
52 style=wx.CONFIG_USE_LOCAL_FILE)
54 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)]
55 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
56 self.config.SetPath("/ModelMRU")
57 self.modelFileHistory.Load(self.config)
59 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)]
60 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
61 self.config.SetPath("/ProfileMRU")
62 self.profileFileHistory.Load(self.config)
64 self.menubar = wx.MenuBar()
65 self.fileMenu = wx.Menu()
66 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
67 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
68 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
69 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
70 i = self.fileMenu.Append(-1, _("Reload platform\tF5"))
71 self.Bind(wx.EVT_MENU, lambda e: self.scene.reloadScene(e), i)
72 i = self.fileMenu.Append(-1, _("Clear platform"))
73 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
75 self.fileMenu.AppendSeparator()
76 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
77 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnPrintButton(1), i)
78 i = self.fileMenu.Append(-1, _("Save GCode..."))
79 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
80 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
81 self.Bind(wx.EVT_MENU, lambda e: self.scene._showEngineLog(), i)
83 self.fileMenu.AppendSeparator()
84 i = self.fileMenu.Append(-1, _("Open Profile..."))
85 self.normalModeOnlyItems.append(i)
86 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
87 i = self.fileMenu.Append(-1, _("Save Profile..."))
88 self.normalModeOnlyItems.append(i)
89 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
90 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
91 self.normalModeOnlyItems.append(i)
92 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
93 self.fileMenu.AppendSeparator()
94 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
95 self.normalModeOnlyItems.append(i)
96 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
98 self.fileMenu.AppendSeparator()
99 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
100 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
101 i = self.fileMenu.Append(-1, _("Machine settings..."))
102 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
103 self.fileMenu.AppendSeparator()
106 modelHistoryMenu = wx.Menu()
107 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
108 self.modelFileHistory.UseMenu(modelHistoryMenu)
109 self.modelFileHistory.AddFilesToMenu()
110 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
113 profileHistoryMenu = wx.Menu()
114 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
115 self.profileFileHistory.UseMenu(profileHistoryMenu)
116 self.profileFileHistory.AddFilesToMenu()
117 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
119 self.fileMenu.AppendSeparator()
120 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
121 self.Bind(wx.EVT_MENU, self.OnQuit, i)
122 self.menubar.Append(self.fileMenu, '&' + _("File"))
124 toolsMenu = wx.Menu()
125 #i = toolsMenu.Append(-1, 'Batch run...')
126 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
127 #self.normalModeOnlyItems.append(i)
129 if minecraftImport.hasMinecraft():
130 i = toolsMenu.Append(-1, _("Minecraft map import..."))
131 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
133 if version.isDevVersion():
134 i = toolsMenu.Append(-1, _("PID Debugger..."))
135 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
136 i = toolsMenu.Append(-1, _("Auto Firmware Update..."))
137 self.Bind(wx.EVT_MENU, self.OnAutoFirmwareUpdate, i)
139 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
140 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
142 toolsMenu.AppendSeparator()
143 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
144 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
145 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
146 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
147 if profile.getPreference('oneAtATime') == 'True':
148 self.oneAtATime.Check(True)
150 self.allAtOnceItem.Check(True)
152 self.menubar.Append(toolsMenu, _("Tools"))
154 #Machine menu for machine configuration/tooling
155 self.machineMenu = wx.Menu()
156 self.updateMachineMenu()
158 self.menubar.Append(self.machineMenu, _("Machine"))
160 expertMenu = wx.Menu()
161 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
162 self.switchToQuickprintMenuItem = i
163 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
165 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
166 self.switchToNormalMenuItem = i
167 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
168 expertMenu.AppendSeparator()
170 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
171 self.normalModeOnlyItems.append(i)
172 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
173 expertMenu.AppendSeparator()
174 i = expertMenu.Append(-1, _("Run first run wizard..."))
175 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
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://lulzbot.com/cura'), i)
186 i = helpMenu.Append(-1, _("Report a problem..."))
187 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/alephobjects/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()
277 def onTimer(self, e):
278 #Check if there is something in the clipboard
281 if not wx.TheClipboard.IsOpened():
282 if not wx.TheClipboard.Open():
284 do = wx.TextDataObject()
285 if wx.TheClipboard.GetData(do):
286 profileString = do.GetText()
287 wx.TheClipboard.Close()
289 startTag = "CURA_PROFILE_STRING:"
290 if startTag in profileString:
291 #print "Found correct syntax on clipboard"
292 profileString = profileString.replace("\n","").strip()
293 profileString = profileString[profileString.find(startTag)+len(startTag):]
294 if profileString != self.lastTriedClipboard:
296 self.lastTriedClipboard = profileString
297 profile.setProfileFromString(profileString)
298 self.scene.notification.message(_("Loaded new profile from clipboard."))
299 self.updateProfileToAllControls()
301 print "Unable to read from clipboard"
304 def updateSliceMode(self):
305 isSimple = profile.getPreference('startMode') == 'Simple'
307 self.normalSettingsPanel.Show(not isSimple)
308 self.simpleSettingsPanel.Show(isSimple)
309 self.leftPane.Layout()
311 for i in self.normalModeOnlyItems:
312 i.Enable(not isSimple)
314 self.switchToQuickprintMenuItem.Check()
316 self.switchToNormalMenuItem.Check()
318 # Set splitter sash position & size
320 # Save normal mode sash
321 self.normalSashPos = self.splitter.GetSashPosition()
323 # Change location of sash to width of quick mode pane
324 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
325 self.splitter.SetSashPosition(width, True)
328 self.splitter.SetSashSize(0)
330 self.splitter.SetSashPosition(self.normalSashPos, True)
332 self.splitter.SetSashSize(4)
333 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
334 if profile.getMachineSetting('machine_type') == 'ultimaker2' or profile.getMachineSetting('machine_type') == 'lulzbot_mini' or profile.getMachineSetting('machine_type') == 'lulzbot_TAZ':
335 self.bedLevelWizardMenuItem.Enable(False)
336 self.headOffsetWizardMenuItem.Enable(False)
338 self.bedLevelWizardMenuItem.Enable(True)
339 self.headOffsetWizardMenuItem.Enable(False)
340 if int(profile.getMachineSetting('extruder_amount')) < 2:
341 self.headOffsetWizardMenuItem.Enable(False)
342 self.scene.updateProfileToControls()
343 self.scene._scene.pushFree()
345 def onOneAtATimeSwitch(self, e):
346 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
347 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
348 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)
349 self.scene.updateProfileToControls()
350 self.scene._scene.pushFree()
351 self.scene.sceneUpdated()
353 def OnPreferences(self, e):
354 prefDialog = preferencesDialog.preferencesDialog(self)
358 wx.CallAfter(prefDialog.Show)
360 def OnMachineSettings(self, e):
361 prefDialog = preferencesDialog.machineSettingsDialog(self)
366 def OnDropFiles(self, files):
368 self.updateProfileToAllControls()
369 self.scene.loadFiles(files)
371 def OnModelMRU(self, e):
372 fileNum = e.GetId() - self.ID_MRU_MODEL1
373 path = self.modelFileHistory.GetHistoryFile(fileNum)
375 self.modelFileHistory.AddFileToHistory(path) # move up the list
376 self.config.SetPath("/ModelMRU")
377 self.modelFileHistory.Save(self.config)
380 profile.putPreference('lastFile', path)
382 self.scene.loadFiles(filelist)
384 def addToModelMRU(self, file):
385 self.modelFileHistory.AddFileToHistory(file)
386 self.config.SetPath("/ModelMRU")
387 self.modelFileHistory.Save(self.config)
390 def OnProfileMRU(self, e):
391 fileNum = e.GetId() - self.ID_MRU_PROFILE1
392 path = self.profileFileHistory.GetHistoryFile(fileNum)
394 self.profileFileHistory.AddFileToHistory(path) # move up the list
395 self.config.SetPath("/ProfileMRU")
396 self.profileFileHistory.Save(self.config)
399 profile.loadProfile(path)
400 self.updateProfileToAllControls()
402 def addToProfileMRU(self, file):
403 self.profileFileHistory.AddFileToHistory(file)
404 self.config.SetPath("/ProfileMRU")
405 self.profileFileHistory.Save(self.config)
408 def updateProfileToAllControls(self):
409 self.scene.updateProfileToControls()
410 self.normalSettingsPanel.updateProfileToControls()
411 self.simpleSettingsPanel.updateProfileToControls()
413 def reloadSettingPanels(self):
414 self.leftSizer.Detach(self.simpleSettingsPanel)
415 self.leftSizer.Detach(self.normalSettingsPanel)
416 self.simpleSettingsPanel.Destroy()
417 self.normalSettingsPanel.Destroy()
418 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
419 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
420 self.leftSizer.Add(self.simpleSettingsPanel, 1)
421 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
422 self.updateSliceMode()
423 self.updateProfileToAllControls()
425 def updateMachineMenu(self):
426 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
427 for item in self.machineMenu.GetMenuItems():
428 self.machineMenu.RemoveItem(item)
430 #Add a menu item for each machine configuration.
431 for n in xrange(0, profile.getMachineCount()):
432 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
433 if n == int(profile.getPreferenceFloat('active_machine')):
435 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
437 self.machineMenu.AppendSeparator()
439 i = self.machineMenu.Append(-1, _("Machine settings..."))
440 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
442 #Add tools for machines.
443 self.machineMenu.AppendSeparator()
445 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
446 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
448 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
449 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
451 def OnLoadProfile(self, e):
452 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)
453 dlg.SetWildcard("ini files (*.ini)|*.ini")
454 if dlg.ShowModal() == wx.ID_OK:
455 profileFile = dlg.GetPath()
456 profile.loadProfile(profileFile)
457 self.updateProfileToAllControls()
459 # Update the Profile MRU
460 self.addToProfileMRU(profileFile)
463 def OnLoadProfileFromGcode(self, e):
464 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)
465 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
466 if dlg.ShowModal() == wx.ID_OK:
467 gcodeFile = dlg.GetPath()
468 f = open(gcodeFile, 'r')
471 if line.startswith(';CURA_PROFILE_STRING:'):
472 profile.setProfileFromString(line[line.find(':')+1:].strip())
473 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
474 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
477 self.updateProfileToAllControls()
479 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)
482 def OnSaveProfile(self, e):
483 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
484 dlg.SetWildcard("ini files (*.ini)|*.ini")
485 if dlg.ShowModal() == wx.ID_OK:
486 profileFile = dlg.GetPath()
487 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
488 profileFile += '.ini'
489 profile.saveProfile(profileFile)
492 def OnResetProfile(self, e):
493 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)
494 result = dlg.ShowModal() == wx.ID_YES
497 profile.resetProfile()
498 self.updateProfileToAllControls()
500 def OnSimpleSwitch(self, e):
501 profile.putPreference('startMode', 'Simple')
502 self.updateSliceMode()
504 def OnNormalSwitch(self, e):
505 profile.putPreference('startMode', 'Normal')
506 self.updateSliceMode()
508 def OnDefaultMarlinFirmware(self, e):
509 firmwareInstall.InstallFirmware(self)
511 def OnCustomFirmware(self, e):
512 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
513 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)
514 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
515 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
516 if dlg.ShowModal() == wx.ID_OK:
517 filename = dlg.GetPath()
519 if not(os.path.exists(filename)):
521 #For some reason my Ubuntu 10.10 crashes here.
522 firmwareInstall.InstallFirmware(self, filename)
524 def OnFirstRunWizard(self, e):
526 configWizard.configWizard()
528 self.reloadSettingPanels()
530 def OnSelectMachine(self, index):
531 profile.setActiveMachine(index)
532 self.reloadSettingPanels()
534 def OnBedLevelWizard(self, e):
535 configWizard.bedLevelWizard()
537 def OnHeadOffsetWizard(self, e):
538 configWizard.headOffsetWizard()
540 def OnExpertOpen(self, e):
541 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
545 def OnMinecraftImport(self, e):
546 mi = minecraftImport.minecraftImportWindow(self)
550 def OnPIDDebugger(self, e):
551 debugger = pidDebugger.debuggerWindow(self)
555 def OnAutoFirmwareUpdate(self, e):
556 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
557 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
558 if dlg.ShowModal() == wx.ID_OK:
559 filename = dlg.GetPath()
561 if not(os.path.exists(filename)):
563 #For some reason my Ubuntu 10.10 crashes here.
564 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
566 def onCopyProfileClipboard(self, e):
568 if not wx.TheClipboard.IsOpened():
569 wx.TheClipboard.Open()
570 clipData = wx.TextDataObject()
571 self.lastTriedClipboard = profile.getProfileString()
572 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
573 clipData.SetText(profileString)
574 wx.TheClipboard.SetData(clipData)
575 wx.TheClipboard.Close()
577 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
579 def OnCheckForUpdate(self, e):
580 newVersion = version.checkForNewerVersion()
581 if newVersion is not None:
582 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:
583 webbrowser.open(newVersion)
585 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
587 def OnAbout(self, e):
588 aboutBox = aboutWindow.aboutWindow()
592 def OnClose(self, e):
593 profile.saveProfile(profile.getDefaultProfilePath(), True)
595 # Save the window position, size & state from the preferences file
596 profile.putPreference('window_maximized', self.IsMaximized())
597 if not self.IsMaximized() and not self.IsIconized():
598 (posx, posy) = self.GetPosition()
599 profile.putPreference('window_pos_x', posx)
600 profile.putPreference('window_pos_y', posy)
601 (width, height) = self.GetSize()
602 profile.putPreference('window_width', width)
603 profile.putPreference('window_height', height)
605 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
606 isSimple = profile.getPreference('startMode') == 'Simple'
608 self.normalSashPos = self.splitter.GetSashPosition()
609 profile.putPreference('window_normal_sash', self.normalSashPos)
611 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
613 self.scene.OnPaint = lambda e : e
614 self.scene._engine.cleanup()
620 class normalSettingsPanel(configBase.configPanelBase):
621 "Main user interface window"
622 def __init__(self, parent, callback = None):
623 super(normalSettingsPanel, self).__init__(parent, callback)
626 self.nb = wx.Notebook(self)
627 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
628 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
630 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, _('Basic'))
631 self._addSettingsToPanels('basic', left, right)
632 self.SizeLabelWidths(left, right)
634 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, _('Advanced'))
635 self._addSettingsToPanels('advanced', left, right)
636 self.SizeLabelWidths(left, right)
639 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
640 self.nb.AddPage(self.pluginPanel, _("Plugins"))
643 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
644 self.alterationPanel = None
646 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
647 self.nb.AddPage(self.alterationPanel, _("Start/End-GCode"))
649 self.Bind(wx.EVT_SIZE, self.OnSize)
651 self.nb.SetSize(self.GetSize())
652 self.UpdateSize(self.printPanel)
653 self.UpdateSize(self.advancedPanel)
655 def _addSettingsToPanels(self, category, left, right):
656 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
660 for title in profile.getSubCategoriesFor(category):
661 n += 1 + len(profile.getSettingsForCategory(category, title))
664 configBase.TitleRow(p, _(title))
665 for s in profile.getSettingsForCategory(category, title):
666 configBase.SettingRow(p, s.getName())
668 def SizeLabelWidths(self, left, right):
669 leftWidth = self.getLabelColumnWidth(left)
670 rightWidth = self.getLabelColumnWidth(right)
671 maxWidth = max(leftWidth, rightWidth)
672 self.setLabelColumnWidth(left, maxWidth)
673 self.setLabelColumnWidth(right, maxWidth)
676 # Make the size of the Notebook control the same size as this control
677 self.nb.SetSize(self.GetSize())
679 # Propegate the OnSize() event (just in case)
682 # Perform out resize magic
683 self.UpdateSize(self.printPanel)
684 self.UpdateSize(self.advancedPanel)
686 def UpdateSize(self, configPanel):
687 sizer = configPanel.GetSizer()
691 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
694 # if width(col1) > (best_width(col1) + best_width(col1)):
695 # switch to horizontal
698 col1 = configPanel.leftPanel
699 colSize1 = col1.GetSize()
700 colBestSize1 = col1.GetBestSize()
701 col2 = configPanel.rightPanel
702 colSize2 = col2.GetSize()
703 colBestSize2 = col2.GetBestSize()
705 orientation = sizer.GetOrientation()
707 if orientation == wx.HORIZONTAL:
708 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
710 sizer = wx.BoxSizer(wx.VERTICAL)
711 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
712 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
713 configPanel.SetSizer(sizer)
719 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
721 sizer = wx.BoxSizer(wx.HORIZONTAL)
722 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
723 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
724 configPanel.SetSizer(sizer)
730 def updateProfileToControls(self):
731 super(normalSettingsPanel, self).updateProfileToControls()
732 if self.alterationPanel is not None:
733 self.alterationPanel.updateProfileToControls()
734 self.pluginPanel.updateProfileToControls()