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 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)
286 self.switchToQuickprintMenuItem.Enable(not isSimple)
287 self.switchToNormalMenuItem.Enable(isSimple)
289 # Set splitter sash position & size
291 # Save normal mode sash
292 self.normalSashPos = self.splitter.GetSashPosition()
294 # Change location of sash to width of quick mode pane
295 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
296 self.splitter.SetSashPosition(width, True)
299 self.splitter.SetSashSize(0)
301 self.splitter.SetSashPosition(self.normalSashPos, True)
303 self.splitter.SetSashSize(4)
304 self.scene.updateProfileToControls()
306 def OnPreferences(self, e):
307 prefDialog = preferencesDialog.preferencesDialog(self)
311 def OnMachineSettings(self, e):
312 prefDialog = preferencesDialog.machineSettingsDialog(self)
316 def OnDropFiles(self, files):
318 profile.setPluginConfig([])
319 self.updateProfileToAllControls()
320 self.scene.loadFiles(files)
322 def OnModelMRU(self, e):
323 fileNum = e.GetId() - self.ID_MRU_MODEL1
324 path = self.modelFileHistory.GetHistoryFile(fileNum)
326 self.modelFileHistory.AddFileToHistory(path) # move up the list
327 self.config.SetPath("/ModelMRU")
328 self.modelFileHistory.Save(self.config)
331 profile.putPreference('lastFile', path)
333 self.scene.loadFiles(filelist)
335 def addToModelMRU(self, file):
336 self.modelFileHistory.AddFileToHistory(file)
337 self.config.SetPath("/ModelMRU")
338 self.modelFileHistory.Save(self.config)
341 def OnProfileMRU(self, e):
342 fileNum = e.GetId() - self.ID_MRU_PROFILE1
343 path = self.profileFileHistory.GetHistoryFile(fileNum)
345 self.profileFileHistory.AddFileToHistory(path) # move up the list
346 self.config.SetPath("/ProfileMRU")
347 self.profileFileHistory.Save(self.config)
350 profile.loadProfile(path)
351 self.updateProfileToAllControls()
353 def addToProfileMRU(self, file):
354 self.profileFileHistory.AddFileToHistory(file)
355 self.config.SetPath("/ProfileMRU")
356 self.profileFileHistory.Save(self.config)
359 def updateProfileToAllControls(self):
360 self.scene.updateProfileToControls()
361 self.normalSettingsPanel.updateProfileToControls()
362 self.simpleSettingsPanel.updateProfileToControls()
364 def reloadSettingPanels(self):
365 self.leftSizer.Detach(self.simpleSettingsPanel)
366 self.leftSizer.Detach(self.normalSettingsPanel)
367 self.simpleSettingsPanel.Destroy()
368 self.normalSettingsPanel.Destroy()
369 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
370 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
371 self.leftSizer.Add(self.simpleSettingsPanel, 1)
372 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
373 self.updateSliceMode()
374 self.updateProfileToAllControls()
376 def updateMachineMenu(self):
377 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
378 for item in self.machineMenu.GetMenuItems():
379 self.machineMenu.RemoveItem(item)
381 #Add a menu item for each machine configuration.
382 for n in xrange(0, profile.getMachineCount()):
383 i = self.machineMenu.Append(n, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
384 if n == int(profile.getPreferenceFloat('active_machine')):
386 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId()), i)
388 self.machineMenu.AppendSeparator()
389 i = self.machineMenu.Append(-1, _("Add new machine..."))
390 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
392 #Add tools for machines.
393 self.machineMenu.AppendSeparator()
394 i = self.machineMenu.Append(-1, _("Install custom firmware"))
395 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
396 i = self.machineMenu.Append(-1, _("Install default Marlin firmware"))
397 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
399 def OnLoadProfile(self, e):
400 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)
401 dlg.SetWildcard("ini files (*.ini)|*.ini")
402 if dlg.ShowModal() == wx.ID_OK:
403 profileFile = dlg.GetPath()
404 profile.loadProfile(profileFile)
405 self.updateProfileToAllControls()
407 # Update the Profile MRU
408 self.addToProfileMRU(profileFile)
411 def OnLoadProfileFromGcode(self, e):
412 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)
413 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
414 if dlg.ShowModal() == wx.ID_OK:
415 gcodeFile = dlg.GetPath()
416 f = open(gcodeFile, 'r')
419 if line.startswith(';CURA_PROFILE_STRING:'):
420 profile.setProfileFromString(line[line.find(':')+1:].strip())
423 self.updateProfileToAllControls()
425 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)
428 def OnSaveProfile(self, e):
429 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
430 dlg.SetWildcard("ini files (*.ini)|*.ini")
431 if dlg.ShowModal() == wx.ID_OK:
432 profileFile = dlg.GetPath()
433 profile.saveProfile(profileFile)
436 def OnResetProfile(self, e):
437 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)
438 result = dlg.ShowModal() == wx.ID_YES
441 profile.resetProfile()
442 self.updateProfileToAllControls()
444 def OnSimpleSwitch(self, e):
445 profile.putPreference('startMode', 'Simple')
446 self.updateSliceMode()
448 def OnNormalSwitch(self, e):
449 profile.putPreference('startMode', 'Normal')
450 self.updateSliceMode()
452 def OnDefaultMarlinFirmware(self, e):
453 firmwareInstall.InstallFirmware()
455 def OnCustomFirmware(self, e):
456 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
457 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)
458 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
459 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
460 if dlg.ShowModal() == wx.ID_OK:
461 filename = dlg.GetPath()
462 if not(os.path.exists(filename)):
464 #For some reason my Ubuntu 10.10 crashes here.
465 firmwareInstall.InstallFirmware(filename)
467 def OnFirstRunWizard(self, e):
469 configWizard.configWizard()
471 self.reloadSettingPanels()
473 def OnAddNewMachine(self, e):
475 profile.setActiveMachine(profile.getMachineCount())
476 configWizard.configWizard(True)
478 self.reloadSettingPanels()
479 self.updateMachineMenu()
481 def OnSelectMachine(self, index):
482 profile.setActiveMachine(index)
483 self.reloadSettingPanels()
485 def OnBedLevelWizard(self, e):
486 configWizard.bedLevelWizard()
488 def OnHeadOffsetWizard(self, e):
489 configWizard.headOffsetWizard()
491 def OnExpertOpen(self, e):
492 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
496 def OnMinecraftImport(self, e):
497 mi = minecraftImport.minecraftImportWindow(self)
501 def OnPIDDebugger(self, e):
502 debugger = pidDebugger.debuggerWindow(self)
506 def onCopyProfileClipboard(self, e):
508 if not wx.TheClipboard.IsOpened():
509 wx.TheClipboard.Open()
510 clipData = wx.TextDataObject()
511 self.lastTriedClipboard = profile.getProfileString()
512 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
513 clipData.SetText(profileString)
514 wx.TheClipboard.SetData(clipData)
515 wx.TheClipboard.Close()
517 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
519 def OnCheckForUpdate(self, e):
520 newVersion = version.checkForNewerVersion()
521 if newVersion is not None:
522 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:
523 webbrowser.open(newVersion)
525 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
527 def OnAbout(self, e):
528 info = wx.AboutDialogInfo()
530 info.SetDescription("""
531 End solution for Open Source Fused Filament Fabrication 3D printing.
532 * Cura is the graphical User Interface.
533 * CuraEngine is the slicer/gcode generator.
534 Cura and the CuraEngine are licensed AGPLv3.
536 info.SetWebSite('http://software.ultimaker.com/')
537 info.SetCopyright(_("Copyright (C) David Braam"))
539 This program is free software: you can redistribute it and/or modify
540 it under the terms of the GNU Affero General Public License as published by
541 the Free Software Foundation, either version 3 of the License, or
542 (at your option) any later version.
544 This program is distributed in the hope that it will be useful,
545 but WITHOUT ANY WARRANTY; without even the implied warranty of
546 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
547 GNU Affero General Public License for more details.
549 You should have received a copy of the GNU Affero General Public License
550 along with this program. If not, see <http://www.gnu.org/licenses/>.
554 def OnClose(self, e):
555 profile.saveProfile(profile.getDefaultProfilePath())
557 # Save the window position, size & state from the preferences file
558 profile.putPreference('window_maximized', self.IsMaximized())
559 if not self.IsMaximized() and not self.IsIconized():
560 (posx, posy) = self.GetPosition()
561 profile.putPreference('window_pos_x', posx)
562 profile.putPreference('window_pos_y', posy)
563 (width, height) = self.GetSize()
564 profile.putPreference('window_width', width)
565 profile.putPreference('window_height', height)
567 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
568 isSimple = profile.getPreference('startMode') == 'Simple'
570 self.normalSashPos = self.splitter.GetSashPosition()
571 profile.putPreference('window_normal_sash', self.normalSashPos)
573 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
575 self.scene.OnPaint = lambda e : e
576 self.scene._slicer.cleanup()
582 class normalSettingsPanel(configBase.configPanelBase):
583 "Main user interface window"
584 def __init__(self, parent, callback = None):
585 super(normalSettingsPanel, self).__init__(parent, callback)
588 self.nb = wx.Notebook(self)
589 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
590 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
592 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
593 self._addSettingsToPanels('basic', left, right)
594 self.SizeLabelWidths(left, right)
596 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
597 self._addSettingsToPanels('advanced', left, right)
598 self.SizeLabelWidths(left, right)
601 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
602 if len(self.pluginPanel.pluginList) > 0:
603 self.nb.AddPage(self.pluginPanel, _("Plugins"))
605 self.pluginPanel.Show(False)
608 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
609 self.alterationPanel = None
611 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
612 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
614 self.Bind(wx.EVT_SIZE, self.OnSize)
616 self.nb.SetSize(self.GetSize())
617 self.UpdateSize(self.printPanel)
618 self.UpdateSize(self.advancedPanel)
620 def _addSettingsToPanels(self, category, left, right):
621 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
625 for title in profile.getSubCategoriesFor(category):
626 n += 1 + len(profile.getSettingsForCategory(category, title))
629 configBase.TitleRow(p, title)
630 for s in profile.getSettingsForCategory(category, title):
631 configBase.SettingRow(p, s.getName())
633 def SizeLabelWidths(self, left, right):
634 leftWidth = self.getLabelColumnWidth(left)
635 rightWidth = self.getLabelColumnWidth(right)
636 maxWidth = max(leftWidth, rightWidth)
637 self.setLabelColumnWidth(left, maxWidth)
638 self.setLabelColumnWidth(right, maxWidth)
641 # Make the size of the Notebook control the same size as this control
642 self.nb.SetSize(self.GetSize())
644 # Propegate the OnSize() event (just in case)
647 # Perform out resize magic
648 self.UpdateSize(self.printPanel)
649 self.UpdateSize(self.advancedPanel)
651 def UpdateSize(self, configPanel):
652 sizer = configPanel.GetSizer()
656 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
659 # if width(col1) > (best_width(col1) + best_width(col1)):
660 # switch to horizontal
663 col1 = configPanel.leftPanel
664 colSize1 = col1.GetSize()
665 colBestSize1 = col1.GetBestSize()
666 col2 = configPanel.rightPanel
667 colSize2 = col2.GetSize()
668 colBestSize2 = col2.GetBestSize()
670 orientation = sizer.GetOrientation()
672 if orientation == wx.HORIZONTAL:
673 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
675 sizer = wx.BoxSizer(wx.VERTICAL)
676 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
677 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
678 configPanel.SetSizer(sizer)
684 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
686 sizer = wx.BoxSizer(wx.HORIZONTAL)
687 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
688 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
689 configPanel.SetSizer(sizer)
695 def updateProfileToControls(self):
696 super(normalSettingsPanel, self).updateProfileToControls()
697 if self.alterationPanel is not None:
698 self.alterationPanel.updateProfileToControls()
699 self.pluginPanel.updateProfileToControls()