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 self.updateProfileToAllControls()
347 self.scene.loadFiles(files)
349 def OnModelMRU(self, e):
350 fileNum = e.GetId() - self.ID_MRU_MODEL1
351 path = self.modelFileHistory.GetHistoryFile(fileNum)
353 self.modelFileHistory.AddFileToHistory(path) # move up the list
354 self.config.SetPath("/ModelMRU")
355 self.modelFileHistory.Save(self.config)
358 profile.putPreference('lastFile', path)
360 self.scene.loadFiles(filelist)
362 def addToModelMRU(self, file):
363 self.modelFileHistory.AddFileToHistory(file)
364 self.config.SetPath("/ModelMRU")
365 self.modelFileHistory.Save(self.config)
368 def OnProfileMRU(self, e):
369 fileNum = e.GetId() - self.ID_MRU_PROFILE1
370 path = self.profileFileHistory.GetHistoryFile(fileNum)
372 self.profileFileHistory.AddFileToHistory(path) # move up the list
373 self.config.SetPath("/ProfileMRU")
374 self.profileFileHistory.Save(self.config)
377 profile.loadProfile(path)
378 self.updateProfileToAllControls()
380 def addToProfileMRU(self, file):
381 self.profileFileHistory.AddFileToHistory(file)
382 self.config.SetPath("/ProfileMRU")
383 self.profileFileHistory.Save(self.config)
386 def updateProfileToAllControls(self):
387 self.scene.updateProfileToControls()
388 self.normalSettingsPanel.updateProfileToControls()
389 self.simpleSettingsPanel.updateProfileToControls()
391 def reloadSettingPanels(self):
392 self.leftSizer.Detach(self.simpleSettingsPanel)
393 self.leftSizer.Detach(self.normalSettingsPanel)
394 self.simpleSettingsPanel.Destroy()
395 self.normalSettingsPanel.Destroy()
396 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
397 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
398 self.leftSizer.Add(self.simpleSettingsPanel, 1)
399 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
400 self.updateSliceMode()
401 self.updateProfileToAllControls()
403 def updateMachineMenu(self):
404 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
405 for item in self.machineMenu.GetMenuItems():
406 self.machineMenu.RemoveItem(item)
408 #Add a menu item for each machine configuration.
409 for n in xrange(0, profile.getMachineCount()):
410 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
411 if n == int(profile.getPreferenceFloat('active_machine')):
413 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
415 self.machineMenu.AppendSeparator()
417 i = self.machineMenu.Append(-1, _("Machine settings..."))
418 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
420 #Add tools for machines.
421 self.machineMenu.AppendSeparator()
423 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
424 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
426 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
427 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
429 def OnLoadProfile(self, e):
430 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)
431 dlg.SetWildcard("ini files (*.ini)|*.ini")
432 if dlg.ShowModal() == wx.ID_OK:
433 profileFile = dlg.GetPath()
434 profile.loadProfile(profileFile)
435 self.updateProfileToAllControls()
437 # Update the Profile MRU
438 self.addToProfileMRU(profileFile)
441 def OnLoadProfileFromGcode(self, e):
442 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)
443 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
444 if dlg.ShowModal() == wx.ID_OK:
445 gcodeFile = dlg.GetPath()
446 f = open(gcodeFile, 'r')
449 if line.startswith(';CURA_PROFILE_STRING:'):
450 profile.setProfileFromString(line[line.find(':')+1:].strip())
453 self.updateProfileToAllControls()
455 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)
458 def OnSaveProfile(self, e):
459 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
460 dlg.SetWildcard("ini files (*.ini)|*.ini")
461 if dlg.ShowModal() == wx.ID_OK:
462 profileFile = dlg.GetPath()
463 profile.saveProfile(profileFile)
466 def OnResetProfile(self, e):
467 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)
468 result = dlg.ShowModal() == wx.ID_YES
471 profile.resetProfile()
472 self.updateProfileToAllControls()
474 def OnSimpleSwitch(self, e):
475 profile.putPreference('startMode', 'Simple')
476 self.updateSliceMode()
478 def OnNormalSwitch(self, e):
479 profile.putPreference('startMode', 'Normal')
480 self.updateSliceMode()
482 def OnDefaultMarlinFirmware(self, e):
483 firmwareInstall.InstallFirmware()
485 def OnCustomFirmware(self, e):
486 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
487 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)
488 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
489 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
490 if dlg.ShowModal() == wx.ID_OK:
491 filename = dlg.GetPath()
492 if not(os.path.exists(filename)):
494 #For some reason my Ubuntu 10.10 crashes here.
495 firmwareInstall.InstallFirmware(filename)
497 def OnFirstRunWizard(self, e):
499 configWizard.configWizard()
501 self.reloadSettingPanels()
503 def OnSelectMachine(self, index):
504 profile.setActiveMachine(index)
505 self.reloadSettingPanels()
507 def OnBedLevelWizard(self, e):
508 configWizard.bedLevelWizard()
510 def OnHeadOffsetWizard(self, e):
511 configWizard.headOffsetWizard()
513 def OnExpertOpen(self, e):
514 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
518 def OnMinecraftImport(self, e):
519 mi = minecraftImport.minecraftImportWindow(self)
523 def OnPIDDebugger(self, e):
524 debugger = pidDebugger.debuggerWindow(self)
528 def onCopyProfileClipboard(self, e):
530 if not wx.TheClipboard.IsOpened():
531 wx.TheClipboard.Open()
532 clipData = wx.TextDataObject()
533 self.lastTriedClipboard = profile.getProfileString()
534 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
535 clipData.SetText(profileString)
536 wx.TheClipboard.SetData(clipData)
537 wx.TheClipboard.Close()
539 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
541 def OnCheckForUpdate(self, e):
542 newVersion = version.checkForNewerVersion()
543 if newVersion is not None:
544 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:
545 webbrowser.open(newVersion)
547 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
549 def OnAbout(self, e):
550 aboutBox = aboutWindow.aboutWindow()
554 def OnClose(self, e):
555 profile.saveProfile(profile.getDefaultProfilePath(), True)
557 # Save the window position, size & state from the preferences file
558 profile.putPreference('window_maximized', self.IsMaximized())
559 if not self.IsMaximized() and not self.IsIconized():
560 (posx, posy) = self.GetPosition()
561 profile.putPreference('window_pos_x', posx)
562 profile.putPreference('window_pos_y', posy)
563 (width, height) = self.GetSize()
564 profile.putPreference('window_width', width)
565 profile.putPreference('window_height', height)
567 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
568 isSimple = profile.getPreference('startMode') == 'Simple'
570 self.normalSashPos = self.splitter.GetSashPosition()
571 profile.putPreference('window_normal_sash', self.normalSashPos)
573 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
575 self.scene.OnPaint = lambda e : e
576 self.scene._engine.cleanup()
582 class normalSettingsPanel(configBase.configPanelBase):
583 "Main user interface window"
584 def __init__(self, parent, callback = None):
585 super(normalSettingsPanel, self).__init__(parent, callback)
588 self.nb = wx.Notebook(self)
589 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
590 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
592 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
593 self._addSettingsToPanels('basic', left, right)
594 self.SizeLabelWidths(left, right)
596 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
597 self._addSettingsToPanels('advanced', left, right)
598 self.SizeLabelWidths(left, right)
601 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
602 self.nb.AddPage(self.pluginPanel, _("Plugins"))
605 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
606 self.alterationPanel = None
608 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
609 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
611 self.Bind(wx.EVT_SIZE, self.OnSize)
613 self.nb.SetSize(self.GetSize())
614 self.UpdateSize(self.printPanel)
615 self.UpdateSize(self.advancedPanel)
617 def _addSettingsToPanels(self, category, left, right):
618 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
622 for title in profile.getSubCategoriesFor(category):
623 n += 1 + len(profile.getSettingsForCategory(category, title))
626 configBase.TitleRow(p, _(title))
627 for s in profile.getSettingsForCategory(category, title):
628 configBase.SettingRow(p, s.getName())
630 def SizeLabelWidths(self, left, right):
631 leftWidth = self.getLabelColumnWidth(left)
632 rightWidth = self.getLabelColumnWidth(right)
633 maxWidth = max(leftWidth, rightWidth)
634 self.setLabelColumnWidth(left, maxWidth)
635 self.setLabelColumnWidth(right, maxWidth)
638 # Make the size of the Notebook control the same size as this control
639 self.nb.SetSize(self.GetSize())
641 # Propegate the OnSize() event (just in case)
644 # Perform out resize magic
645 self.UpdateSize(self.printPanel)
646 self.UpdateSize(self.advancedPanel)
648 def UpdateSize(self, configPanel):
649 sizer = configPanel.GetSizer()
653 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
656 # if width(col1) > (best_width(col1) + best_width(col1)):
657 # switch to horizontal
660 col1 = configPanel.leftPanel
661 colSize1 = col1.GetSize()
662 colBestSize1 = col1.GetBestSize()
663 col2 = configPanel.rightPanel
664 colSize2 = col2.GetSize()
665 colBestSize2 = col2.GetBestSize()
667 orientation = sizer.GetOrientation()
669 if orientation == wx.HORIZONTAL:
670 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
672 sizer = wx.BoxSizer(wx.VERTICAL)
673 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
674 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
675 configPanel.SetSizer(sizer)
681 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
683 sizer = wx.BoxSizer(wx.HORIZONTAL)
684 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
685 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
686 configPanel.SetSizer(sizer)
692 def updateProfileToControls(self):
693 super(normalSettingsPanel, self).updateProfileToControls()
694 if self.alterationPanel is not None:
695 self.alterationPanel.updateProfileToControls()
696 self.pluginPanel.updateProfileToControls()