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.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 self.extruderCount = int(profile.getMachineSetting('extruder_amount'))
31 wx.EVT_CLOSE(self, self.OnClose)
33 # allow dropping any file, restrict later
34 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
36 self.normalModeOnlyItems = []
38 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
39 self.config = wx.FileConfig(appName="Cura",
40 localFilename=mruFile,
41 style=wx.CONFIG_USE_LOCAL_FILE)
43 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)]
44 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
45 self.config.SetPath("/ModelMRU")
46 self.modelFileHistory.Load(self.config)
48 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)]
49 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
50 self.config.SetPath("/ProfileMRU")
51 self.profileFileHistory.Load(self.config)
53 self.menubar = wx.MenuBar()
54 self.fileMenu = wx.Menu()
55 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
56 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
57 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
58 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
59 i = self.fileMenu.Append(-1, _("Clear platform"))
60 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
62 self.fileMenu.AppendSeparator()
63 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
64 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
65 i = self.fileMenu.Append(-1, _("Save GCode..."))
66 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
67 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
68 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
70 self.fileMenu.AppendSeparator()
71 i = self.fileMenu.Append(-1, _("Open Profile..."))
72 self.normalModeOnlyItems.append(i)
73 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
74 i = self.fileMenu.Append(-1, _("Save Profile..."))
75 self.normalModeOnlyItems.append(i)
76 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
77 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
78 self.normalModeOnlyItems.append(i)
79 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
80 self.fileMenu.AppendSeparator()
81 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
82 self.normalModeOnlyItems.append(i)
83 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
85 self.fileMenu.AppendSeparator()
86 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
87 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
88 i = self.fileMenu.Append(-1, _("Machine settings..."))
89 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
90 self.fileMenu.AppendSeparator()
93 modelHistoryMenu = wx.Menu()
94 self.fileMenu.AppendMenu(wx.NewId(), _("&Recent Model Files"), modelHistoryMenu)
95 self.modelFileHistory.UseMenu(modelHistoryMenu)
96 self.modelFileHistory.AddFilesToMenu()
97 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
100 profileHistoryMenu = wx.Menu()
101 self.fileMenu.AppendMenu(wx.NewId(), _("&Recent Profile Files"), profileHistoryMenu)
102 self.profileFileHistory.UseMenu(profileHistoryMenu)
103 self.profileFileHistory.AddFilesToMenu()
104 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
106 self.fileMenu.AppendSeparator()
107 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
108 self.Bind(wx.EVT_MENU, self.OnQuit, i)
109 self.menubar.Append(self.fileMenu, _("&File"))
111 toolsMenu = wx.Menu()
113 i = toolsMenu.Append(-1, _("Switch to quickprint..."))
114 self.switchToQuickprintMenuItem = i
115 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
117 i = toolsMenu.Append(-1, _("Switch to full settings..."))
118 self.switchToNormalMenuItem = i
119 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
121 #toolsMenu.AppendSeparator()
122 #i = toolsMenu.Append(-1, 'Batch run...')
123 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
124 #self.normalModeOnlyItems.append(i)
126 if minecraftImport.hasMinecraft():
127 i = toolsMenu.Append(-1, _("Minecraft import..."))
128 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
130 if version.isDevVersion():
131 i = toolsMenu.Append(-1, _("PID Debugger..."))
132 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
134 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
135 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
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, _("Open expert settings..."))
146 self.normalModeOnlyItems.append(i)
147 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
148 expertMenu.AppendSeparator()
149 i = expertMenu.Append(-1, _("Run first run wizard..."))
150 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
151 i = expertMenu.Append(-1, _("Run bed leveling wizard..."))
152 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
153 if self.extruderCount > 1:
154 i = expertMenu.Append(-1, _("Run head offset wizard..."))
155 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
157 self.menubar.Append(expertMenu, _("Expert"))
160 i = helpMenu.Append(-1, _("Online documentation..."))
161 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
162 i = helpMenu.Append(-1, _("Report a problem..."))
163 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
164 i = helpMenu.Append(-1, _("Check for update..."))
165 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
166 i = helpMenu.Append(-1, _("Open YouMagine website..."))
167 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
168 i = helpMenu.Append(-1, _("About Cura..."))
169 self.Bind(wx.EVT_MENU, self.OnAbout, i)
170 self.menubar.Append(helpMenu, _("Help"))
171 self.SetMenuBar(self.menubar)
173 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
174 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
175 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
176 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
179 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
180 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
182 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
183 self.leftSizer.Add(self.simpleSettingsPanel, 1)
184 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
185 self.leftPane.SetSizer(self.leftSizer)
188 self.scene = sceneView.SceneView(self.rightPane)
190 #Main sizer, to position the preview window, buttons and tab control
191 sizer = wx.BoxSizer()
192 self.rightPane.SetSizer(sizer)
193 sizer.Add(self.scene, 1, flag=wx.EXPAND)
196 sizer = wx.BoxSizer(wx.VERTICAL)
198 sizer.Add(self.splitter, 1, wx.EXPAND)
202 self.updateProfileToAllControls()
204 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
206 self.simpleSettingsPanel.Show(False)
207 self.normalSettingsPanel.Show(False)
209 # Set default window size & position
210 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
213 #Timer set; used to check if profile is on the clipboard
214 self.timer = wx.Timer(self)
215 self.Bind(wx.EVT_TIMER, self.onTimer)
216 self.timer.Start(1000)
217 self.lastTriedClipboard = profile.getProfileString()
219 # Restore the window position, size & state from the preferences file
221 if profile.getPreference('window_maximized') == 'True':
224 posx = int(profile.getPreference('window_pos_x'))
225 posy = int(profile.getPreference('window_pos_y'))
226 width = int(profile.getPreference('window_width'))
227 height = int(profile.getPreference('window_height'))
228 if posx > 0 or posy > 0:
229 self.SetPosition((posx,posy))
230 if width > 0 and height > 0:
231 self.SetSize((width,height))
233 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
235 self.normalSashPos = 0
237 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
238 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
240 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
242 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
244 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
246 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
247 self.SetSize((800,600))
250 self.updateSliceMode()
252 def onTimer(self, e):
253 #Check if there is something in the clipboard
256 if not wx.TheClipboard.IsOpened():
257 wx.TheClipboard.Open()
258 do = wx.TextDataObject()
259 if wx.TheClipboard.GetData(do):
260 profileString = do.GetText()
261 wx.TheClipboard.Close()
263 startTag = "CURA_PROFILE_STRING:"
264 if startTag in profileString:
265 #print "Found correct syntax on clipboard"
266 profileString = profileString.replace("\n","").strip()
267 profileString = profileString[profileString.find(startTag)+len(startTag):]
268 if profileString != self.lastTriedClipboard:
270 self.lastTriedClipboard = profileString
271 profile.setProfileFromString(profileString)
272 self.scene.notification.message("Loaded new profile from clipboard.")
273 self.updateProfileToAllControls()
275 print "Unable to read from clipboard"
278 def updateSliceMode(self):
279 isSimple = profile.getPreference('startMode') == 'Simple'
281 self.normalSettingsPanel.Show(not isSimple)
282 self.simpleSettingsPanel.Show(isSimple)
283 self.leftPane.Layout()
285 for i in self.normalModeOnlyItems:
286 i.Enable(not isSimple)
287 self.switchToQuickprintMenuItem.Enable(not isSimple)
288 self.switchToNormalMenuItem.Enable(isSimple)
290 # Set splitter sash position & size
292 # Save normal mode sash
293 self.normalSashPos = self.splitter.GetSashPosition()
295 # Change location of sash to width of quick mode pane
296 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
297 self.splitter.SetSashPosition(width, True)
300 self.splitter.SetSashSize(0)
302 self.splitter.SetSashPosition(self.normalSashPos, True)
304 self.splitter.SetSashSize(4)
305 self.scene.updateProfileToControls()
307 def OnPreferences(self, e):
308 prefDialog = preferencesDialog.preferencesDialog(self)
312 def OnMachineSettings(self, e):
313 prefDialog = preferencesDialog.machineSettingsDialog(self)
317 def OnDropFiles(self, files):
319 profile.setPluginConfig([])
320 self.updateProfileToAllControls()
321 self.scene.loadFiles(files)
323 def OnModelMRU(self, e):
324 fileNum = e.GetId() - self.ID_MRU_MODEL1
325 path = self.modelFileHistory.GetHistoryFile(fileNum)
327 self.modelFileHistory.AddFileToHistory(path) # move up the list
328 self.config.SetPath("/ModelMRU")
329 self.modelFileHistory.Save(self.config)
332 profile.putPreference('lastFile', path)
334 self.scene.loadFiles(filelist)
336 def addToModelMRU(self, file):
337 self.modelFileHistory.AddFileToHistory(file)
338 self.config.SetPath("/ModelMRU")
339 self.modelFileHistory.Save(self.config)
342 def OnProfileMRU(self, e):
343 fileNum = e.GetId() - self.ID_MRU_PROFILE1
344 path = self.profileFileHistory.GetHistoryFile(fileNum)
346 self.profileFileHistory.AddFileToHistory(path) # move up the list
347 self.config.SetPath("/ProfileMRU")
348 self.profileFileHistory.Save(self.config)
351 profile.loadProfile(path)
352 self.updateProfileToAllControls()
354 def addToProfileMRU(self, file):
355 self.profileFileHistory.AddFileToHistory(file)
356 self.config.SetPath("/ProfileMRU")
357 self.profileFileHistory.Save(self.config)
360 def updateProfileToAllControls(self):
361 self.scene.updateProfileToControls()
362 self.normalSettingsPanel.updateProfileToControls()
363 self.simpleSettingsPanel.updateProfileToControls()
365 def reloadSettingPanels(self):
366 self.leftSizer.Detach(self.simpleSettingsPanel)
367 self.leftSizer.Detach(self.normalSettingsPanel)
368 self.simpleSettingsPanel.Destroy()
369 self.normalSettingsPanel.Destroy()
370 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
371 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
372 self.leftSizer.Add(self.simpleSettingsPanel, 1)
373 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
374 self.updateSliceMode()
375 self.updateProfileToAllControls()
377 def updateMachineMenu(self):
378 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
379 for item in self.machineMenu.GetMenuItems():
380 self.machineMenu.RemoveItem(item)
382 #Add a menu item for each machine configuration.
383 for n in xrange(0, profile.getMachineCount()):
384 i = self.machineMenu.Append(n, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
385 if n == int(profile.getPreferenceFloat('active_machine')):
387 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId()), i)
389 self.machineMenu.AppendSeparator()
390 i = self.machineMenu.Append(-1, _("Add new machine..."))
391 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
393 #Add tools for machines.
394 self.machineMenu.AppendSeparator()
395 i = self.machineMenu.Append(-1, _("Install custom firmware"))
396 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
397 i = self.machineMenu.Append(-1, _("Install default Marlin firmware"))
398 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
400 def OnLoadProfile(self, e):
401 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)
402 dlg.SetWildcard("ini files (*.ini)|*.ini")
403 if dlg.ShowModal() == wx.ID_OK:
404 profileFile = dlg.GetPath()
405 profile.loadProfile(profileFile)
406 self.updateProfileToAllControls()
408 # Update the Profile MRU
409 self.addToProfileMRU(profileFile)
412 def OnLoadProfileFromGcode(self, e):
413 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)
414 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
415 if dlg.ShowModal() == wx.ID_OK:
416 gcodeFile = dlg.GetPath()
417 f = open(gcodeFile, 'r')
420 if line.startswith(';CURA_PROFILE_STRING:'):
421 profile.setProfileFromString(line[line.find(':')+1:].strip())
424 self.updateProfileToAllControls()
426 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)
429 def OnSaveProfile(self, e):
430 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
431 dlg.SetWildcard("ini files (*.ini)|*.ini")
432 if dlg.ShowModal() == wx.ID_OK:
433 profileFile = dlg.GetPath()
434 profile.saveProfile(profileFile)
437 def OnResetProfile(self, e):
438 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)
439 result = dlg.ShowModal() == wx.ID_YES
442 profile.resetProfile()
443 self.updateProfileToAllControls()
445 def OnSimpleSwitch(self, e):
446 profile.putPreference('startMode', 'Simple')
447 self.updateSliceMode()
449 def OnNormalSwitch(self, e):
450 profile.putPreference('startMode', 'Normal')
451 self.updateSliceMode()
453 def OnDefaultMarlinFirmware(self, e):
454 firmwareInstall.InstallFirmware()
456 def OnCustomFirmware(self, e):
457 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
458 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)
459 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
460 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
461 if dlg.ShowModal() == wx.ID_OK:
462 filename = dlg.GetPath()
463 if not(os.path.exists(filename)):
465 #For some reason my Ubuntu 10.10 crashes here.
466 firmwareInstall.InstallFirmware(filename)
468 def OnFirstRunWizard(self, e):
470 configWizard.configWizard()
472 self.reloadSettingPanels()
474 def OnAddNewMachine(self, e):
476 profile.setActiveMachine(profile.getMachineCount())
477 configWizard.configWizard(True)
479 self.reloadSettingPanels()
480 self.updateMachineMenu()
482 def OnSelectMachine(self, index):
483 profile.setActiveMachine(index)
484 self.reloadSettingPanels()
486 def OnBedLevelWizard(self, e):
487 configWizard.bedLevelWizard()
489 def OnHeadOffsetWizard(self, e):
490 configWizard.headOffsetWizard()
492 def OnExpertOpen(self, e):
493 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
497 def OnMinecraftImport(self, e):
498 mi = minecraftImport.minecraftImportWindow(self)
502 def OnPIDDebugger(self, e):
503 debugger = pidDebugger.debuggerWindow(self)
507 def onCopyProfileClipboard(self, e):
509 if not wx.TheClipboard.IsOpened():
510 wx.TheClipboard.Open()
511 clipData = wx.TextDataObject()
512 self.lastTriedClipboard = profile.getProfileString()
513 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
514 clipData.SetText(profileString)
515 wx.TheClipboard.SetData(clipData)
516 wx.TheClipboard.Close()
518 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
520 def OnCheckForUpdate(self, e):
521 newVersion = version.checkForNewerVersion()
522 if newVersion is not None:
523 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:
524 webbrowser.open(newVersion)
526 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
528 def OnAbout(self, e):
529 info = wx.AboutDialogInfo()
531 info.SetDescription("""
532 End solution for Open Source Fused Filament Fabrication 3D printing.
533 * Cura is the graphical User Interface.
534 * CuraEngine is the slicer/gcode generator.
535 Cura and the CuraEngine are licensed AGPLv3.
537 info.SetWebSite('http://software.ultimaker.com/')
538 info.SetCopyright(_("Copyright (C) David Braam"))
540 This program is free software: you can redistribute it and/or modify
541 it under the terms of the GNU Affero General Public License as published by
542 the Free Software Foundation, either version 3 of the License, or
543 (at your option) any later version.
545 This program is distributed in the hope that it will be useful,
546 but WITHOUT ANY WARRANTY; without even the implied warranty of
547 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
548 GNU Affero General Public License for more details.
550 You should have received a copy of the GNU Affero General Public License
551 along with this program. If not, see <http://www.gnu.org/licenses/>.
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 if len(self.pluginPanel.pluginList) > 0:
604 self.nb.AddPage(self.pluginPanel, _("Plugins"))
606 self.pluginPanel.Show(False)
609 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
610 self.alterationPanel = None
612 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
613 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
615 self.Bind(wx.EVT_SIZE, self.OnSize)
617 self.nb.SetSize(self.GetSize())
618 self.UpdateSize(self.printPanel)
619 self.UpdateSize(self.advancedPanel)
621 def _addSettingsToPanels(self, category, left, right):
622 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
626 for title in profile.getSubCategoriesFor(category):
627 n += 1 + len(profile.getSettingsForCategory(category, title))
630 configBase.TitleRow(p, title)
631 for s in profile.getSettingsForCategory(category, title):
632 configBase.SettingRow(p, s.getName())
634 def SizeLabelWidths(self, left, right):
635 leftWidth = self.getLabelColumnWidth(left)
636 rightWidth = self.getLabelColumnWidth(right)
637 maxWidth = max(leftWidth, rightWidth)
638 self.setLabelColumnWidth(left, maxWidth)
639 self.setLabelColumnWidth(right, maxWidth)
642 # Make the size of the Notebook control the same size as this control
643 self.nb.SetSize(self.GetSize())
645 # Propegate the OnSize() event (just in case)
648 # Perform out resize magic
649 self.UpdateSize(self.printPanel)
650 self.UpdateSize(self.advancedPanel)
652 def UpdateSize(self, configPanel):
653 sizer = configPanel.GetSizer()
657 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
660 # if width(col1) > (best_width(col1) + best_width(col1)):
661 # switch to horizontal
664 col1 = configPanel.leftPanel
665 colSize1 = col1.GetSize()
666 colBestSize1 = col1.GetBestSize()
667 col2 = configPanel.rightPanel
668 colSize2 = col2.GetSize()
669 colBestSize2 = col2.GetBestSize()
671 orientation = sizer.GetOrientation()
673 if orientation == wx.HORIZONTAL:
674 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
676 sizer = wx.BoxSizer(wx.VERTICAL)
677 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
678 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
679 configPanel.SetSizer(sizer)
685 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
687 sizer = wx.BoxSizer(wx.HORIZONTAL)
688 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
689 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
690 configPanel.SetSizer(sizer)
696 def updateProfileToControls(self):
697 super(normalSettingsPanel, self).updateProfileToControls()
698 if self.alterationPanel is not None:
699 self.alterationPanel.updateProfileToControls()
700 self.pluginPanel.updateProfileToControls()