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), 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 #Add tools for machines.
387 self.machineMenu.AppendSeparator()
388 i = self.machineMenu.Append(-1, _("Install custom firmware"))
389 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
391 i = self.machineMenu.Append(-1, _("Install default Marlin firmware"))
392 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
394 self.machineMenu.AppendSeparator()
395 i = self.machineMenu.Append(-1, _("Add new machine..."))
396 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
398 def OnLoadProfile(self, e):
399 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)
400 dlg.SetWildcard("ini files (*.ini)|*.ini")
401 if dlg.ShowModal() == wx.ID_OK:
402 profileFile = dlg.GetPath()
403 profile.loadProfile(profileFile)
404 self.updateProfileToAllControls()
406 # Update the Profile MRU
407 self.addToProfileMRU(profileFile)
410 def OnLoadProfileFromGcode(self, e):
411 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)
412 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
413 if dlg.ShowModal() == wx.ID_OK:
414 gcodeFile = dlg.GetPath()
415 f = open(gcodeFile, 'r')
418 if line.startswith(';CURA_PROFILE_STRING:'):
419 profile.setProfileFromString(line[line.find(':')+1:].strip())
422 self.updateProfileToAllControls()
424 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)
427 def OnSaveProfile(self, e):
428 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
429 dlg.SetWildcard("ini files (*.ini)|*.ini")
430 if dlg.ShowModal() == wx.ID_OK:
431 profileFile = dlg.GetPath()
432 profile.saveProfile(profileFile)
435 def OnResetProfile(self, e):
436 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)
437 result = dlg.ShowModal() == wx.ID_YES
440 profile.resetProfile()
441 self.updateProfileToAllControls()
443 def OnSimpleSwitch(self, e):
444 profile.putPreference('startMode', 'Simple')
445 self.updateSliceMode()
447 def OnNormalSwitch(self, e):
448 profile.putPreference('startMode', 'Normal')
449 self.updateSliceMode()
451 def OnDefaultMarlinFirmware(self, e):
452 firmwareInstall.InstallFirmware()
454 def OnCustomFirmware(self, e):
455 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
456 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)
457 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
458 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
459 if dlg.ShowModal() == wx.ID_OK:
460 filename = dlg.GetPath()
461 if not(os.path.exists(filename)):
463 #For some reason my Ubuntu 10.10 crashes here.
464 firmwareInstall.InstallFirmware(filename)
466 def OnFirstRunWizard(self, e):
468 configWizard.configWizard()
470 self.reloadSettingPanels()
472 def OnAddNewMachine(self, e):
474 profile.setActiveMachine(profile.getMachineCount())
475 configWizard.configWizard(True)
477 self.reloadSettingPanels()
478 self.updateMachineMenu()
480 def OnSelectMachine(self, index):
481 profile.setActiveMachine(index)
482 self.reloadSettingPanels()
484 def OnBedLevelWizard(self, e):
485 configWizard.bedLevelWizard()
487 def OnHeadOffsetWizard(self, e):
488 configWizard.headOffsetWizard()
490 def OnExpertOpen(self, e):
491 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
495 def OnMinecraftImport(self, e):
496 mi = minecraftImport.minecraftImportWindow(self)
500 def OnPIDDebugger(self, e):
501 debugger = pidDebugger.debuggerWindow(self)
505 def onCopyProfileClipboard(self, e):
507 if not wx.TheClipboard.IsOpened():
508 wx.TheClipboard.Open()
509 clipData = wx.TextDataObject()
510 self.lastTriedClipboard = profile.getProfileString()
511 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
512 clipData.SetText(profileString)
513 wx.TheClipboard.SetData(clipData)
514 wx.TheClipboard.Close()
516 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
518 def OnCheckForUpdate(self, e):
519 newVersion = version.checkForNewerVersion()
520 if newVersion is not None:
521 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:
522 webbrowser.open(newVersion)
524 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
526 def OnAbout(self, e):
527 info = wx.AboutDialogInfo()
529 info.SetDescription(_("End solution for Open Source Fused Filament Fabrication 3D printing."))
530 info.SetWebSite('http://software.ultimaker.com/')
531 info.SetCopyright(_("Copyright (C) David Braam"))
533 This program is free software: you can redistribute it and/or modify
534 it under the terms of the GNU Affero General Public License as published by
535 the Free Software Foundation, either version 3 of the License, or
536 (at your option) any later version.
538 This program is distributed in the hope that it will be useful,
539 but WITHOUT ANY WARRANTY; without even the implied warranty of
540 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
541 GNU Affero General Public License for more details.
543 You should have received a copy of the GNU Affero General Public License
544 along with this program. If not, see <http://www.gnu.org/licenses/>.
548 def OnClose(self, e):
549 profile.saveProfile(profile.getDefaultProfilePath())
551 # Save the window position, size & state from the preferences file
552 profile.putPreference('window_maximized', self.IsMaximized())
553 if not self.IsMaximized() and not self.IsIconized():
554 (posx, posy) = self.GetPosition()
555 profile.putPreference('window_pos_x', posx)
556 profile.putPreference('window_pos_y', posy)
557 (width, height) = self.GetSize()
558 profile.putPreference('window_width', width)
559 profile.putPreference('window_height', height)
561 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
562 isSimple = profile.getPreference('startMode') == 'Simple'
564 self.normalSashPos = self.splitter.GetSashPosition()
565 profile.putPreference('window_normal_sash', self.normalSashPos)
567 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
569 self.scene.OnPaint = lambda e : e
570 self.scene._slicer.cleanup()
576 class normalSettingsPanel(configBase.configPanelBase):
577 "Main user interface window"
578 def __init__(self, parent, callback = None):
579 super(normalSettingsPanel, self).__init__(parent, callback)
582 self.nb = wx.Notebook(self)
583 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
584 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
586 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
587 self._addSettingsToPanels('basic', left, right)
588 self.SizeLabelWidths(left, right)
590 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
591 self._addSettingsToPanels('advanced', left, right)
592 self.SizeLabelWidths(left, right)
595 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
596 if len(self.pluginPanel.pluginList) > 0:
597 self.nb.AddPage(self.pluginPanel, _("Plugins"))
599 self.pluginPanel.Show(False)
602 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
603 self.alterationPanel = None
605 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
606 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
608 self.Bind(wx.EVT_SIZE, self.OnSize)
610 self.nb.SetSize(self.GetSize())
611 self.UpdateSize(self.printPanel)
612 self.UpdateSize(self.advancedPanel)
614 def _addSettingsToPanels(self, category, left, right):
615 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
619 for title in profile.getSubCategoriesFor(category):
620 n += 1 + len(profile.getSettingsForCategory(category, title))
623 configBase.TitleRow(p, title)
624 for s in profile.getSettingsForCategory(category, title):
625 configBase.SettingRow(p, s.getName())
627 def SizeLabelWidths(self, left, right):
628 leftWidth = self.getLabelColumnWidth(left)
629 rightWidth = self.getLabelColumnWidth(right)
630 maxWidth = max(leftWidth, rightWidth)
631 self.setLabelColumnWidth(left, maxWidth)
632 self.setLabelColumnWidth(right, maxWidth)
635 # Make the size of the Notebook control the same size as this control
636 self.nb.SetSize(self.GetSize())
638 # Propegate the OnSize() event (just in case)
641 # Perform out resize magic
642 self.UpdateSize(self.printPanel)
643 self.UpdateSize(self.advancedPanel)
645 def UpdateSize(self, configPanel):
646 sizer = configPanel.GetSizer()
650 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
653 # if width(col1) > (best_width(col1) + best_width(col1)):
654 # switch to horizontal
657 col1 = configPanel.leftPanel
658 colSize1 = col1.GetSize()
659 colBestSize1 = col1.GetBestSize()
660 col2 = configPanel.rightPanel
661 colSize2 = col2.GetSize()
662 colBestSize2 = col2.GetBestSize()
664 orientation = sizer.GetOrientation()
666 if orientation == wx.HORIZONTAL:
667 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
669 sizer = wx.BoxSizer(wx.VERTICAL)
670 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
671 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
672 configPanel.SetSizer(sizer)
678 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
680 sizer = wx.BoxSizer(wx.HORIZONTAL)
681 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
682 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
683 configPanel.SetSizer(sizer)
689 def updateProfileToControls(self):
690 super(normalSettingsPanel, self).updateProfileToControls()
691 if self.alterationPanel is not None:
692 self.alterationPanel.updateProfileToControls()
693 self.pluginPanel.updateProfileToControls()