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 self.extruderCount = int(profile.getMachineSetting('extruder_amount'))
32 wx.EVT_CLOSE(self, self.OnClose)
34 # allow dropping any file, restrict later
35 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
37 self.normalModeOnlyItems = []
39 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
40 self.config = wx.FileConfig(appName="Cura",
41 localFilename=mruFile,
42 style=wx.CONFIG_USE_LOCAL_FILE)
44 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)]
45 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
46 self.config.SetPath("/ModelMRU")
47 self.modelFileHistory.Load(self.config)
49 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)]
50 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
51 self.config.SetPath("/ProfileMRU")
52 self.profileFileHistory.Load(self.config)
54 self.menubar = wx.MenuBar()
55 self.fileMenu = wx.Menu()
56 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
57 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
58 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
59 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
60 i = self.fileMenu.Append(-1, _("Clear platform"))
61 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
63 self.fileMenu.AppendSeparator()
64 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
65 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
66 i = self.fileMenu.Append(-1, _("Save GCode..."))
67 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
68 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
69 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
71 self.fileMenu.AppendSeparator()
72 i = self.fileMenu.Append(-1, _("Open Profile..."))
73 self.normalModeOnlyItems.append(i)
74 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
75 i = self.fileMenu.Append(-1, _("Save Profile..."))
76 self.normalModeOnlyItems.append(i)
77 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
78 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
79 self.normalModeOnlyItems.append(i)
80 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
81 self.fileMenu.AppendSeparator()
82 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
83 self.normalModeOnlyItems.append(i)
84 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
86 self.fileMenu.AppendSeparator()
87 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
88 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
89 i = self.fileMenu.Append(-1, _("Machine settings..."))
90 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
91 self.fileMenu.AppendSeparator()
94 modelHistoryMenu = wx.Menu()
95 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
96 self.modelFileHistory.UseMenu(modelHistoryMenu)
97 self.modelFileHistory.AddFilesToMenu()
98 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
101 profileHistoryMenu = wx.Menu()
102 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
103 self.profileFileHistory.UseMenu(profileHistoryMenu)
104 self.profileFileHistory.AddFilesToMenu()
105 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
107 self.fileMenu.AppendSeparator()
108 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
109 self.Bind(wx.EVT_MENU, self.OnQuit, i)
110 self.menubar.Append(self.fileMenu, '&' + _("File"))
112 toolsMenu = wx.Menu()
113 #i = toolsMenu.Append(-1, 'Batch run...')
114 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
115 #self.normalModeOnlyItems.append(i)
117 if minecraftImport.hasMinecraft():
118 i = toolsMenu.Append(-1, _("Minecraft map import..."))
119 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
121 if version.isDevVersion():
122 i = toolsMenu.Append(-1, _("PID Debugger..."))
123 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
125 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
126 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
127 self.menubar.Append(toolsMenu, _("Tools"))
129 #Machine menu for machine configuration/tooling
130 self.machineMenu = wx.Menu()
131 self.updateMachineMenu()
133 self.menubar.Append(self.machineMenu, _("Machine"))
135 expertMenu = wx.Menu()
136 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
137 self.switchToQuickprintMenuItem = i
138 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
140 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
141 self.switchToNormalMenuItem = i
142 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
143 expertMenu.AppendSeparator()
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 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
152 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
153 if self.extruderCount > 1:
154 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
155 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
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 if not wx.TheClipboard.Open():
259 do = wx.TextDataObject()
260 if wx.TheClipboard.GetData(do):
261 profileString = do.GetText()
262 wx.TheClipboard.Close()
264 startTag = "CURA_PROFILE_STRING:"
265 if startTag in profileString:
266 #print "Found correct syntax on clipboard"
267 profileString = profileString.replace("\n","").strip()
268 profileString = profileString[profileString.find(startTag)+len(startTag):]
269 if profileString != self.lastTriedClipboard:
271 self.lastTriedClipboard = profileString
272 profile.setProfileFromString(profileString)
273 self.scene.notification.message("Loaded new profile from clipboard.")
274 self.updateProfileToAllControls()
276 print "Unable to read from clipboard"
279 def updateSliceMode(self):
280 isSimple = profile.getPreference('startMode') == 'Simple'
282 self.normalSettingsPanel.Show(not isSimple)
283 self.simpleSettingsPanel.Show(isSimple)
284 self.leftPane.Layout()
286 for i in self.normalModeOnlyItems:
287 i.Enable(not isSimple)
289 self.switchToQuickprintMenuItem.Check()
291 self.switchToNormalMenuItem.Check()
293 # Set splitter sash position & size
295 # Save normal mode sash
296 self.normalSashPos = self.splitter.GetSashPosition()
298 # Change location of sash to width of quick mode pane
299 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
300 self.splitter.SetSashPosition(width, True)
303 self.splitter.SetSashSize(0)
305 self.splitter.SetSashPosition(self.normalSashPos, True)
307 self.splitter.SetSashSize(4)
308 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
309 if profile.getMachineSetting('machine_type') == 'ultimaker2':
310 self.bedLevelWizardMenuItem.Enable(False)
311 self.headOffsetWizardMenuItem.Enable(False)
312 self.scene.updateProfileToControls()
314 def OnPreferences(self, e):
315 prefDialog = preferencesDialog.preferencesDialog(self)
318 wx.CallAfter(prefDialog.Show)
320 def OnMachineSettings(self, e):
321 prefDialog = preferencesDialog.machineSettingsDialog(self)
325 def OnDropFiles(self, files):
327 profile.setPluginConfig([])
328 self.updateProfileToAllControls()
329 self.scene.loadFiles(files)
331 def OnModelMRU(self, e):
332 fileNum = e.GetId() - self.ID_MRU_MODEL1
333 path = self.modelFileHistory.GetHistoryFile(fileNum)
335 self.modelFileHistory.AddFileToHistory(path) # move up the list
336 self.config.SetPath("/ModelMRU")
337 self.modelFileHistory.Save(self.config)
340 profile.putPreference('lastFile', path)
342 self.scene.loadFiles(filelist)
344 def addToModelMRU(self, file):
345 self.modelFileHistory.AddFileToHistory(file)
346 self.config.SetPath("/ModelMRU")
347 self.modelFileHistory.Save(self.config)
350 def OnProfileMRU(self, e):
351 fileNum = e.GetId() - self.ID_MRU_PROFILE1
352 path = self.profileFileHistory.GetHistoryFile(fileNum)
354 self.profileFileHistory.AddFileToHistory(path) # move up the list
355 self.config.SetPath("/ProfileMRU")
356 self.profileFileHistory.Save(self.config)
359 profile.loadProfile(path)
360 self.updateProfileToAllControls()
362 def addToProfileMRU(self, file):
363 self.profileFileHistory.AddFileToHistory(file)
364 self.config.SetPath("/ProfileMRU")
365 self.profileFileHistory.Save(self.config)
368 def updateProfileToAllControls(self):
369 self.scene.updateProfileToControls()
370 self.normalSettingsPanel.updateProfileToControls()
371 self.simpleSettingsPanel.updateProfileToControls()
373 def reloadSettingPanels(self):
374 self.leftSizer.Detach(self.simpleSettingsPanel)
375 self.leftSizer.Detach(self.normalSettingsPanel)
376 self.simpleSettingsPanel.Destroy()
377 self.normalSettingsPanel.Destroy()
378 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
379 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
380 self.leftSizer.Add(self.simpleSettingsPanel, 1)
381 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
382 self.updateSliceMode()
383 self.updateProfileToAllControls()
385 def updateMachineMenu(self):
386 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
387 for item in self.machineMenu.GetMenuItems():
388 self.machineMenu.RemoveItem(item)
390 #Add a menu item for each machine configuration.
391 for n in xrange(0, profile.getMachineCount()):
392 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
393 if n == int(profile.getPreferenceFloat('active_machine')):
395 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
397 self.machineMenu.AppendSeparator()
398 i = self.machineMenu.Append(-1, _("Add new machine..."))
399 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
401 #Add tools for machines.
402 self.machineMenu.AppendSeparator()
404 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
405 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
407 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
408 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
410 def OnLoadProfile(self, e):
411 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)
412 dlg.SetWildcard("ini files (*.ini)|*.ini")
413 if dlg.ShowModal() == wx.ID_OK:
414 profileFile = dlg.GetPath()
415 profile.loadProfile(profileFile)
416 self.updateProfileToAllControls()
418 # Update the Profile MRU
419 self.addToProfileMRU(profileFile)
422 def OnLoadProfileFromGcode(self, e):
423 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)
424 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
425 if dlg.ShowModal() == wx.ID_OK:
426 gcodeFile = dlg.GetPath()
427 f = open(gcodeFile, 'r')
430 if line.startswith(';CURA_PROFILE_STRING:'):
431 profile.setProfileFromString(line[line.find(':')+1:].strip())
434 self.updateProfileToAllControls()
436 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)
439 def OnSaveProfile(self, e):
440 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
441 dlg.SetWildcard("ini files (*.ini)|*.ini")
442 if dlg.ShowModal() == wx.ID_OK:
443 profileFile = dlg.GetPath()
444 profile.saveProfile(profileFile)
447 def OnResetProfile(self, e):
448 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)
449 result = dlg.ShowModal() == wx.ID_YES
452 profile.resetProfile()
453 self.updateProfileToAllControls()
455 def OnSimpleSwitch(self, e):
456 profile.putPreference('startMode', 'Simple')
457 self.updateSliceMode()
459 def OnNormalSwitch(self, e):
460 profile.putPreference('startMode', 'Normal')
461 self.updateSliceMode()
463 def OnDefaultMarlinFirmware(self, e):
464 firmwareInstall.InstallFirmware()
466 def OnCustomFirmware(self, e):
467 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
468 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)
469 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
470 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
471 if dlg.ShowModal() == wx.ID_OK:
472 filename = dlg.GetPath()
473 if not(os.path.exists(filename)):
475 #For some reason my Ubuntu 10.10 crashes here.
476 firmwareInstall.InstallFirmware(filename)
478 def OnFirstRunWizard(self, e):
480 configWizard.configWizard()
482 self.reloadSettingPanels()
484 def OnAddNewMachine(self, e):
486 profile.setActiveMachine(profile.getMachineCount())
487 configWizard.configWizard(True)
489 self.reloadSettingPanels()
490 self.updateMachineMenu()
492 def OnSelectMachine(self, index):
493 profile.setActiveMachine(index)
494 self.reloadSettingPanels()
496 def OnBedLevelWizard(self, e):
497 configWizard.bedLevelWizard()
499 def OnHeadOffsetWizard(self, e):
500 configWizard.headOffsetWizard()
502 def OnExpertOpen(self, e):
503 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
507 def OnMinecraftImport(self, e):
508 mi = minecraftImport.minecraftImportWindow(self)
512 def OnPIDDebugger(self, e):
513 debugger = pidDebugger.debuggerWindow(self)
517 def onCopyProfileClipboard(self, e):
519 if not wx.TheClipboard.IsOpened():
520 wx.TheClipboard.Open()
521 clipData = wx.TextDataObject()
522 self.lastTriedClipboard = profile.getProfileString()
523 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
524 clipData.SetText(profileString)
525 wx.TheClipboard.SetData(clipData)
526 wx.TheClipboard.Close()
528 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
530 def OnCheckForUpdate(self, e):
531 newVersion = version.checkForNewerVersion()
532 if newVersion is not None:
533 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:
534 webbrowser.open(newVersion)
536 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
538 def OnAbout(self, e):
539 aboutBox = aboutWindow.aboutWindow()
543 def OnClose(self, e):
544 profile.saveProfile(profile.getDefaultProfilePath())
546 # Save the window position, size & state from the preferences file
547 profile.putPreference('window_maximized', self.IsMaximized())
548 if not self.IsMaximized() and not self.IsIconized():
549 (posx, posy) = self.GetPosition()
550 profile.putPreference('window_pos_x', posx)
551 profile.putPreference('window_pos_y', posy)
552 (width, height) = self.GetSize()
553 profile.putPreference('window_width', width)
554 profile.putPreference('window_height', height)
556 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
557 isSimple = profile.getPreference('startMode') == 'Simple'
559 self.normalSashPos = self.splitter.GetSashPosition()
560 profile.putPreference('window_normal_sash', self.normalSashPos)
562 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
564 self.scene.OnPaint = lambda e : e
565 self.scene._slicer.cleanup()
571 class normalSettingsPanel(configBase.configPanelBase):
572 "Main user interface window"
573 def __init__(self, parent, callback = None):
574 super(normalSettingsPanel, self).__init__(parent, callback)
577 self.nb = wx.Notebook(self)
578 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
579 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
581 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
582 self._addSettingsToPanels('basic', left, right)
583 self.SizeLabelWidths(left, right)
585 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
586 self._addSettingsToPanels('advanced', left, right)
587 self.SizeLabelWidths(left, right)
590 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
591 self.nb.AddPage(self.pluginPanel, _("Plugins"))
594 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
595 self.alterationPanel = None
597 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
598 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
600 self.Bind(wx.EVT_SIZE, self.OnSize)
602 self.nb.SetSize(self.GetSize())
603 self.UpdateSize(self.printPanel)
604 self.UpdateSize(self.advancedPanel)
606 def _addSettingsToPanels(self, category, left, right):
607 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
611 for title in profile.getSubCategoriesFor(category):
612 n += 1 + len(profile.getSettingsForCategory(category, title))
615 configBase.TitleRow(p, _(title))
616 for s in profile.getSettingsForCategory(category, title):
617 configBase.SettingRow(p, s.getName())
619 def SizeLabelWidths(self, left, right):
620 leftWidth = self.getLabelColumnWidth(left)
621 rightWidth = self.getLabelColumnWidth(right)
622 maxWidth = max(leftWidth, rightWidth)
623 self.setLabelColumnWidth(left, maxWidth)
624 self.setLabelColumnWidth(right, maxWidth)
627 # Make the size of the Notebook control the same size as this control
628 self.nb.SetSize(self.GetSize())
630 # Propegate the OnSize() event (just in case)
633 # Perform out resize magic
634 self.UpdateSize(self.printPanel)
635 self.UpdateSize(self.advancedPanel)
637 def UpdateSize(self, configPanel):
638 sizer = configPanel.GetSizer()
642 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
645 # if width(col1) > (best_width(col1) + best_width(col1)):
646 # switch to horizontal
649 col1 = configPanel.leftPanel
650 colSize1 = col1.GetSize()
651 colBestSize1 = col1.GetBestSize()
652 col2 = configPanel.rightPanel
653 colSize2 = col2.GetSize()
654 colBestSize2 = col2.GetBestSize()
656 orientation = sizer.GetOrientation()
658 if orientation == wx.HORIZONTAL:
659 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
661 sizer = wx.BoxSizer(wx.VERTICAL)
662 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
663 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
664 configPanel.SetSizer(sizer)
670 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
672 sizer = wx.BoxSizer(wx.HORIZONTAL)
673 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
674 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
675 configPanel.SetSizer(sizer)
681 def updateProfileToControls(self):
682 super(normalSettingsPanel, self).updateProfileToControls()
683 if self.alterationPanel is not None:
684 self.alterationPanel.updateProfileToControls()
685 self.pluginPanel.updateProfileToControls()