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 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions() + ['.g', '.gcode', '.ini']))
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()
112 i = toolsMenu.Append(-1, _("Switch to quickprint..."))
113 self.switchToQuickprintMenuItem = i
114 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
116 i = toolsMenu.Append(-1, _("Switch to full settings..."))
117 self.switchToNormalMenuItem = i
118 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
120 #toolsMenu.AppendSeparator()
121 #i = toolsMenu.Append(-1, 'Batch run...')
122 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
123 #self.normalModeOnlyItems.append(i)
125 if minecraftImport.hasMinecraft():
126 i = toolsMenu.Append(-1, _("Minecraft import..."))
127 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
129 if version.isDevVersion():
130 i = toolsMenu.Append(-1, _("PID Debugger..."))
131 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
133 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
134 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
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, _("Open expert settings..."))
145 self.normalModeOnlyItems.append(i)
146 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
147 expertMenu.AppendSeparator()
148 i = expertMenu.Append(-1, _("Run first run wizard..."))
149 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
150 i = expertMenu.Append(-1, _("Run bed leveling wizard..."))
151 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
152 if self.extruderCount > 1:
153 i = expertMenu.Append(-1, _("Run head offset wizard..."))
154 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
156 self.menubar.Append(expertMenu, _("Expert"))
159 i = helpMenu.Append(-1, _("Online documentation..."))
160 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
161 i = helpMenu.Append(-1, _("Report a problem..."))
162 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
163 i = helpMenu.Append(-1, _("Check for update..."))
164 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
165 i = helpMenu.Append(-1, _("Open YouMagine website..."))
166 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
167 i = helpMenu.Append(-1, _("About Cura..."))
168 self.Bind(wx.EVT_MENU, self.OnAbout, i)
169 self.menubar.Append(helpMenu, _("Help"))
170 self.SetMenuBar(self.menubar)
172 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
173 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
174 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
175 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
178 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
179 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
181 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
182 self.leftSizer.Add(self.simpleSettingsPanel, 1)
183 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
184 self.leftPane.SetSizer(self.leftSizer)
187 self.scene = sceneView.SceneView(self.rightPane)
189 #Main sizer, to position the preview window, buttons and tab control
190 sizer = wx.BoxSizer()
191 self.rightPane.SetSizer(sizer)
192 sizer.Add(self.scene, 1, flag=wx.EXPAND)
195 sizer = wx.BoxSizer(wx.VERTICAL)
197 sizer.Add(self.splitter, 1, wx.EXPAND)
201 self.updateProfileToAllControls()
203 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
205 self.simpleSettingsPanel.Show(False)
206 self.normalSettingsPanel.Show(False)
208 # Set default window size & position
209 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
212 #Timer set; used to check if profile is on the clipboard
213 self.timer = wx.Timer(self)
214 self.Bind(wx.EVT_TIMER, self.onTimer)
215 self.timer.Start(1000)
216 self.lastTriedClipboard = profile.getProfileString()
218 # Restore the window position, size & state from the preferences file
220 if profile.getPreference('window_maximized') == 'True':
223 posx = int(profile.getPreference('window_pos_x'))
224 posy = int(profile.getPreference('window_pos_y'))
225 width = int(profile.getPreference('window_width'))
226 height = int(profile.getPreference('window_height'))
227 if posx > 0 or posy > 0:
228 self.SetPosition((posx,posy))
229 if width > 0 and height > 0:
230 self.SetSize((width,height))
232 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
234 self.normalSashPos = 0
236 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
237 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
239 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
241 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
243 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
245 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
246 self.SetSize((800,600))
249 self.updateSliceMode()
251 def onTimer(self, e):
252 #Check if there is something in the clipboard
255 if not wx.TheClipboard.IsOpened():
256 wx.TheClipboard.Open()
257 do = wx.TextDataObject()
258 if wx.TheClipboard.GetData(do):
259 profileString = do.GetText()
260 wx.TheClipboard.Close()
262 if "CURA_PROFILE_STRING:" in profileString:
263 #print "Found correct syntax on clipboard"
264 profileString = profileString.replace("\n","")
265 profileString = profileString.replace("CURA_PROFILE_STRING:", "")
266 if profileString != self.lastTriedClipboard:
267 self.lastTriedClipboard = profileString
268 profile.setProfileFromString(profileString)
269 print "changed profile"
270 self.updateProfileToAllControls()
272 print "Unable to read from clipboard"
275 def updateSliceMode(self):
276 isSimple = profile.getPreference('startMode') == 'Simple'
278 self.normalSettingsPanel.Show(not isSimple)
279 self.simpleSettingsPanel.Show(isSimple)
280 self.leftPane.Layout()
282 for i in self.normalModeOnlyItems:
283 i.Enable(not isSimple)
284 self.switchToQuickprintMenuItem.Enable(not isSimple)
285 self.switchToNormalMenuItem.Enable(isSimple)
287 # Set splitter sash position & size
289 # Save normal mode sash
290 self.normalSashPos = self.splitter.GetSashPosition()
292 # Change location of sash to width of quick mode pane
293 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
294 self.splitter.SetSashPosition(width, True)
297 self.splitter.SetSashSize(0)
299 self.splitter.SetSashPosition(self.normalSashPos, True)
301 self.splitter.SetSashSize(4)
302 self.scene.updateProfileToControls()
304 def OnPreferences(self, e):
305 prefDialog = preferencesDialog.preferencesDialog(self)
309 def OnMachineSettings(self, e):
310 prefDialog = preferencesDialog.machineSettingsDialog(self)
314 def OnDropFiles(self, files):
316 profile.setPluginConfig([])
317 self.updateProfileToAllControls()
318 self.scene.loadFiles(files)
320 def OnModelMRU(self, e):
321 fileNum = e.GetId() - self.ID_MRU_MODEL1
322 path = self.modelFileHistory.GetHistoryFile(fileNum)
324 self.modelFileHistory.AddFileToHistory(path) # move up the list
325 self.config.SetPath("/ModelMRU")
326 self.modelFileHistory.Save(self.config)
329 profile.putPreference('lastFile', path)
331 self.scene.loadFiles(filelist)
333 def addToModelMRU(self, file):
334 self.modelFileHistory.AddFileToHistory(file)
335 self.config.SetPath("/ModelMRU")
336 self.modelFileHistory.Save(self.config)
339 def OnProfileMRU(self, e):
340 fileNum = e.GetId() - self.ID_MRU_PROFILE1
341 path = self.profileFileHistory.GetHistoryFile(fileNum)
343 self.profileFileHistory.AddFileToHistory(path) # move up the list
344 self.config.SetPath("/ProfileMRU")
345 self.profileFileHistory.Save(self.config)
348 profile.loadProfile(path)
349 self.updateProfileToAllControls()
351 def addToProfileMRU(self, file):
352 self.profileFileHistory.AddFileToHistory(file)
353 self.config.SetPath("/ProfileMRU")
354 self.profileFileHistory.Save(self.config)
357 def updateProfileToAllControls(self):
358 self.scene.updateProfileToControls()
359 self.normalSettingsPanel.updateProfileToControls()
360 self.simpleSettingsPanel.updateProfileToControls()
362 def reloadSettingPanels(self):
363 self.leftSizer.Detach(self.simpleSettingsPanel)
364 self.leftSizer.Detach(self.normalSettingsPanel)
365 self.simpleSettingsPanel.Destroy()
366 self.normalSettingsPanel.Destroy()
367 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
368 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
369 self.leftSizer.Add(self.simpleSettingsPanel, 1)
370 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
371 self.updateSliceMode()
372 self.updateProfileToAllControls()
374 def updateMachineMenu(self):
375 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
376 for item in self.machineMenu.GetMenuItems():
377 self.machineMenu.RemoveItem(item)
379 #Add a menu item for each machine configuration.
380 for n in xrange(0, profile.getMachineCount()):
381 i = self.machineMenu.Append(n, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
382 if n == int(profile.getPreferenceFloat('active_machine')):
384 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId()), i)
386 self.machineMenu.AppendSeparator()
387 i = self.machineMenu.Append(-1, _("Add new machine..."))
388 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
390 #Add tools for machines.
391 self.machineMenu.AppendSeparator()
392 i = self.machineMenu.Append(-1, _("Install custom firmware"))
393 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
394 i = self.machineMenu.Append(-1, _("Install default Marlin firmware"))
395 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
397 def OnLoadProfile(self, e):
398 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)
399 dlg.SetWildcard("ini files (*.ini)|*.ini")
400 if dlg.ShowModal() == wx.ID_OK:
401 profileFile = dlg.GetPath()
402 profile.loadProfile(profileFile)
403 self.updateProfileToAllControls()
405 # Update the Profile MRU
406 self.addToProfileMRU(profileFile)
409 def OnLoadProfileFromGcode(self, e):
410 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)
411 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
412 if dlg.ShowModal() == wx.ID_OK:
413 gcodeFile = dlg.GetPath()
414 f = open(gcodeFile, 'r')
417 if line.startswith(';CURA_PROFILE_STRING:'):
418 profile.setProfileFromString(line[line.find(':')+1:].strip())
421 self.updateProfileToAllControls()
423 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)
426 def OnSaveProfile(self, e):
427 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
428 dlg.SetWildcard("ini files (*.ini)|*.ini")
429 if dlg.ShowModal() == wx.ID_OK:
430 profileFile = dlg.GetPath()
431 profile.saveProfile(profileFile)
434 def OnResetProfile(self, e):
435 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)
436 result = dlg.ShowModal() == wx.ID_YES
439 profile.resetProfile()
440 self.updateProfileToAllControls()
442 def OnSimpleSwitch(self, e):
443 profile.putPreference('startMode', 'Simple')
444 self.updateSliceMode()
446 def OnNormalSwitch(self, e):
447 profile.putPreference('startMode', 'Normal')
448 self.updateSliceMode()
450 def OnDefaultMarlinFirmware(self, e):
451 firmwareInstall.InstallFirmware()
453 def OnCustomFirmware(self, e):
454 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
455 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)
456 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
457 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
458 if dlg.ShowModal() == wx.ID_OK:
459 filename = dlg.GetPath()
460 if not(os.path.exists(filename)):
462 #For some reason my Ubuntu 10.10 crashes here.
463 firmwareInstall.InstallFirmware(filename)
465 def OnFirstRunWizard(self, e):
467 configWizard.configWizard()
469 self.reloadSettingPanels()
471 def OnAddNewMachine(self, e):
473 profile.setActiveMachine(profile.getMachineCount())
474 configWizard.configWizard(True)
476 self.reloadSettingPanels()
477 self.updateMachineMenu()
479 def OnSelectMachine(self, index):
480 profile.setActiveMachine(index)
481 self.reloadSettingPanels()
483 def OnBedLevelWizard(self, e):
484 configWizard.bedLevelWizard()
486 def OnHeadOffsetWizard(self, e):
487 configWizard.headOffsetWizard()
489 def OnExpertOpen(self, e):
490 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
494 def OnMinecraftImport(self, e):
495 mi = minecraftImport.minecraftImportWindow(self)
499 def OnPIDDebugger(self, e):
500 debugger = pidDebugger.debuggerWindow(self)
504 def onCopyProfileClipboard(self, e):
506 if not wx.TheClipboard.IsOpened():
507 wx.TheClipboard.Open()
508 clipData = wx.TextDataObject()
509 self.lastTriedClipboard = profile.getProfileString()
510 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
511 clipData.SetText(profileString)
512 wx.TheClipboard.SetData(clipData)
513 wx.TheClipboard.Close()
515 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
517 def OnCheckForUpdate(self, e):
518 newVersion = version.checkForNewerVersion()
519 if newVersion is not None:
520 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:
521 webbrowser.open(newVersion)
523 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
525 def OnAbout(self, e):
526 info = wx.AboutDialogInfo()
528 info.SetDescription(_("End solution for Open Source Fused Filament Fabrication 3D printing."))
529 info.SetWebSite('http://software.ultimaker.com/')
530 info.SetCopyright(_("Copyright (C) David Braam"))
532 This program is free software: you can redistribute it and/or modify
533 it under the terms of the GNU Affero General Public License as published by
534 the Free Software Foundation, either version 3 of the License, or
535 (at your option) any later version.
537 This program is distributed in the hope that it will be useful,
538 but WITHOUT ANY WARRANTY; without even the implied warranty of
539 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
540 GNU Affero General Public License for more details.
542 You should have received a copy of the GNU Affero General Public License
543 along with this program. If not, see <http://www.gnu.org/licenses/>.
547 def OnClose(self, e):
548 profile.saveProfile(profile.getDefaultProfilePath())
550 # Save the window position, size & state from the preferences file
551 profile.putPreference('window_maximized', self.IsMaximized())
552 if not self.IsMaximized() and not self.IsIconized():
553 (posx, posy) = self.GetPosition()
554 profile.putPreference('window_pos_x', posx)
555 profile.putPreference('window_pos_y', posy)
556 (width, height) = self.GetSize()
557 profile.putPreference('window_width', width)
558 profile.putPreference('window_height', height)
560 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
561 isSimple = profile.getPreference('startMode') == 'Simple'
563 self.normalSashPos = self.splitter.GetSashPosition()
564 profile.putPreference('window_normal_sash', self.normalSashPos)
566 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
568 self.scene.OnPaint = lambda e : e
569 self.scene._slicer.cleanup()
575 class normalSettingsPanel(configBase.configPanelBase):
576 "Main user interface window"
577 def __init__(self, parent, callback = None):
578 super(normalSettingsPanel, self).__init__(parent, callback)
581 self.nb = wx.Notebook(self)
582 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
583 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
585 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
586 self._addSettingsToPanels('basic', left, right)
587 self.SizeLabelWidths(left, right)
589 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
590 self._addSettingsToPanels('advanced', left, right)
591 self.SizeLabelWidths(left, right)
594 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
595 if len(self.pluginPanel.pluginList) > 0:
596 self.nb.AddPage(self.pluginPanel, _("Plugins"))
598 self.pluginPanel.Show(False)
601 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
602 self.alterationPanel = None
604 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
605 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
607 self.Bind(wx.EVT_SIZE, self.OnSize)
609 self.nb.SetSize(self.GetSize())
610 self.UpdateSize(self.printPanel)
611 self.UpdateSize(self.advancedPanel)
613 def _addSettingsToPanels(self, category, left, right):
614 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
618 for title in profile.getSubCategoriesFor(category):
619 n += 1 + len(profile.getSettingsForCategory(category, title))
622 configBase.TitleRow(p, title)
623 for s in profile.getSettingsForCategory(category, title):
624 configBase.SettingRow(p, s.getName())
626 def SizeLabelWidths(self, left, right):
627 leftWidth = self.getLabelColumnWidth(left)
628 rightWidth = self.getLabelColumnWidth(right)
629 maxWidth = max(leftWidth, rightWidth)
630 self.setLabelColumnWidth(left, maxWidth)
631 self.setLabelColumnWidth(right, maxWidth)
634 # Make the size of the Notebook control the same size as this control
635 self.nb.SetSize(self.GetSize())
637 # Propegate the OnSize() event (just in case)
640 # Perform out resize magic
641 self.UpdateSize(self.printPanel)
642 self.UpdateSize(self.advancedPanel)
644 def UpdateSize(self, configPanel):
645 sizer = configPanel.GetSizer()
649 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
652 # if width(col1) > (best_width(col1) + best_width(col1)):
653 # switch to horizontal
656 col1 = configPanel.leftPanel
657 colSize1 = col1.GetSize()
658 colBestSize1 = col1.GetBestSize()
659 col2 = configPanel.rightPanel
660 colSize2 = col2.GetSize()
661 colBestSize2 = col2.GetBestSize()
663 orientation = sizer.GetOrientation()
665 if orientation == wx.HORIZONTAL:
666 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
668 sizer = wx.BoxSizer(wx.VERTICAL)
669 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
670 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
671 configPanel.SetSizer(sizer)
677 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
679 sizer = wx.BoxSizer(wx.HORIZONTAL)
680 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
681 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
682 configPanel.SetSizer(sizer)
688 def updateProfileToControls(self):
689 super(normalSettingsPanel, self).updateProfileToControls()
690 if self.alterationPanel is not None:
691 self.alterationPanel.updateProfileToControls()
692 self.pluginPanel.updateProfileToControls()