1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
7 from Cura.gui import configBase
8 from Cura.gui import expertConfig
9 from Cura.gui import alterationPanel
10 from Cura.gui import pluginPanel
11 from Cura.gui import preferencesDialog
12 from Cura.gui import configWizard
13 from Cura.gui import firmwareInstall
14 from Cura.gui import simpleMode
15 from Cura.gui import sceneView
16 from Cura.gui import aboutWindow
17 from Cura.gui.util import dropTarget
18 #from Cura.gui.tools import batchRun
19 from Cura.gui.tools import pidDebugger
20 from Cura.gui.tools import minecraftImport
21 from Cura.util import profile
22 from Cura.util import version
23 from Cura.util import meshLoader
25 class mainWindow(wx.Frame):
27 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
29 wx.EVT_CLOSE(self, self.OnClose)
31 # allow dropping any file, restrict later
32 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
34 self.normalModeOnlyItems = []
36 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
37 self.config = wx.FileConfig(appName="Cura",
38 localFilename=mruFile,
39 style=wx.CONFIG_USE_LOCAL_FILE)
41 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)]
42 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
43 self.config.SetPath("/ModelMRU")
44 self.modelFileHistory.Load(self.config)
46 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)]
47 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
48 self.config.SetPath("/ProfileMRU")
49 self.profileFileHistory.Load(self.config)
51 self.menubar = wx.MenuBar()
52 self.fileMenu = wx.Menu()
53 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
54 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
55 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
56 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
57 i = self.fileMenu.Append(-1, _("Clear platform"))
58 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
60 self.fileMenu.AppendSeparator()
61 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
62 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnPrintButton(1), i)
63 i = self.fileMenu.Append(-1, _("Save GCode..."))
64 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
65 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
66 self.Bind(wx.EVT_MENU, lambda e: self.scene._showEngineLog(), i)
68 self.fileMenu.AppendSeparator()
69 i = self.fileMenu.Append(-1, _("Open Profile..."))
70 self.normalModeOnlyItems.append(i)
71 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
72 i = self.fileMenu.Append(-1, _("Save Profile..."))
73 self.normalModeOnlyItems.append(i)
74 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
75 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
76 self.normalModeOnlyItems.append(i)
77 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
78 self.fileMenu.AppendSeparator()
79 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
80 self.normalModeOnlyItems.append(i)
81 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
83 self.fileMenu.AppendSeparator()
84 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
85 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
86 i = self.fileMenu.Append(-1, _("Machine settings..."))
87 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
88 self.fileMenu.AppendSeparator()
91 modelHistoryMenu = wx.Menu()
92 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
93 self.modelFileHistory.UseMenu(modelHistoryMenu)
94 self.modelFileHistory.AddFilesToMenu()
95 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
98 profileHistoryMenu = wx.Menu()
99 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
100 self.profileFileHistory.UseMenu(profileHistoryMenu)
101 self.profileFileHistory.AddFilesToMenu()
102 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
104 self.fileMenu.AppendSeparator()
105 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
106 self.Bind(wx.EVT_MENU, self.OnQuit, i)
107 self.menubar.Append(self.fileMenu, '&' + _("File"))
109 toolsMenu = wx.Menu()
110 #i = toolsMenu.Append(-1, 'Batch run...')
111 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
112 #self.normalModeOnlyItems.append(i)
114 if minecraftImport.hasMinecraft():
115 i = toolsMenu.Append(-1, _("Minecraft map import..."))
116 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
118 if version.isDevVersion():
119 i = toolsMenu.Append(-1, _("PID Debugger..."))
120 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
122 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
123 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
125 toolsMenu.AppendSeparator()
126 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
127 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
128 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
129 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
130 if profile.getPreference('oneAtATime') == 'True':
131 self.oneAtATime.Check(True)
133 self.allAtOnceItem.Check(True)
135 self.menubar.Append(toolsMenu, _("Tools"))
137 #Machine menu for machine configuration/tooling
138 self.machineMenu = wx.Menu()
139 self.updateMachineMenu()
141 self.menubar.Append(self.machineMenu, _("Machine"))
143 expertMenu = wx.Menu()
144 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
145 self.switchToQuickprintMenuItem = i
146 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
148 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
149 self.switchToNormalMenuItem = i
150 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
151 expertMenu.AppendSeparator()
153 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
154 self.normalModeOnlyItems.append(i)
155 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
156 expertMenu.AppendSeparator()
157 i = expertMenu.Append(-1, _("Run first run wizard..."))
158 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
159 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
160 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
161 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
162 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
164 self.menubar.Append(expertMenu, _("Expert"))
167 i = helpMenu.Append(-1, _("Online documentation..."))
168 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
169 i = helpMenu.Append(-1, _("Report a problem..."))
170 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
171 i = helpMenu.Append(-1, _("Check for update..."))
172 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
173 i = helpMenu.Append(-1, _("Open YouMagine website..."))
174 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
175 i = helpMenu.Append(-1, _("About Cura..."))
176 self.Bind(wx.EVT_MENU, self.OnAbout, i)
177 self.menubar.Append(helpMenu, _("Help"))
178 self.SetMenuBar(self.menubar)
180 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
181 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
182 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
183 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
186 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
187 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
189 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
190 self.leftSizer.Add(self.simpleSettingsPanel, 1)
191 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
192 self.leftPane.SetSizer(self.leftSizer)
195 self.scene = sceneView.SceneView(self.rightPane)
197 #Main sizer, to position the preview window, buttons and tab control
198 sizer = wx.BoxSizer()
199 self.rightPane.SetSizer(sizer)
200 sizer.Add(self.scene, 1, flag=wx.EXPAND)
203 sizer = wx.BoxSizer(wx.VERTICAL)
205 sizer.Add(self.splitter, 1, wx.EXPAND)
209 self.updateProfileToAllControls()
211 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
213 self.simpleSettingsPanel.Show(False)
214 self.normalSettingsPanel.Show(False)
216 # Set default window size & position
217 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
220 #Timer set; used to check if profile is on the clipboard
221 self.timer = wx.Timer(self)
222 self.Bind(wx.EVT_TIMER, self.onTimer)
223 self.timer.Start(1000)
224 self.lastTriedClipboard = profile.getProfileString()
226 # Restore the window position, size & state from the preferences file
228 if profile.getPreference('window_maximized') == 'True':
231 posx = int(profile.getPreference('window_pos_x'))
232 posy = int(profile.getPreference('window_pos_y'))
233 width = int(profile.getPreference('window_width'))
234 height = int(profile.getPreference('window_height'))
235 if posx > 0 or posy > 0:
236 self.SetPosition((posx,posy))
237 if width > 0 and height > 0:
238 self.SetSize((width,height))
240 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
242 self.normalSashPos = 0
244 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
245 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
247 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
249 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
251 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
253 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
254 self.SetSize((800,600))
257 self.updateSliceMode()
258 self.scene.SetFocus()
260 def onTimer(self, e):
261 #Check if there is something in the clipboard
264 if not wx.TheClipboard.IsOpened():
265 if not wx.TheClipboard.Open():
267 do = wx.TextDataObject()
268 if wx.TheClipboard.GetData(do):
269 profileString = do.GetText()
270 wx.TheClipboard.Close()
272 startTag = "CURA_PROFILE_STRING:"
273 if startTag in profileString:
274 #print "Found correct syntax on clipboard"
275 profileString = profileString.replace("\n","").strip()
276 profileString = profileString[profileString.find(startTag)+len(startTag):]
277 if profileString != self.lastTriedClipboard:
279 self.lastTriedClipboard = profileString
280 profile.setProfileFromString(profileString)
281 self.scene.notification.message("Loaded new profile from clipboard.")
282 self.updateProfileToAllControls()
284 print "Unable to read from clipboard"
287 def updateSliceMode(self):
288 isSimple = profile.getPreference('startMode') == 'Simple'
290 self.normalSettingsPanel.Show(not isSimple)
291 self.simpleSettingsPanel.Show(isSimple)
292 self.leftPane.Layout()
294 for i in self.normalModeOnlyItems:
295 i.Enable(not isSimple)
297 self.switchToQuickprintMenuItem.Check()
299 self.switchToNormalMenuItem.Check()
301 # Set splitter sash position & size
303 # Save normal mode sash
304 self.normalSashPos = self.splitter.GetSashPosition()
306 # Change location of sash to width of quick mode pane
307 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
308 self.splitter.SetSashPosition(width, True)
311 self.splitter.SetSashSize(0)
313 self.splitter.SetSashPosition(self.normalSashPos, True)
315 self.splitter.SetSashSize(4)
316 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
317 if profile.getMachineSetting('machine_type') == 'ultimaker2':
318 self.bedLevelWizardMenuItem.Enable(False)
319 self.headOffsetWizardMenuItem.Enable(False)
320 if int(profile.getMachineSetting('extruder_amount')) < 2:
321 self.headOffsetWizardMenuItem.Enable(False)
322 self.scene.updateProfileToControls()
324 def onOneAtATimeSwitch(self, e):
325 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
326 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
327 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)
328 self.scene.updateProfileToControls()
329 self.scene.sceneUpdated()
331 def OnPreferences(self, e):
332 prefDialog = preferencesDialog.preferencesDialog(self)
336 wx.CallAfter(prefDialog.Show)
338 def OnMachineSettings(self, e):
339 prefDialog = preferencesDialog.machineSettingsDialog(self)
344 def OnDropFiles(self, files):
346 profile.setPluginConfig([])
347 self.updateProfileToAllControls()
348 self.scene.loadFiles(files)
350 def OnModelMRU(self, e):
351 fileNum = e.GetId() - self.ID_MRU_MODEL1
352 path = self.modelFileHistory.GetHistoryFile(fileNum)
354 self.modelFileHistory.AddFileToHistory(path) # move up the list
355 self.config.SetPath("/ModelMRU")
356 self.modelFileHistory.Save(self.config)
359 profile.putPreference('lastFile', path)
361 self.scene.loadFiles(filelist)
363 def addToModelMRU(self, file):
364 self.modelFileHistory.AddFileToHistory(file)
365 self.config.SetPath("/ModelMRU")
366 self.modelFileHistory.Save(self.config)
369 def OnProfileMRU(self, e):
370 fileNum = e.GetId() - self.ID_MRU_PROFILE1
371 path = self.profileFileHistory.GetHistoryFile(fileNum)
373 self.profileFileHistory.AddFileToHistory(path) # move up the list
374 self.config.SetPath("/ProfileMRU")
375 self.profileFileHistory.Save(self.config)
378 profile.loadProfile(path)
379 self.updateProfileToAllControls()
381 def addToProfileMRU(self, file):
382 self.profileFileHistory.AddFileToHistory(file)
383 self.config.SetPath("/ProfileMRU")
384 self.profileFileHistory.Save(self.config)
387 def updateProfileToAllControls(self):
388 self.scene.updateProfileToControls()
389 self.normalSettingsPanel.updateProfileToControls()
390 self.simpleSettingsPanel.updateProfileToControls()
392 def reloadSettingPanels(self):
393 self.leftSizer.Detach(self.simpleSettingsPanel)
394 self.leftSizer.Detach(self.normalSettingsPanel)
395 self.simpleSettingsPanel.Destroy()
396 self.normalSettingsPanel.Destroy()
397 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
398 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
399 self.leftSizer.Add(self.simpleSettingsPanel, 1)
400 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
401 self.updateSliceMode()
402 self.updateProfileToAllControls()
404 def updateMachineMenu(self):
405 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
406 for item in self.machineMenu.GetMenuItems():
407 self.machineMenu.RemoveItem(item)
409 #Add a menu item for each machine configuration.
410 for n in xrange(0, profile.getMachineCount()):
411 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
412 if n == int(profile.getPreferenceFloat('active_machine')):
414 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
416 self.machineMenu.AppendSeparator()
418 i = self.machineMenu.Append(-1, _("Machine settings..."))
419 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
421 #Add tools for machines.
422 self.machineMenu.AppendSeparator()
424 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
425 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
427 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
428 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
430 def OnLoadProfile(self, e):
431 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)
432 dlg.SetWildcard("ini files (*.ini)|*.ini")
433 if dlg.ShowModal() == wx.ID_OK:
434 profileFile = dlg.GetPath()
435 profile.loadProfile(profileFile)
436 self.updateProfileToAllControls()
438 # Update the Profile MRU
439 self.addToProfileMRU(profileFile)
442 def OnLoadProfileFromGcode(self, e):
443 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)
444 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
445 if dlg.ShowModal() == wx.ID_OK:
446 gcodeFile = dlg.GetPath()
447 f = open(gcodeFile, 'r')
450 if line.startswith(';CURA_PROFILE_STRING:'):
451 profile.setProfileFromString(line[line.find(':')+1:].strip())
454 self.updateProfileToAllControls()
456 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)
459 def OnSaveProfile(self, e):
460 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
461 dlg.SetWildcard("ini files (*.ini)|*.ini")
462 if dlg.ShowModal() == wx.ID_OK:
463 profileFile = dlg.GetPath()
464 profile.saveProfile(profileFile)
467 def OnResetProfile(self, e):
468 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)
469 result = dlg.ShowModal() == wx.ID_YES
472 profile.resetProfile()
473 self.updateProfileToAllControls()
475 def OnSimpleSwitch(self, e):
476 profile.putPreference('startMode', 'Simple')
477 self.updateSliceMode()
479 def OnNormalSwitch(self, e):
480 profile.putPreference('startMode', 'Normal')
481 self.updateSliceMode()
483 def OnDefaultMarlinFirmware(self, e):
484 firmwareInstall.InstallFirmware()
486 def OnCustomFirmware(self, e):
487 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
488 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)
489 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
490 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
491 if dlg.ShowModal() == wx.ID_OK:
492 filename = dlg.GetPath()
493 if not(os.path.exists(filename)):
495 #For some reason my Ubuntu 10.10 crashes here.
496 firmwareInstall.InstallFirmware(filename)
498 def OnFirstRunWizard(self, e):
500 configWizard.configWizard()
502 self.reloadSettingPanels()
504 def OnSelectMachine(self, index):
505 profile.setActiveMachine(index)
506 self.reloadSettingPanels()
508 def OnBedLevelWizard(self, e):
509 configWizard.bedLevelWizard()
511 def OnHeadOffsetWizard(self, e):
512 configWizard.headOffsetWizard()
514 def OnExpertOpen(self, e):
515 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
519 def OnMinecraftImport(self, e):
520 mi = minecraftImport.minecraftImportWindow(self)
524 def OnPIDDebugger(self, e):
525 debugger = pidDebugger.debuggerWindow(self)
529 def onCopyProfileClipboard(self, e):
531 if not wx.TheClipboard.IsOpened():
532 wx.TheClipboard.Open()
533 clipData = wx.TextDataObject()
534 self.lastTriedClipboard = profile.getProfileString()
535 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
536 clipData.SetText(profileString)
537 wx.TheClipboard.SetData(clipData)
538 wx.TheClipboard.Close()
540 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
542 def OnCheckForUpdate(self, e):
543 newVersion = version.checkForNewerVersion()
544 if newVersion is not None:
545 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:
546 webbrowser.open(newVersion)
548 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
550 def OnAbout(self, e):
551 aboutBox = aboutWindow.aboutWindow()
555 def OnClose(self, e):
556 profile.saveProfile(profile.getDefaultProfilePath())
558 # Save the window position, size & state from the preferences file
559 profile.putPreference('window_maximized', self.IsMaximized())
560 if not self.IsMaximized() and not self.IsIconized():
561 (posx, posy) = self.GetPosition()
562 profile.putPreference('window_pos_x', posx)
563 profile.putPreference('window_pos_y', posy)
564 (width, height) = self.GetSize()
565 profile.putPreference('window_width', width)
566 profile.putPreference('window_height', height)
568 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
569 isSimple = profile.getPreference('startMode') == 'Simple'
571 self.normalSashPos = self.splitter.GetSashPosition()
572 profile.putPreference('window_normal_sash', self.normalSashPos)
574 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
576 self.scene.OnPaint = lambda e : e
577 self.scene._engine.cleanup()
583 class normalSettingsPanel(configBase.configPanelBase):
584 "Main user interface window"
585 def __init__(self, parent, callback = None):
586 super(normalSettingsPanel, self).__init__(parent, callback)
589 self.nb = wx.Notebook(self)
590 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
591 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
593 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
594 self._addSettingsToPanels('basic', left, right)
595 self.SizeLabelWidths(left, right)
597 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
598 self._addSettingsToPanels('advanced', left, right)
599 self.SizeLabelWidths(left, right)
602 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
603 self.nb.AddPage(self.pluginPanel, _("Plugins"))
606 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
607 self.alterationPanel = None
609 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
610 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
612 self.Bind(wx.EVT_SIZE, self.OnSize)
614 self.nb.SetSize(self.GetSize())
615 self.UpdateSize(self.printPanel)
616 self.UpdateSize(self.advancedPanel)
618 def _addSettingsToPanels(self, category, left, right):
619 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
623 for title in profile.getSubCategoriesFor(category):
624 n += 1 + len(profile.getSettingsForCategory(category, title))
627 configBase.TitleRow(p, _(title))
628 for s in profile.getSettingsForCategory(category, title):
629 configBase.SettingRow(p, s.getName())
631 def SizeLabelWidths(self, left, right):
632 leftWidth = self.getLabelColumnWidth(left)
633 rightWidth = self.getLabelColumnWidth(right)
634 maxWidth = max(leftWidth, rightWidth)
635 self.setLabelColumnWidth(left, maxWidth)
636 self.setLabelColumnWidth(right, maxWidth)
639 # Make the size of the Notebook control the same size as this control
640 self.nb.SetSize(self.GetSize())
642 # Propegate the OnSize() event (just in case)
645 # Perform out resize magic
646 self.UpdateSize(self.printPanel)
647 self.UpdateSize(self.advancedPanel)
649 def UpdateSize(self, configPanel):
650 sizer = configPanel.GetSizer()
654 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
657 # if width(col1) > (best_width(col1) + best_width(col1)):
658 # switch to horizontal
661 col1 = configPanel.leftPanel
662 colSize1 = col1.GetSize()
663 colBestSize1 = col1.GetBestSize()
664 col2 = configPanel.rightPanel
665 colSize2 = col2.GetSize()
666 colBestSize2 = col2.GetBestSize()
668 orientation = sizer.GetOrientation()
670 if orientation == wx.HORIZONTAL:
671 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
673 sizer = wx.BoxSizer(wx.VERTICAL)
674 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
675 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
676 configPanel.SetSizer(sizer)
682 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
684 sizer = wx.BoxSizer(wx.HORIZONTAL)
685 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
686 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
687 configPanel.SetSizer(sizer)
693 def updateProfileToControls(self):
694 super(normalSettingsPanel, self).updateProfileToControls()
695 if self.alterationPanel is not None:
696 self.alterationPanel.updateProfileToControls()
697 self.pluginPanel.updateProfileToControls()