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()
330 self.scene.sceneUpdated()
332 def OnPreferences(self, e):
333 prefDialog = preferencesDialog.preferencesDialog(self)
337 wx.CallAfter(prefDialog.Show)
339 def OnMachineSettings(self, e):
340 prefDialog = preferencesDialog.machineSettingsDialog(self)
345 def OnDropFiles(self, files):
347 profile.setPluginConfig([])
348 self.updateProfileToAllControls()
349 self.scene.loadFiles(files)
351 def OnModelMRU(self, e):
352 fileNum = e.GetId() - self.ID_MRU_MODEL1
353 path = self.modelFileHistory.GetHistoryFile(fileNum)
355 self.modelFileHistory.AddFileToHistory(path) # move up the list
356 self.config.SetPath("/ModelMRU")
357 self.modelFileHistory.Save(self.config)
360 profile.putPreference('lastFile', path)
362 self.scene.loadFiles(filelist)
364 def addToModelMRU(self, file):
365 self.modelFileHistory.AddFileToHistory(file)
366 self.config.SetPath("/ModelMRU")
367 self.modelFileHistory.Save(self.config)
370 def OnProfileMRU(self, e):
371 fileNum = e.GetId() - self.ID_MRU_PROFILE1
372 path = self.profileFileHistory.GetHistoryFile(fileNum)
374 self.profileFileHistory.AddFileToHistory(path) # move up the list
375 self.config.SetPath("/ProfileMRU")
376 self.profileFileHistory.Save(self.config)
379 profile.loadProfile(path)
380 self.updateProfileToAllControls()
382 def addToProfileMRU(self, file):
383 self.profileFileHistory.AddFileToHistory(file)
384 self.config.SetPath("/ProfileMRU")
385 self.profileFileHistory.Save(self.config)
388 def updateProfileToAllControls(self):
389 self.scene.updateProfileToControls()
390 self.normalSettingsPanel.updateProfileToControls()
391 self.simpleSettingsPanel.updateProfileToControls()
393 def reloadSettingPanels(self):
394 self.leftSizer.Detach(self.simpleSettingsPanel)
395 self.leftSizer.Detach(self.normalSettingsPanel)
396 self.simpleSettingsPanel.Destroy()
397 self.normalSettingsPanel.Destroy()
398 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
399 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
400 self.leftSizer.Add(self.simpleSettingsPanel, 1)
401 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
402 self.updateSliceMode()
403 self.updateProfileToAllControls()
405 def updateMachineMenu(self):
406 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
407 for item in self.machineMenu.GetMenuItems():
408 self.machineMenu.RemoveItem(item)
410 #Add a menu item for each machine configuration.
411 for n in xrange(0, profile.getMachineCount()):
412 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
413 if n == int(profile.getPreferenceFloat('active_machine')):
415 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
417 self.machineMenu.AppendSeparator()
419 i = self.machineMenu.Append(-1, _("Machine settings..."))
420 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
422 #Add tools for machines.
423 self.machineMenu.AppendSeparator()
425 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
426 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
428 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
429 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
431 def OnLoadProfile(self, e):
432 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)
433 dlg.SetWildcard("ini files (*.ini)|*.ini")
434 if dlg.ShowModal() == wx.ID_OK:
435 profileFile = dlg.GetPath()
436 profile.loadProfile(profileFile)
437 self.updateProfileToAllControls()
439 # Update the Profile MRU
440 self.addToProfileMRU(profileFile)
443 def OnLoadProfileFromGcode(self, e):
444 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)
445 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
446 if dlg.ShowModal() == wx.ID_OK:
447 gcodeFile = dlg.GetPath()
448 f = open(gcodeFile, 'r')
451 if line.startswith(';CURA_PROFILE_STRING:'):
452 profile.setProfileFromString(line[line.find(':')+1:].strip())
455 self.updateProfileToAllControls()
457 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)
460 def OnSaveProfile(self, e):
461 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
462 dlg.SetWildcard("ini files (*.ini)|*.ini")
463 if dlg.ShowModal() == wx.ID_OK:
464 profileFile = dlg.GetPath()
465 profile.saveProfile(profileFile)
468 def OnResetProfile(self, e):
469 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)
470 result = dlg.ShowModal() == wx.ID_YES
473 profile.resetProfile()
474 self.updateProfileToAllControls()
476 def OnSimpleSwitch(self, e):
477 profile.putPreference('startMode', 'Simple')
478 self.updateSliceMode()
480 def OnNormalSwitch(self, e):
481 profile.putPreference('startMode', 'Normal')
482 self.updateSliceMode()
484 def OnDefaultMarlinFirmware(self, e):
485 firmwareInstall.InstallFirmware()
487 def OnCustomFirmware(self, e):
488 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
489 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)
490 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
491 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
492 if dlg.ShowModal() == wx.ID_OK:
493 filename = dlg.GetPath()
494 if not(os.path.exists(filename)):
496 #For some reason my Ubuntu 10.10 crashes here.
497 firmwareInstall.InstallFirmware(filename)
499 def OnFirstRunWizard(self, e):
501 configWizard.configWizard()
503 self.reloadSettingPanels()
505 def OnSelectMachine(self, index):
506 profile.setActiveMachine(index)
507 self.reloadSettingPanels()
509 def OnBedLevelWizard(self, e):
510 configWizard.bedLevelWizard()
512 def OnHeadOffsetWizard(self, e):
513 configWizard.headOffsetWizard()
515 def OnExpertOpen(self, e):
516 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
520 def OnMinecraftImport(self, e):
521 mi = minecraftImport.minecraftImportWindow(self)
525 def OnPIDDebugger(self, e):
526 debugger = pidDebugger.debuggerWindow(self)
530 def onCopyProfileClipboard(self, e):
532 if not wx.TheClipboard.IsOpened():
533 wx.TheClipboard.Open()
534 clipData = wx.TextDataObject()
535 self.lastTriedClipboard = profile.getProfileString()
536 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
537 clipData.SetText(profileString)
538 wx.TheClipboard.SetData(clipData)
539 wx.TheClipboard.Close()
541 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
543 def OnCheckForUpdate(self, e):
544 newVersion = version.checkForNewerVersion()
545 if newVersion is not None:
546 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:
547 webbrowser.open(newVersion)
549 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
551 def OnAbout(self, e):
552 aboutBox = aboutWindow.aboutWindow()
556 def OnClose(self, e):
557 profile.saveProfile(profile.getDefaultProfilePath())
559 # Save the window position, size & state from the preferences file
560 profile.putPreference('window_maximized', self.IsMaximized())
561 if not self.IsMaximized() and not self.IsIconized():
562 (posx, posy) = self.GetPosition()
563 profile.putPreference('window_pos_x', posx)
564 profile.putPreference('window_pos_y', posy)
565 (width, height) = self.GetSize()
566 profile.putPreference('window_width', width)
567 profile.putPreference('window_height', height)
569 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
570 isSimple = profile.getPreference('startMode') == 'Simple'
572 self.normalSashPos = self.splitter.GetSashPosition()
573 profile.putPreference('window_normal_sash', self.normalSashPos)
575 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
577 self.scene.OnPaint = lambda e : e
578 self.scene._engine.cleanup()
584 class normalSettingsPanel(configBase.configPanelBase):
585 "Main user interface window"
586 def __init__(self, parent, callback = None):
587 super(normalSettingsPanel, self).__init__(parent, callback)
590 self.nb = wx.Notebook(self)
591 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
592 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
594 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
595 self._addSettingsToPanels('basic', left, right)
596 self.SizeLabelWidths(left, right)
598 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
599 self._addSettingsToPanels('advanced', left, right)
600 self.SizeLabelWidths(left, right)
603 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
604 self.nb.AddPage(self.pluginPanel, _("Plugins"))
607 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
608 self.alterationPanel = None
610 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
611 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
613 self.Bind(wx.EVT_SIZE, self.OnSize)
615 self.nb.SetSize(self.GetSize())
616 self.UpdateSize(self.printPanel)
617 self.UpdateSize(self.advancedPanel)
619 def _addSettingsToPanels(self, category, left, right):
620 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
624 for title in profile.getSubCategoriesFor(category):
625 n += 1 + len(profile.getSettingsForCategory(category, title))
628 configBase.TitleRow(p, _(title))
629 for s in profile.getSettingsForCategory(category, title):
630 configBase.SettingRow(p, s.getName())
632 def SizeLabelWidths(self, left, right):
633 leftWidth = self.getLabelColumnWidth(left)
634 rightWidth = self.getLabelColumnWidth(right)
635 maxWidth = max(leftWidth, rightWidth)
636 self.setLabelColumnWidth(left, maxWidth)
637 self.setLabelColumnWidth(right, maxWidth)
640 # Make the size of the Notebook control the same size as this control
641 self.nb.SetSize(self.GetSize())
643 # Propegate the OnSize() event (just in case)
646 # Perform out resize magic
647 self.UpdateSize(self.printPanel)
648 self.UpdateSize(self.advancedPanel)
650 def UpdateSize(self, configPanel):
651 sizer = configPanel.GetSizer()
655 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
658 # if width(col1) > (best_width(col1) + best_width(col1)):
659 # switch to horizontal
662 col1 = configPanel.leftPanel
663 colSize1 = col1.GetSize()
664 colBestSize1 = col1.GetBestSize()
665 col2 = configPanel.rightPanel
666 colSize2 = col2.GetSize()
667 colBestSize2 = col2.GetBestSize()
669 orientation = sizer.GetOrientation()
671 if orientation == wx.HORIZONTAL:
672 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
674 sizer = wx.BoxSizer(wx.VERTICAL)
675 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
676 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
677 configPanel.SetSizer(sizer)
683 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
685 sizer = wx.BoxSizer(wx.HORIZONTAL)
686 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
687 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
688 configPanel.SetSizer(sizer)
694 def updateProfileToControls(self):
695 super(normalSettingsPanel, self).updateProfileToControls()
696 if self.alterationPanel is not None:
697 self.alterationPanel.updateProfileToControls()
698 self.pluginPanel.updateProfileToControls()