1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
8 from Cura.gui import configBase
9 from Cura.gui import expertConfig
10 from Cura.gui import alterationPanel
11 from Cura.gui import pluginPanel
12 from Cura.gui import preferencesDialog
13 from Cura.gui import configWizard
14 from Cura.gui import firmwareInstall
15 from Cura.gui import simpleMode
16 from Cura.gui import sceneView
17 from Cura.gui import aboutWindow
18 from Cura.gui.util import dropTarget
19 #from Cura.gui.tools import batchRun
20 from Cura.gui.tools import pidDebugger
21 from Cura.gui.tools import minecraftImport
22 from Cura.util import profile
23 from Cura.util import version
24 from Cura.util import meshLoader
26 class mainWindow(wx.Frame):
28 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
30 wx.EVT_CLOSE(self, self.OnClose)
32 # allow dropping any file, restrict later
33 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
35 self.normalModeOnlyItems = []
37 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
38 self.config = wx.FileConfig(appName="Cura",
39 localFilename=mruFile,
40 style=wx.CONFIG_USE_LOCAL_FILE)
42 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)]
43 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
44 self.config.SetPath("/ModelMRU")
45 self.modelFileHistory.Load(self.config)
47 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)]
48 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
49 self.config.SetPath("/ProfileMRU")
50 self.profileFileHistory.Load(self.config)
52 self.menubar = wx.MenuBar()
53 self.fileMenu = wx.Menu()
54 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
55 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
56 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
57 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
58 i = self.fileMenu.Append(-1, _("Clear platform"))
59 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
61 self.fileMenu.AppendSeparator()
62 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
63 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
64 i = self.fileMenu.Append(-1, _("Save GCode..."))
65 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
66 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
67 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
69 self.fileMenu.AppendSeparator()
70 i = self.fileMenu.Append(-1, _("Open Profile..."))
71 self.normalModeOnlyItems.append(i)
72 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
73 i = self.fileMenu.Append(-1, _("Save Profile..."))
74 self.normalModeOnlyItems.append(i)
75 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
76 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
77 self.normalModeOnlyItems.append(i)
78 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
79 self.fileMenu.AppendSeparator()
80 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
81 self.normalModeOnlyItems.append(i)
82 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
84 self.fileMenu.AppendSeparator()
85 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
86 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
87 i = self.fileMenu.Append(-1, _("Machine settings..."))
88 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
89 self.fileMenu.AppendSeparator()
92 modelHistoryMenu = wx.Menu()
93 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
94 self.modelFileHistory.UseMenu(modelHistoryMenu)
95 self.modelFileHistory.AddFilesToMenu()
96 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
99 profileHistoryMenu = wx.Menu()
100 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
101 self.profileFileHistory.UseMenu(profileHistoryMenu)
102 self.profileFileHistory.AddFilesToMenu()
103 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
105 self.fileMenu.AppendSeparator()
106 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
107 self.Bind(wx.EVT_MENU, self.OnQuit, i)
108 self.menubar.Append(self.fileMenu, '&' + _("File"))
110 toolsMenu = wx.Menu()
111 #i = toolsMenu.Append(-1, 'Batch run...')
112 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
113 #self.normalModeOnlyItems.append(i)
115 if minecraftImport.hasMinecraft():
116 i = toolsMenu.Append(-1, _("Minecraft map import..."))
117 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
119 if version.isDevVersion():
120 i = toolsMenu.Append(-1, _("PID Debugger..."))
121 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
123 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
124 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
126 toolsMenu.AppendSeparator()
127 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
128 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
129 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
130 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
131 if profile.getPreference('oneAtATime') == 'True':
132 self.oneAtATime.Check(True)
134 self.allAtOnceItem.Check(True)
136 self.menubar.Append(toolsMenu, _("Tools"))
138 #Machine menu for machine configuration/tooling
139 self.machineMenu = wx.Menu()
140 self.updateMachineMenu()
142 self.menubar.Append(self.machineMenu, _("Machine"))
144 expertMenu = wx.Menu()
145 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
146 self.switchToQuickprintMenuItem = i
147 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
149 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
150 self.switchToNormalMenuItem = i
151 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
152 expertMenu.AppendSeparator()
154 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
155 self.normalModeOnlyItems.append(i)
156 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
157 expertMenu.AppendSeparator()
158 i = expertMenu.Append(-1, _("Run first run wizard..."))
159 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
160 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
161 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
162 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
163 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
165 self.menubar.Append(expertMenu, _("Expert"))
168 i = helpMenu.Append(-1, _("Online documentation..."))
169 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
170 i = helpMenu.Append(-1, _("Report a problem..."))
171 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
172 i = helpMenu.Append(-1, _("Check for update..."))
173 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
174 i = helpMenu.Append(-1, _("Open YouMagine website..."))
175 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
176 i = helpMenu.Append(-1, _("About Cura..."))
177 self.Bind(wx.EVT_MENU, self.OnAbout, i)
178 self.menubar.Append(helpMenu, _("Help"))
179 self.SetMenuBar(self.menubar)
181 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
182 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
183 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
184 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
187 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
188 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
190 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
191 self.leftSizer.Add(self.simpleSettingsPanel, 1)
192 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
193 self.leftPane.SetSizer(self.leftSizer)
196 self.scene = sceneView.SceneView(self.rightPane)
198 #Main sizer, to position the preview window, buttons and tab control
199 sizer = wx.BoxSizer()
200 self.rightPane.SetSizer(sizer)
201 sizer.Add(self.scene, 1, flag=wx.EXPAND)
204 sizer = wx.BoxSizer(wx.VERTICAL)
206 sizer.Add(self.splitter, 1, wx.EXPAND)
210 self.updateProfileToAllControls()
212 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
214 self.simpleSettingsPanel.Show(False)
215 self.normalSettingsPanel.Show(False)
217 # Set default window size & position
218 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
221 #Timer set; used to check if profile is on the clipboard
222 self.timer = wx.Timer(self)
223 self.Bind(wx.EVT_TIMER, self.onTimer)
224 self.timer.Start(1000)
225 self.lastTriedClipboard = profile.getProfileString()
227 # Restore the window position, size & state from the preferences file
229 if profile.getPreference('window_maximized') == 'True':
232 posx = int(profile.getPreference('window_pos_x'))
233 posy = int(profile.getPreference('window_pos_y'))
234 width = int(profile.getPreference('window_width'))
235 height = int(profile.getPreference('window_height'))
236 if posx > 0 or posy > 0:
237 self.SetPosition((posx,posy))
238 if width > 0 and height > 0:
239 self.SetSize((width,height))
241 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
243 self.normalSashPos = 0
245 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
246 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
248 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
250 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
252 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
254 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
255 self.SetSize((800,600))
258 self.updateSliceMode()
259 self.scene.SetFocus()
261 def onTimer(self, e):
262 #Check if there is something in the clipboard
265 if not wx.TheClipboard.IsOpened():
266 if not wx.TheClipboard.Open():
268 do = wx.TextDataObject()
269 if wx.TheClipboard.GetData(do):
270 profileString = do.GetText()
271 wx.TheClipboard.Close()
273 startTag = "CURA_PROFILE_STRING:"
274 if startTag in profileString:
275 #print "Found correct syntax on clipboard"
276 profileString = profileString.replace("\n","").strip()
277 profileString = profileString[profileString.find(startTag)+len(startTag):]
278 if profileString != self.lastTriedClipboard:
280 self.lastTriedClipboard = profileString
281 profile.setProfileFromString(profileString)
282 self.scene.notification.message("Loaded new profile from clipboard.")
283 self.updateProfileToAllControls()
285 print "Unable to read from clipboard"
288 def updateSliceMode(self):
289 isSimple = profile.getPreference('startMode') == 'Simple'
291 self.normalSettingsPanel.Show(not isSimple)
292 self.simpleSettingsPanel.Show(isSimple)
293 self.leftPane.Layout()
295 for i in self.normalModeOnlyItems:
296 i.Enable(not isSimple)
298 self.switchToQuickprintMenuItem.Check()
300 self.switchToNormalMenuItem.Check()
302 # Set splitter sash position & size
304 # Save normal mode sash
305 self.normalSashPos = self.splitter.GetSashPosition()
307 # Change location of sash to width of quick mode pane
308 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
309 self.splitter.SetSashPosition(width, True)
312 self.splitter.SetSashSize(0)
314 self.splitter.SetSashPosition(self.normalSashPos, True)
316 self.splitter.SetSashSize(4)
317 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
318 if profile.getMachineSetting('machine_type') == 'ultimaker2':
319 self.bedLevelWizardMenuItem.Enable(False)
320 self.headOffsetWizardMenuItem.Enable(False)
321 if int(profile.getMachineSetting('extruder_amount')) < 2:
322 self.headOffsetWizardMenuItem.Enable(False)
323 self.scene.updateProfileToControls()
325 def onOneAtATimeSwitch(self, e):
326 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
327 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
328 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)
329 self.scene.updateProfileToControls()
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._slicer.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()