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']))
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 self.fileMenu.AppendSeparator()
90 modelHistoryMenu = wx.Menu()
91 self.fileMenu.AppendMenu(wx.NewId(), _("&Recent Model Files"), modelHistoryMenu)
92 self.modelFileHistory.UseMenu(modelHistoryMenu)
93 self.modelFileHistory.AddFilesToMenu()
94 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
97 profileHistoryMenu = wx.Menu()
98 self.fileMenu.AppendMenu(wx.NewId(), _("&Recent Profile Files"), profileHistoryMenu)
99 self.profileFileHistory.UseMenu(profileHistoryMenu)
100 self.profileFileHistory.AddFilesToMenu()
101 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
103 self.fileMenu.AppendSeparator()
104 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
105 self.Bind(wx.EVT_MENU, self.OnQuit, i)
106 self.menubar.Append(self.fileMenu, _("&File"))
108 toolsMenu = wx.Menu()
110 i = toolsMenu.Append(-1, _("Switch to quickprint..."))
111 self.switchToQuickprintMenuItem = i
112 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
114 i = toolsMenu.Append(-1, _("Switch to full settings..."))
115 self.switchToNormalMenuItem = i
116 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
118 #toolsMenu.AppendSeparator()
119 #i = toolsMenu.Append(-1, 'Batch run...')
120 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
121 #self.normalModeOnlyItems.append(i)
123 if minecraftImport.hasMinecraft():
124 i = toolsMenu.Append(-1, _("Minecraft import..."))
125 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
127 if version.isDevVersion():
128 i = toolsMenu.Append(-1, _("PID Debugger..."))
129 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
131 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
132 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
133 self.menubar.Append(toolsMenu, _("Tools"))
135 expertMenu = wx.Menu()
136 i = expertMenu.Append(-1, _("Open expert settings..."))
137 self.normalModeOnlyItems.append(i)
138 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
139 expertMenu.AppendSeparator()
140 if firmwareInstall.getDefaultFirmware() is not None:
141 i = expertMenu.Append(-1, _("Install default Marlin firmware"))
142 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
143 i = expertMenu.Append(-1, _("Install custom firmware"))
144 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
145 expertMenu.AppendSeparator()
146 i = expertMenu.Append(-1, _("Run first run wizard..."))
147 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
148 i = expertMenu.Append(-1, _("Run bed leveling wizard..."))
149 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
150 if self.extruderCount > 1:
151 i = expertMenu.Append(-1, _("Run head offset wizard..."))
152 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
154 i = expertMenu.Append(-1, _("Add new machine..."))
155 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
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 wx.TheClipboard.Open()
258 do = wx.TextDataObject()
259 if wx.TheClipboard.GetData(do):
260 profileString = do.GetText()
261 wx.TheClipboard.Close()
263 if "CURA_PROFILE_STRING:" in profileString:
264 #print "Found correct syntax on clipboard"
265 profileString = profileString.replace("\n","")
266 profileString = profileString.replace("CURA_PROFILE_STRING:", "")
267 if profileString != self.lastTriedClipboard:
268 self.lastTriedClipboard = profileString
269 profile.setProfileFromString(profileString)
270 print "changed profile"
271 self.updateProfileToAllControls()
273 print "Unable to read from clipboard"
276 def updateSliceMode(self):
277 isSimple = profile.getPreference('startMode') == 'Simple'
279 self.normalSettingsPanel.Show(not isSimple)
280 self.simpleSettingsPanel.Show(isSimple)
281 self.leftPane.Layout()
283 for i in self.normalModeOnlyItems:
284 i.Enable(not isSimple)
285 self.switchToQuickprintMenuItem.Enable(not isSimple)
286 self.switchToNormalMenuItem.Enable(isSimple)
288 # Set splitter sash position & size
290 # Save normal mode sash
291 self.normalSashPos = self.splitter.GetSashPosition()
293 # Change location of sash to width of quick mode pane
294 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
295 self.splitter.SetSashPosition(width, True)
298 self.splitter.SetSashSize(0)
300 self.splitter.SetSashPosition(self.normalSashPos, True)
302 self.splitter.SetSashSize(4)
303 self.scene.updateProfileToControls()
305 def OnPreferences(self, e):
306 prefDialog = preferencesDialog.preferencesDialog(self)
310 def OnDropFiles(self, files):
312 profile.setPluginConfig([])
313 self.updateProfileToAllControls()
314 self.scene.loadFiles(files)
316 def OnModelMRU(self, e):
317 fileNum = e.GetId() - self.ID_MRU_MODEL1
318 path = self.modelFileHistory.GetHistoryFile(fileNum)
320 self.modelFileHistory.AddFileToHistory(path) # move up the list
321 self.config.SetPath("/ModelMRU")
322 self.modelFileHistory.Save(self.config)
325 profile.putPreference('lastFile', path)
327 self.scene.loadFiles(filelist)
329 def addToModelMRU(self, file):
330 self.modelFileHistory.AddFileToHistory(file)
331 self.config.SetPath("/ModelMRU")
332 self.modelFileHistory.Save(self.config)
335 def OnProfileMRU(self, e):
336 fileNum = e.GetId() - self.ID_MRU_PROFILE1
337 path = self.profileFileHistory.GetHistoryFile(fileNum)
339 self.profileFileHistory.AddFileToHistory(path) # move up the list
340 self.config.SetPath("/ProfileMRU")
341 self.profileFileHistory.Save(self.config)
344 profile.loadProfile(path)
345 self.updateProfileToAllControls()
347 def addToProfileMRU(self, file):
348 self.profileFileHistory.AddFileToHistory(file)
349 self.config.SetPath("/ProfileMRU")
350 self.profileFileHistory.Save(self.config)
353 def updateProfileToAllControls(self):
354 self.scene.updateProfileToControls()
355 self.normalSettingsPanel.updateProfileToControls()
356 self.simpleSettingsPanel.updateProfileToControls()
358 def reloadSettingPanels(self):
359 self.leftSizer.Detach(self.simpleSettingsPanel)
360 self.leftSizer.Detach(self.normalSettingsPanel)
361 self.simpleSettingsPanel.Destroy()
362 self.normalSettingsPanel.Destroy()
363 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
364 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
365 self.leftSizer.Add(self.simpleSettingsPanel, 1)
366 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
367 self.updateSliceMode()
368 self.updateProfileToAllControls()
370 def OnLoadProfile(self, e):
371 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)
372 dlg.SetWildcard("ini files (*.ini)|*.ini")
373 if dlg.ShowModal() == wx.ID_OK:
374 profileFile = dlg.GetPath()
375 profile.loadProfile(profileFile)
376 self.updateProfileToAllControls()
378 # Update the Profile MRU
379 self.addToProfileMRU(profileFile)
382 def OnLoadProfileFromGcode(self, e):
383 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)
384 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
385 if dlg.ShowModal() == wx.ID_OK:
386 gcodeFile = dlg.GetPath()
387 f = open(gcodeFile, 'r')
390 if line.startswith(';CURA_PROFILE_STRING:'):
391 profile.setProfileFromString(line[line.find(':')+1:].strip())
394 self.updateProfileToAllControls()
396 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)
399 def OnSaveProfile(self, e):
400 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
401 dlg.SetWildcard("ini files (*.ini)|*.ini")
402 if dlg.ShowModal() == wx.ID_OK:
403 profileFile = dlg.GetPath()
404 profile.saveProfile(profileFile)
407 def OnResetProfile(self, e):
408 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)
409 result = dlg.ShowModal() == wx.ID_YES
412 profile.resetProfile()
413 self.updateProfileToAllControls()
415 def OnSimpleSwitch(self, e):
416 profile.putPreference('startMode', 'Simple')
417 self.updateSliceMode()
419 def OnNormalSwitch(self, e):
420 profile.putPreference('startMode', 'Normal')
421 self.updateSliceMode()
423 def OnDefaultMarlinFirmware(self, e):
424 firmwareInstall.InstallFirmware()
426 def OnCustomFirmware(self, e):
427 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
428 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)
429 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
430 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
431 if dlg.ShowModal() == wx.ID_OK:
432 filename = dlg.GetPath()
433 if not(os.path.exists(filename)):
435 #For some reason my Ubuntu 10.10 crashes here.
436 firmwareInstall.InstallFirmware(filename)
438 def OnFirstRunWizard(self, e):
440 configWizard.configWizard()
442 self.reloadSettingPanels()
444 def OnAddNewMachine(self, e):
447 while profile.getMachineSetting('machine_name', n) != '':
449 profile.setActiveMachine(n)
450 configWizard.configWizard(True)
452 self.reloadSettingPanels()
454 def OnBedLevelWizard(self, e):
455 configWizard.bedLevelWizard()
457 def OnHeadOffsetWizard(self, e):
458 configWizard.headOffsetWizard()
460 def OnExpertOpen(self, e):
461 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
465 def OnMinecraftImport(self, e):
466 mi = minecraftImport.minecraftImportWindow(self)
470 def OnPIDDebugger(self, e):
471 debugger = pidDebugger.debuggerWindow(self)
475 def onCopyProfileClipboard(self, e):
477 if not wx.TheClipboard.IsOpened():
478 wx.TheClipboard.Open()
479 clipData = wx.TextDataObject()
480 self.lastTriedClipboard = profile.getProfileString()
481 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
482 clipData.SetText(profileString)
483 wx.TheClipboard.SetData(clipData)
484 wx.TheClipboard.Close()
486 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
488 def OnCheckForUpdate(self, e):
489 newVersion = version.checkForNewerVersion()
490 if newVersion is not None:
491 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:
492 webbrowser.open(newVersion)
494 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
496 def OnAbout(self, e):
497 info = wx.AboutDialogInfo()
499 info.SetDescription(_("End solution for Open Source Fused Filament Fabrication 3D printing."))
500 info.SetWebSite('http://software.ultimaker.com/')
501 info.SetCopyright(_("Copyright (C) David Braam"))
503 This program is free software: you can redistribute it and/or modify
504 it under the terms of the GNU Affero General Public License as published by
505 the Free Software Foundation, either version 3 of the License, or
506 (at your option) any later version.
508 This program is distributed in the hope that it will be useful,
509 but WITHOUT ANY WARRANTY; without even the implied warranty of
510 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
511 GNU Affero General Public License for more details.
513 You should have received a copy of the GNU Affero General Public License
514 along with this program. If not, see <http://www.gnu.org/licenses/>.
518 def OnClose(self, e):
519 profile.saveProfile(profile.getDefaultProfilePath())
521 # Save the window position, size & state from the preferences file
522 profile.putPreference('window_maximized', self.IsMaximized())
523 if not self.IsMaximized() and not self.IsIconized():
524 (posx, posy) = self.GetPosition()
525 profile.putPreference('window_pos_x', posx)
526 profile.putPreference('window_pos_y', posy)
527 (width, height) = self.GetSize()
528 profile.putPreference('window_width', width)
529 profile.putPreference('window_height', height)
531 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
532 isSimple = profile.getPreference('startMode') == 'Simple'
534 self.normalSashPos = self.splitter.GetSashPosition()
535 profile.putPreference('window_normal_sash', self.normalSashPos)
537 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
539 self.scene.OnPaint = lambda e : e
540 self.scene._slicer.cleanup()
546 class normalSettingsPanel(configBase.configPanelBase):
547 "Main user interface window"
548 def __init__(self, parent, callback = None):
549 super(normalSettingsPanel, self).__init__(parent, callback)
552 self.nb = wx.Notebook(self)
553 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
554 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
556 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
557 self._addSettingsToPanels('basic', left, right)
558 self.SizeLabelWidths(left, right)
560 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
561 self._addSettingsToPanels('advanced', left, right)
562 self.SizeLabelWidths(left, right)
565 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
566 if len(self.pluginPanel.pluginList) > 0:
567 self.nb.AddPage(self.pluginPanel, "Plugins")
569 self.pluginPanel.Show(False)
572 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
573 self.alterationPanel = None
575 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
576 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
578 self.Bind(wx.EVT_SIZE, self.OnSize)
580 self.nb.SetSize(self.GetSize())
581 self.UpdateSize(self.printPanel)
582 self.UpdateSize(self.advancedPanel)
584 def _addSettingsToPanels(self, category, left, right):
585 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
589 for title in profile.getSubCategoriesFor(category):
590 n += 1 + len(profile.getSettingsForCategory(category, title))
593 configBase.TitleRow(p, title)
594 for s in profile.getSettingsForCategory(category, title):
595 configBase.SettingRow(p, s.getName())
597 def SizeLabelWidths(self, left, right):
598 leftWidth = self.getLabelColumnWidth(left)
599 rightWidth = self.getLabelColumnWidth(right)
600 maxWidth = max(leftWidth, rightWidth)
601 self.setLabelColumnWidth(left, maxWidth)
602 self.setLabelColumnWidth(right, maxWidth)
605 # Make the size of the Notebook control the same size as this control
606 self.nb.SetSize(self.GetSize())
608 # Propegate the OnSize() event (just in case)
611 # Perform out resize magic
612 self.UpdateSize(self.printPanel)
613 self.UpdateSize(self.advancedPanel)
615 def UpdateSize(self, configPanel):
616 sizer = configPanel.GetSizer()
620 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
623 # if width(col1) > (best_width(col1) + best_width(col1)):
624 # switch to horizontal
627 col1 = configPanel.leftPanel
628 colSize1 = col1.GetSize()
629 colBestSize1 = col1.GetBestSize()
630 col2 = configPanel.rightPanel
631 colSize2 = col2.GetSize()
632 colBestSize2 = col2.GetBestSize()
634 orientation = sizer.GetOrientation()
636 if orientation == wx.HORIZONTAL:
637 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
639 sizer = wx.BoxSizer(wx.VERTICAL)
640 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
641 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
642 configPanel.SetSizer(sizer)
648 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
650 sizer = wx.BoxSizer(wx.HORIZONTAL)
651 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
652 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
653 configPanel.SetSizer(sizer)
659 def updateProfileToControls(self):
660 super(normalSettingsPanel, self).updateProfileToControls()
661 if self.alterationPanel is not None:
662 self.alterationPanel.updateProfileToControls()
663 self.pluginPanel.updateProfileToControls()