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
25 from Cura.util import meshLoader
27 class mainWindow(wx.Frame):
29 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
31 wx.EVT_CLOSE(self, self.OnClose)
33 # allow dropping any file, restrict later
34 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
36 # 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
37 if sys.platform.startswith('darwin'):
40 nswindow = objc.objc_object(c_void_p=self.MacGetTopLevelWindowRef())
41 view = nswindow.contentView()
42 view.registerForDraggedTypes_([u'NSFilenamesPboardType'])
46 self.normalModeOnlyItems = []
48 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
49 self.config = wx.FileConfig(appName="Cura",
50 localFilename=mruFile,
51 style=wx.CONFIG_USE_LOCAL_FILE)
53 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)]
54 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
55 self.config.SetPath("/ModelMRU")
56 self.modelFileHistory.Load(self.config)
58 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)]
59 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
60 self.config.SetPath("/ProfileMRU")
61 self.profileFileHistory.Load(self.config)
63 self.menubar = wx.MenuBar()
64 self.fileMenu = wx.Menu()
65 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
66 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
67 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
68 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
69 i = self.fileMenu.Append(-1, _("Clear platform"))
70 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
72 self.fileMenu.AppendSeparator()
73 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
74 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnPrintButton(1), i)
75 i = self.fileMenu.Append(-1, _("Save GCode..."))
76 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
77 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
78 self.Bind(wx.EVT_MENU, lambda e: self.scene._showEngineLog(), i)
80 self.fileMenu.AppendSeparator()
81 i = self.fileMenu.Append(-1, _("Open Profile..."))
82 self.normalModeOnlyItems.append(i)
83 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
84 i = self.fileMenu.Append(-1, _("Save Profile..."))
85 self.normalModeOnlyItems.append(i)
86 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
87 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
88 self.normalModeOnlyItems.append(i)
89 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
90 self.fileMenu.AppendSeparator()
91 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
92 self.normalModeOnlyItems.append(i)
93 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
95 self.fileMenu.AppendSeparator()
96 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
97 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
98 i = self.fileMenu.Append(-1, _("Machine settings..."))
99 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
100 self.fileMenu.AppendSeparator()
103 modelHistoryMenu = wx.Menu()
104 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
105 self.modelFileHistory.UseMenu(modelHistoryMenu)
106 self.modelFileHistory.AddFilesToMenu()
107 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
110 profileHistoryMenu = wx.Menu()
111 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
112 self.profileFileHistory.UseMenu(profileHistoryMenu)
113 self.profileFileHistory.AddFilesToMenu()
114 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
116 self.fileMenu.AppendSeparator()
117 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
118 self.Bind(wx.EVT_MENU, self.OnQuit, i)
119 self.menubar.Append(self.fileMenu, '&' + _("File"))
121 toolsMenu = wx.Menu()
122 #i = toolsMenu.Append(-1, 'Batch run...')
123 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
124 #self.normalModeOnlyItems.append(i)
126 if minecraftImport.hasMinecraft():
127 i = toolsMenu.Append(-1, _("Minecraft map import..."))
128 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
130 if version.isDevVersion():
131 i = toolsMenu.Append(-1, _("PID Debugger..."))
132 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
134 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
135 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
137 toolsMenu.AppendSeparator()
138 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
139 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
140 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
141 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
142 if profile.getPreference('oneAtATime') == 'True':
143 self.oneAtATime.Check(True)
145 self.allAtOnceItem.Check(True)
147 self.menubar.Append(toolsMenu, _("Tools"))
149 #Machine menu for machine configuration/tooling
150 self.machineMenu = wx.Menu()
151 self.updateMachineMenu()
153 self.menubar.Append(self.machineMenu, _("Machine"))
155 expertMenu = wx.Menu()
156 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
157 self.switchToQuickprintMenuItem = i
158 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
160 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
161 self.switchToNormalMenuItem = i
162 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
163 expertMenu.AppendSeparator()
165 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
166 self.normalModeOnlyItems.append(i)
167 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
168 expertMenu.AppendSeparator()
169 i = expertMenu.Append(-1, _("Run first run wizard..."))
170 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
171 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
172 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
173 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
174 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
176 self.menubar.Append(expertMenu, _("Expert"))
179 i = helpMenu.Append(-1, _("Online documentation..."))
180 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
181 i = helpMenu.Append(-1, _("Report a problem..."))
182 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
183 i = helpMenu.Append(-1, _("Check for update..."))
184 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
185 i = helpMenu.Append(-1, _("Open YouMagine website..."))
186 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
187 i = helpMenu.Append(-1, _("About Cura..."))
188 self.Bind(wx.EVT_MENU, self.OnAbout, i)
189 self.menubar.Append(helpMenu, _("Help"))
190 self.SetMenuBar(self.menubar)
192 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
193 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
194 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
195 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
198 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
199 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
201 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
202 self.leftSizer.Add(self.simpleSettingsPanel, 1)
203 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
204 self.leftPane.SetSizer(self.leftSizer)
207 self.scene = sceneView.SceneView(self.rightPane)
209 #Main sizer, to position the preview window, buttons and tab control
210 sizer = wx.BoxSizer()
211 self.rightPane.SetSizer(sizer)
212 sizer.Add(self.scene, 1, flag=wx.EXPAND)
215 sizer = wx.BoxSizer(wx.VERTICAL)
217 sizer.Add(self.splitter, 1, wx.EXPAND)
221 self.updateProfileToAllControls()
223 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
225 self.simpleSettingsPanel.Show(False)
226 self.normalSettingsPanel.Show(False)
228 # Set default window size & position
229 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
232 #Timer set; used to check if profile is on the clipboard
233 self.timer = wx.Timer(self)
234 self.Bind(wx.EVT_TIMER, self.onTimer)
235 self.timer.Start(1000)
236 self.lastTriedClipboard = profile.getProfileString()
238 # Restore the window position, size & state from the preferences file
240 if profile.getPreference('window_maximized') == 'True':
243 posx = int(profile.getPreference('window_pos_x'))
244 posy = int(profile.getPreference('window_pos_y'))
245 width = int(profile.getPreference('window_width'))
246 height = int(profile.getPreference('window_height'))
247 if posx > 0 or posy > 0:
248 self.SetPosition((posx,posy))
249 if width > 0 and height > 0:
250 self.SetSize((width,height))
252 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
254 self.normalSashPos = 0
256 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
257 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
259 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
261 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
263 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
265 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
266 self.SetSize((800,600))
269 self.updateSliceMode()
270 self.scene.SetFocus()
272 def onTimer(self, e):
273 #Check if there is something in the clipboard
276 if not wx.TheClipboard.IsOpened():
277 if not wx.TheClipboard.Open():
279 do = wx.TextDataObject()
280 if wx.TheClipboard.GetData(do):
281 profileString = do.GetText()
282 wx.TheClipboard.Close()
284 startTag = "CURA_PROFILE_STRING:"
285 if startTag in profileString:
286 #print "Found correct syntax on clipboard"
287 profileString = profileString.replace("\n","").strip()
288 profileString = profileString[profileString.find(startTag)+len(startTag):]
289 if profileString != self.lastTriedClipboard:
291 self.lastTriedClipboard = profileString
292 profile.setProfileFromString(profileString)
293 self.scene.notification.message("Loaded new profile from clipboard.")
294 self.updateProfileToAllControls()
296 print "Unable to read from clipboard"
299 def updateSliceMode(self):
300 isSimple = profile.getPreference('startMode') == 'Simple'
302 self.normalSettingsPanel.Show(not isSimple)
303 self.simpleSettingsPanel.Show(isSimple)
304 self.leftPane.Layout()
306 for i in self.normalModeOnlyItems:
307 i.Enable(not isSimple)
309 self.switchToQuickprintMenuItem.Check()
311 self.switchToNormalMenuItem.Check()
313 # Set splitter sash position & size
315 # Save normal mode sash
316 self.normalSashPos = self.splitter.GetSashPosition()
318 # Change location of sash to width of quick mode pane
319 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
320 self.splitter.SetSashPosition(width, True)
323 self.splitter.SetSashSize(0)
325 self.splitter.SetSashPosition(self.normalSashPos, True)
327 self.splitter.SetSashSize(4)
328 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
329 if profile.getMachineSetting('machine_type') == 'ultimaker2':
330 self.bedLevelWizardMenuItem.Enable(False)
331 self.headOffsetWizardMenuItem.Enable(False)
332 if int(profile.getMachineSetting('extruder_amount')) < 2:
333 self.headOffsetWizardMenuItem.Enable(False)
334 self.scene.updateProfileToControls()
335 self.scene._scene.pushFree()
337 def onOneAtATimeSwitch(self, e):
338 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
339 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
340 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)
341 self.scene.updateProfileToControls()
342 self.scene._scene.pushFree()
343 self.scene.sceneUpdated()
345 def OnPreferences(self, e):
346 prefDialog = preferencesDialog.preferencesDialog(self)
350 wx.CallAfter(prefDialog.Show)
352 def OnMachineSettings(self, e):
353 prefDialog = preferencesDialog.machineSettingsDialog(self)
358 def OnDropFiles(self, files):
360 self.updateProfileToAllControls()
361 self.scene.loadFiles(files)
363 def OnModelMRU(self, e):
364 fileNum = e.GetId() - self.ID_MRU_MODEL1
365 path = self.modelFileHistory.GetHistoryFile(fileNum)
367 self.modelFileHistory.AddFileToHistory(path) # move up the list
368 self.config.SetPath("/ModelMRU")
369 self.modelFileHistory.Save(self.config)
372 profile.putPreference('lastFile', path)
374 self.scene.loadFiles(filelist)
376 def addToModelMRU(self, file):
377 self.modelFileHistory.AddFileToHistory(file)
378 self.config.SetPath("/ModelMRU")
379 self.modelFileHistory.Save(self.config)
382 def OnProfileMRU(self, e):
383 fileNum = e.GetId() - self.ID_MRU_PROFILE1
384 path = self.profileFileHistory.GetHistoryFile(fileNum)
386 self.profileFileHistory.AddFileToHistory(path) # move up the list
387 self.config.SetPath("/ProfileMRU")
388 self.profileFileHistory.Save(self.config)
391 profile.loadProfile(path)
392 self.updateProfileToAllControls()
394 def addToProfileMRU(self, file):
395 self.profileFileHistory.AddFileToHistory(file)
396 self.config.SetPath("/ProfileMRU")
397 self.profileFileHistory.Save(self.config)
400 def updateProfileToAllControls(self):
401 self.scene.updateProfileToControls()
402 self.normalSettingsPanel.updateProfileToControls()
403 self.simpleSettingsPanel.updateProfileToControls()
405 def reloadSettingPanels(self):
406 self.leftSizer.Detach(self.simpleSettingsPanel)
407 self.leftSizer.Detach(self.normalSettingsPanel)
408 self.simpleSettingsPanel.Destroy()
409 self.normalSettingsPanel.Destroy()
410 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
411 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
412 self.leftSizer.Add(self.simpleSettingsPanel, 1)
413 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
414 self.updateSliceMode()
415 self.updateProfileToAllControls()
417 def updateMachineMenu(self):
418 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
419 for item in self.machineMenu.GetMenuItems():
420 self.machineMenu.RemoveItem(item)
422 #Add a menu item for each machine configuration.
423 for n in xrange(0, profile.getMachineCount()):
424 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
425 if n == int(profile.getPreferenceFloat('active_machine')):
427 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
429 self.machineMenu.AppendSeparator()
431 i = self.machineMenu.Append(-1, _("Machine settings..."))
432 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
434 #Add tools for machines.
435 self.machineMenu.AppendSeparator()
437 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
438 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
440 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
441 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
443 def OnLoadProfile(self, e):
444 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)
445 dlg.SetWildcard("ini files (*.ini)|*.ini")
446 if dlg.ShowModal() == wx.ID_OK:
447 profileFile = dlg.GetPath()
448 profile.loadProfile(profileFile)
449 self.updateProfileToAllControls()
451 # Update the Profile MRU
452 self.addToProfileMRU(profileFile)
455 def OnLoadProfileFromGcode(self, e):
456 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)
457 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
458 if dlg.ShowModal() == wx.ID_OK:
459 gcodeFile = dlg.GetPath()
460 f = open(gcodeFile, 'r')
463 if line.startswith(';CURA_PROFILE_STRING:'):
464 profile.setProfileFromString(line[line.find(':')+1:].strip())
467 self.updateProfileToAllControls()
469 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)
472 def OnSaveProfile(self, e):
473 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
474 dlg.SetWildcard("ini files (*.ini)|*.ini")
475 if dlg.ShowModal() == wx.ID_OK:
476 profileFile = dlg.GetPath()
477 profile.saveProfile(profileFile)
480 def OnResetProfile(self, e):
481 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)
482 result = dlg.ShowModal() == wx.ID_YES
485 profile.resetProfile()
486 self.updateProfileToAllControls()
488 def OnSimpleSwitch(self, e):
489 profile.putPreference('startMode', 'Simple')
490 self.updateSliceMode()
492 def OnNormalSwitch(self, e):
493 profile.putPreference('startMode', 'Normal')
494 self.updateSliceMode()
496 def OnDefaultMarlinFirmware(self, e):
497 firmwareInstall.InstallFirmware()
499 def OnCustomFirmware(self, e):
500 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
501 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)
502 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
503 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
504 if dlg.ShowModal() == wx.ID_OK:
505 filename = dlg.GetPath()
506 if not(os.path.exists(filename)):
508 #For some reason my Ubuntu 10.10 crashes here.
509 firmwareInstall.InstallFirmware(filename)
511 def OnFirstRunWizard(self, e):
513 configWizard.configWizard()
515 self.reloadSettingPanels()
517 def OnSelectMachine(self, index):
518 profile.setActiveMachine(index)
519 self.reloadSettingPanels()
521 def OnBedLevelWizard(self, e):
522 configWizard.bedLevelWizard()
524 def OnHeadOffsetWizard(self, e):
525 configWizard.headOffsetWizard()
527 def OnExpertOpen(self, e):
528 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
532 def OnMinecraftImport(self, e):
533 mi = minecraftImport.minecraftImportWindow(self)
537 def OnPIDDebugger(self, e):
538 debugger = pidDebugger.debuggerWindow(self)
542 def onCopyProfileClipboard(self, e):
544 if not wx.TheClipboard.IsOpened():
545 wx.TheClipboard.Open()
546 clipData = wx.TextDataObject()
547 self.lastTriedClipboard = profile.getProfileString()
548 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
549 clipData.SetText(profileString)
550 wx.TheClipboard.SetData(clipData)
551 wx.TheClipboard.Close()
553 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
555 def OnCheckForUpdate(self, e):
556 newVersion = version.checkForNewerVersion()
557 if newVersion is not None:
558 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:
559 webbrowser.open(newVersion)
561 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
563 def OnAbout(self, e):
564 aboutBox = aboutWindow.aboutWindow()
568 def OnClose(self, e):
569 profile.saveProfile(profile.getDefaultProfilePath(), True)
571 # Save the window position, size & state from the preferences file
572 profile.putPreference('window_maximized', self.IsMaximized())
573 if not self.IsMaximized() and not self.IsIconized():
574 (posx, posy) = self.GetPosition()
575 profile.putPreference('window_pos_x', posx)
576 profile.putPreference('window_pos_y', posy)
577 (width, height) = self.GetSize()
578 profile.putPreference('window_width', width)
579 profile.putPreference('window_height', height)
581 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
582 isSimple = profile.getPreference('startMode') == 'Simple'
584 self.normalSashPos = self.splitter.GetSashPosition()
585 profile.putPreference('window_normal_sash', self.normalSashPos)
587 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
589 self.scene.OnPaint = lambda e : e
590 self.scene._engine.cleanup()
596 class normalSettingsPanel(configBase.configPanelBase):
597 "Main user interface window"
598 def __init__(self, parent, callback = None):
599 super(normalSettingsPanel, self).__init__(parent, callback)
602 self.nb = wx.Notebook(self)
603 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
604 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
606 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
607 self._addSettingsToPanels('basic', left, right)
608 self.SizeLabelWidths(left, right)
610 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
611 self._addSettingsToPanels('advanced', left, right)
612 self.SizeLabelWidths(left, right)
615 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
616 self.nb.AddPage(self.pluginPanel, _("Plugins"))
619 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
620 self.alterationPanel = None
622 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
623 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
625 self.Bind(wx.EVT_SIZE, self.OnSize)
627 self.nb.SetSize(self.GetSize())
628 self.UpdateSize(self.printPanel)
629 self.UpdateSize(self.advancedPanel)
631 def _addSettingsToPanels(self, category, left, right):
632 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
636 for title in profile.getSubCategoriesFor(category):
637 n += 1 + len(profile.getSettingsForCategory(category, title))
640 configBase.TitleRow(p, _(title))
641 for s in profile.getSettingsForCategory(category, title):
642 configBase.SettingRow(p, s.getName())
644 def SizeLabelWidths(self, left, right):
645 leftWidth = self.getLabelColumnWidth(left)
646 rightWidth = self.getLabelColumnWidth(right)
647 maxWidth = max(leftWidth, rightWidth)
648 self.setLabelColumnWidth(left, maxWidth)
649 self.setLabelColumnWidth(right, maxWidth)
652 # Make the size of the Notebook control the same size as this control
653 self.nb.SetSize(self.GetSize())
655 # Propegate the OnSize() event (just in case)
658 # Perform out resize magic
659 self.UpdateSize(self.printPanel)
660 self.UpdateSize(self.advancedPanel)
662 def UpdateSize(self, configPanel):
663 sizer = configPanel.GetSizer()
667 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
670 # if width(col1) > (best_width(col1) + best_width(col1)):
671 # switch to horizontal
674 col1 = configPanel.leftPanel
675 colSize1 = col1.GetSize()
676 colBestSize1 = col1.GetBestSize()
677 col2 = configPanel.rightPanel
678 colSize2 = col2.GetSize()
679 colBestSize2 = col2.GetBestSize()
681 orientation = sizer.GetOrientation()
683 if orientation == wx.HORIZONTAL:
684 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
686 sizer = wx.BoxSizer(wx.VERTICAL)
687 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
688 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
689 configPanel.SetSizer(sizer)
695 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
697 sizer = wx.BoxSizer(wx.HORIZONTAL)
698 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
699 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
700 configPanel.SetSizer(sizer)
706 def updateProfileToControls(self):
707 super(normalSettingsPanel, self).updateProfileToControls()
708 if self.alterationPanel is not None:
709 self.alterationPanel.updateProfileToControls()
710 self.pluginPanel.updateProfileToControls()