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)
137 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
138 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
140 toolsMenu.AppendSeparator()
141 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
142 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
143 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
144 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
145 if profile.getPreference('oneAtATime') == 'True':
146 self.oneAtATime.Check(True)
148 self.allAtOnceItem.Check(True)
150 self.menubar.Append(toolsMenu, _("Tools"))
152 #Machine menu for machine configuration/tooling
153 self.machineMenu = wx.Menu()
154 self.updateMachineMenu()
156 self.menubar.Append(self.machineMenu, _("Machine"))
158 expertMenu = wx.Menu()
159 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
160 self.switchToQuickprintMenuItem = i
161 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
163 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
164 self.switchToNormalMenuItem = i
165 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
166 expertMenu.AppendSeparator()
168 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
169 self.normalModeOnlyItems.append(i)
170 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
171 expertMenu.AppendSeparator()
172 i = expertMenu.Append(-1, _("Run first run wizard..."))
173 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
174 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
175 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
176 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
177 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
179 self.menubar.Append(expertMenu, _("Expert"))
182 i = helpMenu.Append(-1, _("Online documentation..."))
183 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://lulzbot.com/cura'), i)
184 i = helpMenu.Append(-1, _("Report a problem..."))
185 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/alephobjects/Cura/issues'), i)
186 #i = helpMenu.Append(-1, _("Check for update..."))
187 #self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
188 #i = helpMenu.Append(-1, _("Open YouMagine website..."))
189 #self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
190 i = helpMenu.Append(-1, _("About Cura..."))
191 self.Bind(wx.EVT_MENU, self.OnAbout, i)
192 self.menubar.Append(helpMenu, _("Help"))
193 self.SetMenuBar(self.menubar)
195 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
196 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
197 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
198 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
201 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
202 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
204 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
205 self.leftSizer.Add(self.simpleSettingsPanel, 1)
206 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
207 self.leftPane.SetSizer(self.leftSizer)
210 self.scene = sceneView.SceneView(self.rightPane)
212 #Main sizer, to position the preview window, buttons and tab control
213 sizer = wx.BoxSizer()
214 self.rightPane.SetSizer(sizer)
215 sizer.Add(self.scene, 1, flag=wx.EXPAND)
218 sizer = wx.BoxSizer(wx.VERTICAL)
220 sizer.Add(self.splitter, 1, wx.EXPAND)
224 self.updateProfileToAllControls()
226 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
228 self.simpleSettingsPanel.Show(False)
229 self.normalSettingsPanel.Show(False)
231 # Set default window size & position
232 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
235 #Timer set; used to check if profile is on the clipboard
236 self.timer = wx.Timer(self)
237 self.Bind(wx.EVT_TIMER, self.onTimer)
238 self.timer.Start(1000)
239 self.lastTriedClipboard = profile.getProfileString()
241 # Restore the window position, size & state from the preferences file
243 if profile.getPreference('window_maximized') == 'True':
246 posx = int(profile.getPreference('window_pos_x'))
247 posy = int(profile.getPreference('window_pos_y'))
248 width = int(profile.getPreference('window_width'))
249 height = int(profile.getPreference('window_height'))
250 if posx > 0 or posy > 0:
251 self.SetPosition((posx,posy))
252 if width > 0 and height > 0:
253 self.SetSize((width,height))
255 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
257 self.normalSashPos = 0
259 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
260 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
262 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
264 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
266 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
268 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
269 self.SetSize((800,600))
272 self.updateSliceMode()
273 self.scene.SetFocus()
275 def onTimer(self, e):
276 #Check if there is something in the clipboard
279 if not wx.TheClipboard.IsOpened():
280 if not wx.TheClipboard.Open():
282 do = wx.TextDataObject()
283 if wx.TheClipboard.GetData(do):
284 profileString = do.GetText()
285 wx.TheClipboard.Close()
287 startTag = "CURA_PROFILE_STRING:"
288 if startTag in profileString:
289 #print "Found correct syntax on clipboard"
290 profileString = profileString.replace("\n","").strip()
291 profileString = profileString[profileString.find(startTag)+len(startTag):]
292 if profileString != self.lastTriedClipboard:
294 self.lastTriedClipboard = profileString
295 profile.setProfileFromString(profileString)
296 self.scene.notification.message("Loaded new profile from clipboard.")
297 self.updateProfileToAllControls()
299 print "Unable to read from clipboard"
302 def updateSliceMode(self):
303 isSimple = profile.getPreference('startMode') == 'Simple'
305 self.normalSettingsPanel.Show(not isSimple)
306 self.simpleSettingsPanel.Show(isSimple)
307 self.leftPane.Layout()
309 for i in self.normalModeOnlyItems:
310 i.Enable(not isSimple)
312 self.switchToQuickprintMenuItem.Check()
314 self.switchToNormalMenuItem.Check()
316 # Set splitter sash position & size
318 # Save normal mode sash
319 self.normalSashPos = self.splitter.GetSashPosition()
321 # Change location of sash to width of quick mode pane
322 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
323 self.splitter.SetSashPosition(width, True)
326 self.splitter.SetSashSize(0)
328 self.splitter.SetSashPosition(self.normalSashPos, True)
330 self.splitter.SetSashSize(4)
331 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
332 if profile.getMachineSetting('machine_type') == 'ultimaker2' or profile.getMachineSetting('machine_type') == 'lulzbot_mini' or profile.getMachineSetting('machine_type') == 'lulzbot_TAZ':
333 self.bedLevelWizardMenuItem.Enable(False)
334 self.headOffsetWizardMenuItem.Enable(False)
336 self.bedLevelWizardMenuItem.Enable(True)
337 self.headOffsetWizardMenuItem.Enable(False)
338 if int(profile.getMachineSetting('extruder_amount')) < 2:
339 self.headOffsetWizardMenuItem.Enable(False)
340 self.scene.updateProfileToControls()
341 self.scene._scene.pushFree()
343 def onOneAtATimeSwitch(self, e):
344 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
345 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
346 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)
347 self.scene.updateProfileToControls()
348 self.scene._scene.pushFree()
349 self.scene.sceneUpdated()
351 def OnPreferences(self, e):
352 prefDialog = preferencesDialog.preferencesDialog(self)
356 wx.CallAfter(prefDialog.Show)
358 def OnMachineSettings(self, e):
359 prefDialog = preferencesDialog.machineSettingsDialog(self)
364 def OnDropFiles(self, files):
366 self.updateProfileToAllControls()
367 self.scene.loadFiles(files)
369 def OnModelMRU(self, e):
370 fileNum = e.GetId() - self.ID_MRU_MODEL1
371 path = self.modelFileHistory.GetHistoryFile(fileNum)
373 self.modelFileHistory.AddFileToHistory(path) # move up the list
374 self.config.SetPath("/ModelMRU")
375 self.modelFileHistory.Save(self.config)
378 profile.putPreference('lastFile', path)
380 self.scene.loadFiles(filelist)
382 def addToModelMRU(self, file):
383 self.modelFileHistory.AddFileToHistory(file)
384 self.config.SetPath("/ModelMRU")
385 self.modelFileHistory.Save(self.config)
388 def OnProfileMRU(self, e):
389 fileNum = e.GetId() - self.ID_MRU_PROFILE1
390 path = self.profileFileHistory.GetHistoryFile(fileNum)
392 self.profileFileHistory.AddFileToHistory(path) # move up the list
393 self.config.SetPath("/ProfileMRU")
394 self.profileFileHistory.Save(self.config)
397 profile.loadProfile(path)
398 self.updateProfileToAllControls()
400 def addToProfileMRU(self, file):
401 self.profileFileHistory.AddFileToHistory(file)
402 self.config.SetPath("/ProfileMRU")
403 self.profileFileHistory.Save(self.config)
406 def updateProfileToAllControls(self):
407 self.scene.updateProfileToControls()
408 self.normalSettingsPanel.updateProfileToControls()
409 self.simpleSettingsPanel.updateProfileToControls()
411 def reloadSettingPanels(self):
412 self.leftSizer.Detach(self.simpleSettingsPanel)
413 self.leftSizer.Detach(self.normalSettingsPanel)
414 self.simpleSettingsPanel.Destroy()
415 self.normalSettingsPanel.Destroy()
416 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
417 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
418 self.leftSizer.Add(self.simpleSettingsPanel, 1)
419 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
420 self.updateSliceMode()
421 self.updateProfileToAllControls()
423 def updateMachineMenu(self):
424 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
425 for item in self.machineMenu.GetMenuItems():
426 self.machineMenu.RemoveItem(item)
428 #Add a menu item for each machine configuration.
429 for n in xrange(0, profile.getMachineCount()):
430 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
431 if n == int(profile.getPreferenceFloat('active_machine')):
433 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
435 self.machineMenu.AppendSeparator()
437 i = self.machineMenu.Append(-1, _("Machine settings..."))
438 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
440 #Add tools for machines.
441 self.machineMenu.AppendSeparator()
443 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
444 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
446 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
447 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
449 def OnLoadProfile(self, e):
450 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)
451 dlg.SetWildcard("ini files (*.ini)|*.ini")
452 if dlg.ShowModal() == wx.ID_OK:
453 profileFile = dlg.GetPath()
454 profile.loadProfile(profileFile)
455 self.updateProfileToAllControls()
457 # Update the Profile MRU
458 self.addToProfileMRU(profileFile)
461 def OnLoadProfileFromGcode(self, e):
462 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)
463 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
464 if dlg.ShowModal() == wx.ID_OK:
465 gcodeFile = dlg.GetPath()
466 f = open(gcodeFile, 'r')
469 if line.startswith(';CURA_PROFILE_STRING:'):
470 profile.setProfileFromString(line[line.find(':')+1:].strip())
471 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
472 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
475 self.updateProfileToAllControls()
477 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)
480 def OnSaveProfile(self, e):
481 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
482 dlg.SetWildcard("ini files (*.ini)|*.ini")
483 if dlg.ShowModal() == wx.ID_OK:
484 profileFile = dlg.GetPath()
485 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
486 profileFile += '.ini'
487 profile.saveProfile(profileFile)
490 def OnResetProfile(self, e):
491 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)
492 result = dlg.ShowModal() == wx.ID_YES
495 profile.resetProfile()
496 self.updateProfileToAllControls()
498 def OnSimpleSwitch(self, e):
499 profile.putPreference('startMode', 'Simple')
500 self.updateSliceMode()
502 def OnNormalSwitch(self, e):
503 profile.putPreference('startMode', 'Normal')
504 self.updateSliceMode()
506 def OnDefaultMarlinFirmware(self, e):
507 firmwareInstall.InstallFirmware()
509 def OnCustomFirmware(self, e):
510 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
511 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)
512 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
513 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
514 if dlg.ShowModal() == wx.ID_OK:
515 filename = dlg.GetPath()
517 if not(os.path.exists(filename)):
519 #For some reason my Ubuntu 10.10 crashes here.
520 firmwareInstall.InstallFirmware(filename)
522 def OnFirstRunWizard(self, e):
524 configWizard.configWizard()
526 self.reloadSettingPanels()
528 def OnSelectMachine(self, index):
529 profile.setActiveMachine(index)
530 self.reloadSettingPanels()
532 def OnBedLevelWizard(self, e):
533 configWizard.bedLevelWizard()
535 def OnHeadOffsetWizard(self, e):
536 configWizard.headOffsetWizard()
538 def OnExpertOpen(self, e):
539 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
543 def OnMinecraftImport(self, e):
544 mi = minecraftImport.minecraftImportWindow(self)
548 def OnPIDDebugger(self, e):
549 debugger = pidDebugger.debuggerWindow(self)
553 def onCopyProfileClipboard(self, e):
555 if not wx.TheClipboard.IsOpened():
556 wx.TheClipboard.Open()
557 clipData = wx.TextDataObject()
558 self.lastTriedClipboard = profile.getProfileString()
559 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
560 clipData.SetText(profileString)
561 wx.TheClipboard.SetData(clipData)
562 wx.TheClipboard.Close()
564 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
566 def OnCheckForUpdate(self, e):
567 newVersion = version.checkForNewerVersion()
568 if newVersion is not None:
569 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:
570 webbrowser.open(newVersion)
572 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
574 def OnAbout(self, e):
575 aboutBox = aboutWindow.aboutWindow()
579 def OnClose(self, e):
580 profile.saveProfile(profile.getDefaultProfilePath(), True)
582 # Save the window position, size & state from the preferences file
583 profile.putPreference('window_maximized', self.IsMaximized())
584 if not self.IsMaximized() and not self.IsIconized():
585 (posx, posy) = self.GetPosition()
586 profile.putPreference('window_pos_x', posx)
587 profile.putPreference('window_pos_y', posy)
588 (width, height) = self.GetSize()
589 profile.putPreference('window_width', width)
590 profile.putPreference('window_height', height)
592 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
593 isSimple = profile.getPreference('startMode') == 'Simple'
595 self.normalSashPos = self.splitter.GetSashPosition()
596 profile.putPreference('window_normal_sash', self.normalSashPos)
598 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
600 self.scene.OnPaint = lambda e : e
601 self.scene._engine.cleanup()
607 class normalSettingsPanel(configBase.configPanelBase):
608 "Main user interface window"
609 def __init__(self, parent, callback = None):
610 super(normalSettingsPanel, self).__init__(parent, callback)
613 self.nb = wx.Notebook(self)
614 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
615 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
617 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
618 self._addSettingsToPanels('basic', left, right)
619 self.SizeLabelWidths(left, right)
621 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
622 self._addSettingsToPanels('advanced', left, right)
623 self.SizeLabelWidths(left, right)
626 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
627 self.nb.AddPage(self.pluginPanel, _("Plugins"))
630 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
631 self.alterationPanel = None
633 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
634 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
636 self.Bind(wx.EVT_SIZE, self.OnSize)
638 self.nb.SetSize(self.GetSize())
639 self.UpdateSize(self.printPanel)
640 self.UpdateSize(self.advancedPanel)
642 def _addSettingsToPanels(self, category, left, right):
643 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
647 for title in profile.getSubCategoriesFor(category):
648 n += 1 + len(profile.getSettingsForCategory(category, title))
651 configBase.TitleRow(p, _(title))
652 for s in profile.getSettingsForCategory(category, title):
653 configBase.SettingRow(p, s.getName())
655 def SizeLabelWidths(self, left, right):
656 leftWidth = self.getLabelColumnWidth(left)
657 rightWidth = self.getLabelColumnWidth(right)
658 maxWidth = max(leftWidth, rightWidth)
659 self.setLabelColumnWidth(left, maxWidth)
660 self.setLabelColumnWidth(right, maxWidth)
663 # Make the size of the Notebook control the same size as this control
664 self.nb.SetSize(self.GetSize())
666 # Propegate the OnSize() event (just in case)
669 # Perform out resize magic
670 self.UpdateSize(self.printPanel)
671 self.UpdateSize(self.advancedPanel)
673 def UpdateSize(self, configPanel):
674 sizer = configPanel.GetSizer()
678 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
681 # if width(col1) > (best_width(col1) + best_width(col1)):
682 # switch to horizontal
685 col1 = configPanel.leftPanel
686 colSize1 = col1.GetSize()
687 colBestSize1 = col1.GetBestSize()
688 col2 = configPanel.rightPanel
689 colSize2 = col2.GetSize()
690 colBestSize2 = col2.GetBestSize()
692 orientation = sizer.GetOrientation()
694 if orientation == wx.HORIZONTAL:
695 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
697 sizer = wx.BoxSizer(wx.VERTICAL)
698 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
699 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
700 configPanel.SetSizer(sizer)
706 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
708 sizer = wx.BoxSizer(wx.HORIZONTAL)
709 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
710 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
711 configPanel.SetSizer(sizer)
717 def updateProfileToControls(self):
718 super(normalSettingsPanel, self).updateProfileToControls()
719 if self.alterationPanel is not None:
720 self.alterationPanel.updateProfileToControls()
721 self.pluginPanel.updateProfileToControls()