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://daid.github.com/Cura'), i)
184 i = helpMenu.Append(-1, _("Report a problem..."))
185 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/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':
333 self.bedLevelWizardMenuItem.Enable(False)
334 self.headOffsetWizardMenuItem.Enable(False)
335 if int(profile.getMachineSetting('extruder_amount')) < 2:
336 self.headOffsetWizardMenuItem.Enable(False)
337 self.scene.updateProfileToControls()
338 self.scene._scene.pushFree()
340 def onOneAtATimeSwitch(self, e):
341 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
342 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
343 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)
344 self.scene.updateProfileToControls()
345 self.scene._scene.pushFree()
346 self.scene.sceneUpdated()
348 def OnPreferences(self, e):
349 prefDialog = preferencesDialog.preferencesDialog(self)
353 wx.CallAfter(prefDialog.Show)
355 def OnMachineSettings(self, e):
356 prefDialog = preferencesDialog.machineSettingsDialog(self)
361 def OnDropFiles(self, files):
363 self.updateProfileToAllControls()
364 self.scene.loadFiles(files)
366 def OnModelMRU(self, e):
367 fileNum = e.GetId() - self.ID_MRU_MODEL1
368 path = self.modelFileHistory.GetHistoryFile(fileNum)
370 self.modelFileHistory.AddFileToHistory(path) # move up the list
371 self.config.SetPath("/ModelMRU")
372 self.modelFileHistory.Save(self.config)
375 profile.putPreference('lastFile', path)
377 self.scene.loadFiles(filelist)
379 def addToModelMRU(self, file):
380 self.modelFileHistory.AddFileToHistory(file)
381 self.config.SetPath("/ModelMRU")
382 self.modelFileHistory.Save(self.config)
385 def OnProfileMRU(self, e):
386 fileNum = e.GetId() - self.ID_MRU_PROFILE1
387 path = self.profileFileHistory.GetHistoryFile(fileNum)
389 self.profileFileHistory.AddFileToHistory(path) # move up the list
390 self.config.SetPath("/ProfileMRU")
391 self.profileFileHistory.Save(self.config)
394 profile.loadProfile(path)
395 self.updateProfileToAllControls()
397 def addToProfileMRU(self, file):
398 self.profileFileHistory.AddFileToHistory(file)
399 self.config.SetPath("/ProfileMRU")
400 self.profileFileHistory.Save(self.config)
403 def updateProfileToAllControls(self):
404 self.scene.updateProfileToControls()
405 self.normalSettingsPanel.updateProfileToControls()
406 self.simpleSettingsPanel.updateProfileToControls()
408 def reloadSettingPanels(self):
409 self.leftSizer.Detach(self.simpleSettingsPanel)
410 self.leftSizer.Detach(self.normalSettingsPanel)
411 self.simpleSettingsPanel.Destroy()
412 self.normalSettingsPanel.Destroy()
413 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
414 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
415 self.leftSizer.Add(self.simpleSettingsPanel, 1)
416 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
417 self.updateSliceMode()
418 self.updateProfileToAllControls()
420 def updateMachineMenu(self):
421 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
422 for item in self.machineMenu.GetMenuItems():
423 self.machineMenu.RemoveItem(item)
425 #Add a menu item for each machine configuration.
426 for n in xrange(0, profile.getMachineCount()):
427 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
428 if n == int(profile.getPreferenceFloat('active_machine')):
430 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
432 self.machineMenu.AppendSeparator()
434 i = self.machineMenu.Append(-1, _("Machine settings..."))
435 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
437 #Add tools for machines.
438 self.machineMenu.AppendSeparator()
440 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
441 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
443 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
444 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
446 def OnLoadProfile(self, e):
447 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)
448 dlg.SetWildcard("ini files (*.ini)|*.ini")
449 if dlg.ShowModal() == wx.ID_OK:
450 profileFile = dlg.GetPath()
451 profile.loadProfile(profileFile)
452 self.updateProfileToAllControls()
454 # Update the Profile MRU
455 self.addToProfileMRU(profileFile)
458 def OnLoadProfileFromGcode(self, e):
459 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)
460 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
461 if dlg.ShowModal() == wx.ID_OK:
462 gcodeFile = dlg.GetPath()
463 f = open(gcodeFile, 'r')
466 if line.startswith(';CURA_PROFILE_STRING:'):
467 profile.setProfileFromString(line[line.find(':')+1:].strip())
468 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
469 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
472 self.updateProfileToAllControls()
474 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)
477 def OnSaveProfile(self, e):
478 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
479 dlg.SetWildcard("ini files (*.ini)|*.ini")
480 if dlg.ShowModal() == wx.ID_OK:
481 profileFile = dlg.GetPath()
482 if platform.system() == 'Linux': #hack for linux, as for some reason the .ini is not appended.
483 profileFile += '.ini'
484 profile.saveProfile(profileFile)
487 def OnResetProfile(self, e):
488 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)
489 result = dlg.ShowModal() == wx.ID_YES
492 profile.resetProfile()
493 self.updateProfileToAllControls()
495 def OnSimpleSwitch(self, e):
496 profile.putPreference('startMode', 'Simple')
497 self.updateSliceMode()
499 def OnNormalSwitch(self, e):
500 profile.putPreference('startMode', 'Normal')
501 self.updateSliceMode()
503 def OnDefaultMarlinFirmware(self, e):
504 firmwareInstall.InstallFirmware()
506 def OnCustomFirmware(self, e):
507 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
508 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)
509 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
510 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
511 if dlg.ShowModal() == wx.ID_OK:
512 filename = dlg.GetPath()
513 if not(os.path.exists(filename)):
515 #For some reason my Ubuntu 10.10 crashes here.
516 firmwareInstall.InstallFirmware(filename)
518 def OnFirstRunWizard(self, e):
520 configWizard.configWizard()
522 self.reloadSettingPanels()
524 def OnSelectMachine(self, index):
525 profile.setActiveMachine(index)
526 self.reloadSettingPanels()
528 def OnBedLevelWizard(self, e):
529 configWizard.bedLevelWizard()
531 def OnHeadOffsetWizard(self, e):
532 configWizard.headOffsetWizard()
534 def OnExpertOpen(self, e):
535 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
539 def OnMinecraftImport(self, e):
540 mi = minecraftImport.minecraftImportWindow(self)
544 def OnPIDDebugger(self, e):
545 debugger = pidDebugger.debuggerWindow(self)
549 def onCopyProfileClipboard(self, e):
551 if not wx.TheClipboard.IsOpened():
552 wx.TheClipboard.Open()
553 clipData = wx.TextDataObject()
554 self.lastTriedClipboard = profile.getProfileString()
555 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
556 clipData.SetText(profileString)
557 wx.TheClipboard.SetData(clipData)
558 wx.TheClipboard.Close()
560 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
562 def OnCheckForUpdate(self, e):
563 newVersion = version.checkForNewerVersion()
564 if newVersion is not None:
565 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:
566 webbrowser.open(newVersion)
568 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
570 def OnAbout(self, e):
571 aboutBox = aboutWindow.aboutWindow()
575 def OnClose(self, e):
576 profile.saveProfile(profile.getDefaultProfilePath(), True)
578 # Save the window position, size & state from the preferences file
579 profile.putPreference('window_maximized', self.IsMaximized())
580 if not self.IsMaximized() and not self.IsIconized():
581 (posx, posy) = self.GetPosition()
582 profile.putPreference('window_pos_x', posx)
583 profile.putPreference('window_pos_y', posy)
584 (width, height) = self.GetSize()
585 profile.putPreference('window_width', width)
586 profile.putPreference('window_height', height)
588 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
589 isSimple = profile.getPreference('startMode') == 'Simple'
591 self.normalSashPos = self.splitter.GetSashPosition()
592 profile.putPreference('window_normal_sash', self.normalSashPos)
594 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
596 self.scene.OnPaint = lambda e : e
597 self.scene._engine.cleanup()
603 class normalSettingsPanel(configBase.configPanelBase):
604 "Main user interface window"
605 def __init__(self, parent, callback = None):
606 super(normalSettingsPanel, self).__init__(parent, callback)
609 self.nb = wx.Notebook(self)
610 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
611 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
613 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
614 self._addSettingsToPanels('basic', left, right)
615 self.SizeLabelWidths(left, right)
617 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
618 self._addSettingsToPanels('advanced', left, right)
619 self.SizeLabelWidths(left, right)
622 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
623 self.nb.AddPage(self.pluginPanel, _("Plugins"))
626 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
627 self.alterationPanel = None
629 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
630 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
632 self.Bind(wx.EVT_SIZE, self.OnSize)
634 self.nb.SetSize(self.GetSize())
635 self.UpdateSize(self.printPanel)
636 self.UpdateSize(self.advancedPanel)
638 def _addSettingsToPanels(self, category, left, right):
639 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
643 for title in profile.getSubCategoriesFor(category):
644 n += 1 + len(profile.getSettingsForCategory(category, title))
647 configBase.TitleRow(p, _(title))
648 for s in profile.getSettingsForCategory(category, title):
649 configBase.SettingRow(p, s.getName())
651 def SizeLabelWidths(self, left, right):
652 leftWidth = self.getLabelColumnWidth(left)
653 rightWidth = self.getLabelColumnWidth(right)
654 maxWidth = max(leftWidth, rightWidth)
655 self.setLabelColumnWidth(left, maxWidth)
656 self.setLabelColumnWidth(right, maxWidth)
659 # Make the size of the Notebook control the same size as this control
660 self.nb.SetSize(self.GetSize())
662 # Propegate the OnSize() event (just in case)
665 # Perform out resize magic
666 self.UpdateSize(self.printPanel)
667 self.UpdateSize(self.advancedPanel)
669 def UpdateSize(self, configPanel):
670 sizer = configPanel.GetSizer()
674 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
677 # if width(col1) > (best_width(col1) + best_width(col1)):
678 # switch to horizontal
681 col1 = configPanel.leftPanel
682 colSize1 = col1.GetSize()
683 colBestSize1 = col1.GetBestSize()
684 col2 = configPanel.rightPanel
685 colSize2 = col2.GetSize()
686 colBestSize2 = col2.GetBestSize()
688 orientation = sizer.GetOrientation()
690 if orientation == wx.HORIZONTAL:
691 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
693 sizer = wx.BoxSizer(wx.VERTICAL)
694 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
695 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
696 configPanel.SetSizer(sizer)
702 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
704 sizer = wx.BoxSizer(wx.HORIZONTAL)
705 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
706 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
707 configPanel.SetSizer(sizer)
713 def updateProfileToControls(self):
714 super(normalSettingsPanel, self).updateProfileToControls()
715 if self.alterationPanel is not None:
716 self.alterationPanel.updateProfileToControls()
717 self.pluginPanel.updateProfileToControls()