1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
8 from Cura.gui import configBase
9 from Cura.gui import expertConfig
10 from Cura.gui import alterationPanel
11 from Cura.gui import pluginPanel
12 from Cura.gui import preferencesDialog
13 from Cura.gui import configWizard
14 from Cura.gui import firmwareInstall
15 from Cura.gui import simpleMode
16 from Cura.gui import sceneView
17 from Cura.gui import aboutWindow
18 from Cura.gui.util import dropTarget
19 #from Cura.gui.tools import batchRun
20 from Cura.gui.tools import pidDebugger
21 from Cura.gui.tools import minecraftImport
22 from Cura.util import profile
23 from Cura.util import version
24 from Cura.util import meshLoader
26 class mainWindow(wx.Frame):
28 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
30 wx.EVT_CLOSE(self, self.OnClose)
32 # allow dropping any file, restrict later
33 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
35 self.normalModeOnlyItems = []
37 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
38 self.config = wx.FileConfig(appName="Cura",
39 localFilename=mruFile,
40 style=wx.CONFIG_USE_LOCAL_FILE)
42 self.ID_MRU_MODEL1, self.ID_MRU_MODEL2, self.ID_MRU_MODEL3, self.ID_MRU_MODEL4, self.ID_MRU_MODEL5, self.ID_MRU_MODEL6, self.ID_MRU_MODEL7, self.ID_MRU_MODEL8, self.ID_MRU_MODEL9, self.ID_MRU_MODEL10 = [wx.NewId() for line in xrange(10)]
43 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
44 self.config.SetPath("/ModelMRU")
45 self.modelFileHistory.Load(self.config)
47 self.ID_MRU_PROFILE1, self.ID_MRU_PROFILE2, self.ID_MRU_PROFILE3, self.ID_MRU_PROFILE4, self.ID_MRU_PROFILE5, self.ID_MRU_PROFILE6, self.ID_MRU_PROFILE7, self.ID_MRU_PROFILE8, self.ID_MRU_PROFILE9, self.ID_MRU_PROFILE10 = [wx.NewId() for line in xrange(10)]
48 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
49 self.config.SetPath("/ProfileMRU")
50 self.profileFileHistory.Load(self.config)
52 self.menubar = wx.MenuBar()
53 self.fileMenu = wx.Menu()
54 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
55 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
56 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
57 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
58 i = self.fileMenu.Append(-1, _("Clear platform"))
59 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
61 self.fileMenu.AppendSeparator()
62 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
63 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
64 i = self.fileMenu.Append(-1, _("Save GCode..."))
65 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
66 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
67 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
69 self.fileMenu.AppendSeparator()
70 i = self.fileMenu.Append(-1, _("Open Profile..."))
71 self.normalModeOnlyItems.append(i)
72 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
73 i = self.fileMenu.Append(-1, _("Save Profile..."))
74 self.normalModeOnlyItems.append(i)
75 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
76 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
77 self.normalModeOnlyItems.append(i)
78 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
79 self.fileMenu.AppendSeparator()
80 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
81 self.normalModeOnlyItems.append(i)
82 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
84 self.fileMenu.AppendSeparator()
85 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
86 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
87 i = self.fileMenu.Append(-1, _("Machine settings..."))
88 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
89 self.fileMenu.AppendSeparator()
92 modelHistoryMenu = wx.Menu()
93 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
94 self.modelFileHistory.UseMenu(modelHistoryMenu)
95 self.modelFileHistory.AddFilesToMenu()
96 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
99 profileHistoryMenu = wx.Menu()
100 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
101 self.profileFileHistory.UseMenu(profileHistoryMenu)
102 self.profileFileHistory.AddFilesToMenu()
103 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
105 self.fileMenu.AppendSeparator()
106 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
107 self.Bind(wx.EVT_MENU, self.OnQuit, i)
108 self.menubar.Append(self.fileMenu, '&' + _("File"))
110 toolsMenu = wx.Menu()
111 #i = toolsMenu.Append(-1, 'Batch run...')
112 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
113 #self.normalModeOnlyItems.append(i)
115 if minecraftImport.hasMinecraft():
116 i = toolsMenu.Append(-1, _("Minecraft map import..."))
117 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
119 if version.isDevVersion():
120 i = toolsMenu.Append(-1, _("PID Debugger..."))
121 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
123 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
124 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
125 self.menubar.Append(toolsMenu, _("Tools"))
127 #Machine menu for machine configuration/tooling
128 self.machineMenu = wx.Menu()
129 self.updateMachineMenu()
131 self.menubar.Append(self.machineMenu, _("Machine"))
133 expertMenu = wx.Menu()
134 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
135 self.switchToQuickprintMenuItem = i
136 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
138 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
139 self.switchToNormalMenuItem = i
140 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
141 expertMenu.AppendSeparator()
143 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
144 self.normalModeOnlyItems.append(i)
145 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
146 expertMenu.AppendSeparator()
147 i = expertMenu.Append(-1, _("Run first run wizard..."))
148 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
149 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
150 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
151 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
152 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
154 self.menubar.Append(expertMenu, _("Expert"))
157 i = helpMenu.Append(-1, _("Online documentation..."))
158 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
159 i = helpMenu.Append(-1, _("Report a problem..."))
160 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
161 i = helpMenu.Append(-1, _("Check for update..."))
162 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
163 i = helpMenu.Append(-1, _("Open YouMagine website..."))
164 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
165 i = helpMenu.Append(-1, _("About Cura..."))
166 self.Bind(wx.EVT_MENU, self.OnAbout, i)
167 self.menubar.Append(helpMenu, _("Help"))
168 self.SetMenuBar(self.menubar)
170 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
171 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
172 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
173 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
176 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
177 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
179 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
180 self.leftSizer.Add(self.simpleSettingsPanel, 1)
181 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
182 self.leftPane.SetSizer(self.leftSizer)
185 self.scene = sceneView.SceneView(self.rightPane)
187 #Main sizer, to position the preview window, buttons and tab control
188 sizer = wx.BoxSizer()
189 self.rightPane.SetSizer(sizer)
190 sizer.Add(self.scene, 1, flag=wx.EXPAND)
193 sizer = wx.BoxSizer(wx.VERTICAL)
195 sizer.Add(self.splitter, 1, wx.EXPAND)
199 self.updateProfileToAllControls()
201 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
203 self.simpleSettingsPanel.Show(False)
204 self.normalSettingsPanel.Show(False)
206 # Set default window size & position
207 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
210 #Timer set; used to check if profile is on the clipboard
211 self.timer = wx.Timer(self)
212 self.Bind(wx.EVT_TIMER, self.onTimer)
213 self.timer.Start(1000)
214 self.lastTriedClipboard = profile.getProfileString()
216 # Restore the window position, size & state from the preferences file
218 if profile.getPreference('window_maximized') == 'True':
221 posx = int(profile.getPreference('window_pos_x'))
222 posy = int(profile.getPreference('window_pos_y'))
223 width = int(profile.getPreference('window_width'))
224 height = int(profile.getPreference('window_height'))
225 if posx > 0 or posy > 0:
226 self.SetPosition((posx,posy))
227 if width > 0 and height > 0:
228 self.SetSize((width,height))
230 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
232 self.normalSashPos = 0
234 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
235 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
237 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
239 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
241 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
243 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
244 self.SetSize((800,600))
247 self.updateSliceMode()
248 self.scene.SetFocus()
250 def onTimer(self, e):
251 #Check if there is something in the clipboard
254 if not wx.TheClipboard.IsOpened():
255 if not wx.TheClipboard.Open():
257 do = wx.TextDataObject()
258 if wx.TheClipboard.GetData(do):
259 profileString = do.GetText()
260 wx.TheClipboard.Close()
262 startTag = "CURA_PROFILE_STRING:"
263 if startTag in profileString:
264 #print "Found correct syntax on clipboard"
265 profileString = profileString.replace("\n","").strip()
266 profileString = profileString[profileString.find(startTag)+len(startTag):]
267 if profileString != self.lastTriedClipboard:
269 self.lastTriedClipboard = profileString
270 profile.setProfileFromString(profileString)
271 self.scene.notification.message("Loaded new profile from clipboard.")
272 self.updateProfileToAllControls()
274 print "Unable to read from clipboard"
277 def updateSliceMode(self):
278 isSimple = profile.getPreference('startMode') == 'Simple'
280 self.normalSettingsPanel.Show(not isSimple)
281 self.simpleSettingsPanel.Show(isSimple)
282 self.leftPane.Layout()
284 for i in self.normalModeOnlyItems:
285 i.Enable(not isSimple)
287 self.switchToQuickprintMenuItem.Check()
289 self.switchToNormalMenuItem.Check()
291 # Set splitter sash position & size
293 # Save normal mode sash
294 self.normalSashPos = self.splitter.GetSashPosition()
296 # Change location of sash to width of quick mode pane
297 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
298 self.splitter.SetSashPosition(width, True)
301 self.splitter.SetSashSize(0)
303 self.splitter.SetSashPosition(self.normalSashPos, True)
305 self.splitter.SetSashSize(4)
306 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
307 if profile.getMachineSetting('machine_type') == 'ultimaker2':
308 self.bedLevelWizardMenuItem.Enable(False)
309 self.headOffsetWizardMenuItem.Enable(False)
310 if int(profile.getMachineSetting('extruder_amount')) < 2:
311 self.headOffsetWizardMenuItem.Enable(False)
312 self.scene.updateProfileToControls()
314 def OnPreferences(self, e):
315 prefDialog = preferencesDialog.preferencesDialog(self)
319 wx.CallAfter(prefDialog.Show)
321 def OnMachineSettings(self, e):
322 prefDialog = preferencesDialog.machineSettingsDialog(self)
327 def OnDropFiles(self, files):
329 profile.setPluginConfig([])
330 self.updateProfileToAllControls()
331 self.scene.loadFiles(files)
333 def OnModelMRU(self, e):
334 fileNum = e.GetId() - self.ID_MRU_MODEL1
335 path = self.modelFileHistory.GetHistoryFile(fileNum)
337 self.modelFileHistory.AddFileToHistory(path) # move up the list
338 self.config.SetPath("/ModelMRU")
339 self.modelFileHistory.Save(self.config)
342 profile.putPreference('lastFile', path)
344 self.scene.loadFiles(filelist)
346 def addToModelMRU(self, file):
347 self.modelFileHistory.AddFileToHistory(file)
348 self.config.SetPath("/ModelMRU")
349 self.modelFileHistory.Save(self.config)
352 def OnProfileMRU(self, e):
353 fileNum = e.GetId() - self.ID_MRU_PROFILE1
354 path = self.profileFileHistory.GetHistoryFile(fileNum)
356 self.profileFileHistory.AddFileToHistory(path) # move up the list
357 self.config.SetPath("/ProfileMRU")
358 self.profileFileHistory.Save(self.config)
361 profile.loadProfile(path)
362 self.updateProfileToAllControls()
364 def addToProfileMRU(self, file):
365 self.profileFileHistory.AddFileToHistory(file)
366 self.config.SetPath("/ProfileMRU")
367 self.profileFileHistory.Save(self.config)
370 def updateProfileToAllControls(self):
371 self.scene.updateProfileToControls()
372 self.normalSettingsPanel.updateProfileToControls()
373 self.simpleSettingsPanel.updateProfileToControls()
375 def reloadSettingPanels(self):
376 self.leftSizer.Detach(self.simpleSettingsPanel)
377 self.leftSizer.Detach(self.normalSettingsPanel)
378 self.simpleSettingsPanel.Destroy()
379 self.normalSettingsPanel.Destroy()
380 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
381 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
382 self.leftSizer.Add(self.simpleSettingsPanel, 1)
383 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
384 self.updateSliceMode()
385 self.updateProfileToAllControls()
387 def updateMachineMenu(self):
388 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
389 for item in self.machineMenu.GetMenuItems():
390 self.machineMenu.RemoveItem(item)
392 #Add a menu item for each machine configuration.
393 for n in xrange(0, profile.getMachineCount()):
394 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
395 if n == int(profile.getPreferenceFloat('active_machine')):
397 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
399 self.machineMenu.AppendSeparator()
401 i = self.machineMenu.Append(-1, _("Machine settings..."))
402 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
404 #Add tools for machines.
405 self.machineMenu.AppendSeparator()
407 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
408 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
410 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
411 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
413 def OnLoadProfile(self, e):
414 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)
415 dlg.SetWildcard("ini files (*.ini)|*.ini")
416 if dlg.ShowModal() == wx.ID_OK:
417 profileFile = dlg.GetPath()
418 profile.loadProfile(profileFile)
419 self.updateProfileToAllControls()
421 # Update the Profile MRU
422 self.addToProfileMRU(profileFile)
425 def OnLoadProfileFromGcode(self, e):
426 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)
427 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
428 if dlg.ShowModal() == wx.ID_OK:
429 gcodeFile = dlg.GetPath()
430 f = open(gcodeFile, 'r')
433 if line.startswith(';CURA_PROFILE_STRING:'):
434 profile.setProfileFromString(line[line.find(':')+1:].strip())
437 self.updateProfileToAllControls()
439 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)
442 def OnSaveProfile(self, e):
443 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
444 dlg.SetWildcard("ini files (*.ini)|*.ini")
445 if dlg.ShowModal() == wx.ID_OK:
446 profileFile = dlg.GetPath()
447 profile.saveProfile(profileFile)
450 def OnResetProfile(self, e):
451 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)
452 result = dlg.ShowModal() == wx.ID_YES
455 profile.resetProfile()
456 self.updateProfileToAllControls()
458 def OnSimpleSwitch(self, e):
459 profile.putPreference('startMode', 'Simple')
460 self.updateSliceMode()
462 def OnNormalSwitch(self, e):
463 profile.putPreference('startMode', 'Normal')
464 self.updateSliceMode()
466 def OnDefaultMarlinFirmware(self, e):
467 firmwareInstall.InstallFirmware()
469 def OnCustomFirmware(self, e):
470 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
471 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)
472 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
473 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
474 if dlg.ShowModal() == wx.ID_OK:
475 filename = dlg.GetPath()
476 if not(os.path.exists(filename)):
478 #For some reason my Ubuntu 10.10 crashes here.
479 firmwareInstall.InstallFirmware(filename)
481 def OnFirstRunWizard(self, e):
483 configWizard.configWizard()
485 self.reloadSettingPanels()
487 def OnSelectMachine(self, index):
488 profile.setActiveMachine(index)
489 self.reloadSettingPanels()
491 def OnBedLevelWizard(self, e):
492 configWizard.bedLevelWizard()
494 def OnHeadOffsetWizard(self, e):
495 configWizard.headOffsetWizard()
497 def OnExpertOpen(self, e):
498 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
502 def OnMinecraftImport(self, e):
503 mi = minecraftImport.minecraftImportWindow(self)
507 def OnPIDDebugger(self, e):
508 debugger = pidDebugger.debuggerWindow(self)
512 def onCopyProfileClipboard(self, e):
514 if not wx.TheClipboard.IsOpened():
515 wx.TheClipboard.Open()
516 clipData = wx.TextDataObject()
517 self.lastTriedClipboard = profile.getProfileString()
518 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
519 clipData.SetText(profileString)
520 wx.TheClipboard.SetData(clipData)
521 wx.TheClipboard.Close()
523 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
525 def OnCheckForUpdate(self, e):
526 newVersion = version.checkForNewerVersion()
527 if newVersion is not None:
528 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:
529 webbrowser.open(newVersion)
531 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
533 def OnAbout(self, e):
534 aboutBox = aboutWindow.aboutWindow()
538 def OnClose(self, e):
539 profile.saveProfile(profile.getDefaultProfilePath())
541 # Save the window position, size & state from the preferences file
542 profile.putPreference('window_maximized', self.IsMaximized())
543 if not self.IsMaximized() and not self.IsIconized():
544 (posx, posy) = self.GetPosition()
545 profile.putPreference('window_pos_x', posx)
546 profile.putPreference('window_pos_y', posy)
547 (width, height) = self.GetSize()
548 profile.putPreference('window_width', width)
549 profile.putPreference('window_height', height)
551 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
552 isSimple = profile.getPreference('startMode') == 'Simple'
554 self.normalSashPos = self.splitter.GetSashPosition()
555 profile.putPreference('window_normal_sash', self.normalSashPos)
557 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
559 self.scene.OnPaint = lambda e : e
560 self.scene._slicer.cleanup()
566 class normalSettingsPanel(configBase.configPanelBase):
567 "Main user interface window"
568 def __init__(self, parent, callback = None):
569 super(normalSettingsPanel, self).__init__(parent, callback)
572 self.nb = wx.Notebook(self)
573 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
574 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
576 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
577 self._addSettingsToPanels('basic', left, right)
578 self.SizeLabelWidths(left, right)
580 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
581 self._addSettingsToPanels('advanced', left, right)
582 self.SizeLabelWidths(left, right)
585 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
586 self.nb.AddPage(self.pluginPanel, _("Plugins"))
589 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
590 self.alterationPanel = None
592 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
593 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
595 self.Bind(wx.EVT_SIZE, self.OnSize)
597 self.nb.SetSize(self.GetSize())
598 self.UpdateSize(self.printPanel)
599 self.UpdateSize(self.advancedPanel)
601 def _addSettingsToPanels(self, category, left, right):
602 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
606 for title in profile.getSubCategoriesFor(category):
607 n += 1 + len(profile.getSettingsForCategory(category, title))
610 configBase.TitleRow(p, _(title))
611 for s in profile.getSettingsForCategory(category, title):
612 configBase.SettingRow(p, s.getName())
614 def SizeLabelWidths(self, left, right):
615 leftWidth = self.getLabelColumnWidth(left)
616 rightWidth = self.getLabelColumnWidth(right)
617 maxWidth = max(leftWidth, rightWidth)
618 self.setLabelColumnWidth(left, maxWidth)
619 self.setLabelColumnWidth(right, maxWidth)
622 # Make the size of the Notebook control the same size as this control
623 self.nb.SetSize(self.GetSize())
625 # Propegate the OnSize() event (just in case)
628 # Perform out resize magic
629 self.UpdateSize(self.printPanel)
630 self.UpdateSize(self.advancedPanel)
632 def UpdateSize(self, configPanel):
633 sizer = configPanel.GetSizer()
637 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
640 # if width(col1) > (best_width(col1) + best_width(col1)):
641 # switch to horizontal
644 col1 = configPanel.leftPanel
645 colSize1 = col1.GetSize()
646 colBestSize1 = col1.GetBestSize()
647 col2 = configPanel.rightPanel
648 colSize2 = col2.GetSize()
649 colBestSize2 = col2.GetBestSize()
651 orientation = sizer.GetOrientation()
653 if orientation == wx.HORIZONTAL:
654 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
656 sizer = wx.BoxSizer(wx.VERTICAL)
657 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
658 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
659 configPanel.SetSizer(sizer)
665 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
667 sizer = wx.BoxSizer(wx.HORIZONTAL)
668 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
669 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
670 configPanel.SetSizer(sizer)
676 def updateProfileToControls(self):
677 super(normalSettingsPanel, self).updateProfileToControls()
678 if self.alterationPanel is not None:
679 self.alterationPanel.updateProfileToControls()
680 self.pluginPanel.updateProfileToControls()