1 from __future__ import absolute_import
7 from Cura.gui import configBase
8 from Cura.gui import expertConfig
9 from Cura.gui import alterationPanel
10 from Cura.gui import pluginPanel
11 from Cura.gui import preferencesDialog
12 from Cura.gui import configWizard
13 from Cura.gui import firmwareInstall
14 from Cura.gui import simpleMode
15 from Cura.gui import sceneView
16 #from Cura.gui.tools import batchRun
17 from Cura.gui.util import dropTarget
18 from Cura.gui.tools import minecraftImport
19 from Cura.gui.tools import superformula
20 from Cura.util import profile
21 from Cura.util import version
22 from Cura.util import meshLoader
24 class mainWindow(wx.Frame):
26 super(mainWindow, self).__init__(None, title='Cura Steam Engine BETA - ' + version.getVersion())
28 self.extruderCount = int(profile.getPreference('extruder_amount'))
30 wx.EVT_CLOSE(self, self.OnClose)
32 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions()))
34 self.normalModeOnlyItems = []
36 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
37 self.config = wx.FileConfig(appName="Cura",
38 localFilename=mruFile,
39 style=wx.CONFIG_USE_LOCAL_FILE)
41 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)]
42 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
43 self.config.SetPath("/ModelMRU")
44 self.modelFileHistory.Load(self.config)
46 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)]
47 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
48 self.config.SetPath("/ProfileMRU")
49 self.profileFileHistory.Load(self.config)
51 self.menubar = wx.MenuBar()
52 self.fileMenu = wx.Menu()
53 i = self.fileMenu.Append(-1, 'Load model file...\tCTRL+L')
54 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
55 i = self.fileMenu.Append(-1, 'Save model...\tCTRL+S')
56 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
58 self.fileMenu.AppendSeparator()
59 i = self.fileMenu.Append(-1, 'Print...\tCTRL+P')
60 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
61 i = self.fileMenu.Append(-1, 'Save GCode...')
62 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
64 self.fileMenu.AppendSeparator()
65 i = self.fileMenu.Append(-1, 'Open Profile...')
66 self.normalModeOnlyItems.append(i)
67 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
68 i = self.fileMenu.Append(-1, 'Save Profile...')
69 self.normalModeOnlyItems.append(i)
70 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
71 i = self.fileMenu.Append(-1, 'Load Profile from GCode...')
72 self.normalModeOnlyItems.append(i)
73 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
74 self.fileMenu.AppendSeparator()
75 i = self.fileMenu.Append(-1, 'Reset Profile to default')
76 self.normalModeOnlyItems.append(i)
77 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
79 self.fileMenu.AppendSeparator()
80 i = self.fileMenu.Append(-1, 'Preferences...\tCTRL+,')
81 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
82 self.fileMenu.AppendSeparator()
85 modelHistoryMenu = wx.Menu()
86 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Model Files", modelHistoryMenu)
87 self.modelFileHistory.UseMenu(modelHistoryMenu)
88 self.modelFileHistory.AddFilesToMenu()
89 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
92 profileHistoryMenu = wx.Menu()
93 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Profile Files", profileHistoryMenu)
94 self.profileFileHistory.UseMenu(profileHistoryMenu)
95 self.profileFileHistory.AddFilesToMenu()
96 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
98 self.fileMenu.AppendSeparator()
99 i = self.fileMenu.Append(wx.ID_EXIT, 'Quit')
100 self.Bind(wx.EVT_MENU, self.OnQuit, i)
101 self.menubar.Append(self.fileMenu, '&File')
103 toolsMenu = wx.Menu()
104 i = toolsMenu.Append(-1, 'Switch to quickprint...')
105 self.switchToQuickprintMenuItem = i
106 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
107 i = toolsMenu.Append(-1, 'Switch to full settings...')
108 self.switchToNormalMenuItem = i
109 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
110 toolsMenu.AppendSeparator()
111 #i = toolsMenu.Append(-1, 'Batch run...')
112 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
113 #self.normalModeOnlyItems.append(i)
114 if minecraftImport.hasMinecraft():
115 i = toolsMenu.Append(-1, 'Minecraft import...')
116 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
117 i = toolsMenu.Append(-1, 'Super-shaper...')
118 self.Bind(wx.EVT_MENU, self.OnSuperformula, i)
119 self.menubar.Append(toolsMenu, 'Tools')
121 expertMenu = wx.Menu()
122 i = expertMenu.Append(-1, 'Open expert settings...')
123 self.normalModeOnlyItems.append(i)
124 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
125 expertMenu.AppendSeparator()
126 if firmwareInstall.getDefaultFirmware() is not None:
127 i = expertMenu.Append(-1, 'Install default Marlin firmware')
128 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
129 i = expertMenu.Append(-1, 'Install custom firmware')
130 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
131 expertMenu.AppendSeparator()
132 i = expertMenu.Append(-1, 'Run first run wizard...')
133 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
134 i = expertMenu.Append(-1, 'Run bed leveling wizard...')
135 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
136 if self.extruderCount > 1:
137 i = expertMenu.Append(-1, 'Run head offset wizard...')
138 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
139 self.menubar.Append(expertMenu, 'Expert')
142 i = helpMenu.Append(-1, 'Online documentation...')
143 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
144 i = helpMenu.Append(-1, 'Report a problem...')
145 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
146 i = helpMenu.Append(-1, 'Check for update...')
147 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
148 i = helpMenu.Append(-1, 'About Cura...')
149 self.Bind(wx.EVT_MENU, self.OnAbout, i)
150 self.menubar.Append(helpMenu, 'Help')
151 self.SetMenuBar(self.menubar)
153 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
154 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
155 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
156 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
159 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
160 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
162 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
163 self.leftSizer.Add(self.simpleSettingsPanel)
164 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
165 self.leftPane.SetSizer(self.leftSizer)
168 self.scene = sceneView.SceneView(self.rightPane)
170 #Main sizer, to position the preview window, buttons and tab control
171 sizer = wx.BoxSizer()
172 self.rightPane.SetSizer(sizer)
173 sizer.Add(self.scene, 1, flag=wx.EXPAND)
176 sizer = wx.BoxSizer(wx.VERTICAL)
178 sizer.Add(self.splitter, 1, wx.EXPAND)
182 self.updateProfileToControls()
184 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
186 self.simpleSettingsPanel.Show(False)
187 self.normalSettingsPanel.Show(False)
189 # Set default window size & position
190 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
193 # Restore the window position, size & state from the preferences file
195 if profile.getPreference('window_maximized') == 'True':
198 posx = int(profile.getPreference('window_pos_x'))
199 posy = int(profile.getPreference('window_pos_y'))
200 width = int(profile.getPreference('window_width'))
201 height = int(profile.getPreference('window_height'))
202 if posx > 0 or posy > 0:
203 self.SetPosition((posx,posy))
204 if width > 0 and height > 0:
205 self.SetSize((width,height))
207 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
209 self.normalSashPos = 0
211 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
212 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
214 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
216 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
218 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
220 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
221 self.SetSize((800,600))
224 self.updateSliceMode()
226 def updateSliceMode(self):
227 isSimple = profile.getPreference('startMode') == 'Simple'
229 self.normalSettingsPanel.Show(not isSimple)
230 self.simpleSettingsPanel.Show(isSimple)
231 self.leftPane.Layout()
233 for i in self.normalModeOnlyItems:
234 i.Enable(not isSimple)
235 self.switchToQuickprintMenuItem.Enable(not isSimple)
236 self.switchToNormalMenuItem.Enable(isSimple)
238 # Set splitter sash position & size
240 # Save normal mode sash
241 self.normalSashPos = self.splitter.GetSashPosition()
243 # Change location of sash to width of quick mode pane
244 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
245 self.splitter.SetSashPosition(width, True)
248 self.splitter.SetSashSize(0)
250 self.splitter.SetSashPosition(self.normalSashPos, True)
252 self.splitter.SetSashSize(4)
253 self.scene.updateProfileToControls()
255 def OnPreferences(self, e):
256 prefDialog = preferencesDialog.preferencesDialog(self)
260 def OnDropFiles(self, files):
261 profile.setPluginConfig([])
262 self.updateProfileToControls()
263 self.scene.loadScene(files)
265 def OnModelMRU(self, e):
266 fileNum = e.GetId() - self.ID_MRU_MODEL1
267 path = self.modelFileHistory.GetHistoryFile(fileNum)
269 self.modelFileHistory.AddFileToHistory(path) # move up the list
270 self.config.SetPath("/ModelMRU")
271 self.modelFileHistory.Save(self.config)
275 self.scene.loadScene(filelist)
277 def addToModelMRU(self, file):
278 self.modelFileHistory.AddFileToHistory(file)
279 self.config.SetPath("/ModelMRU")
280 self.modelFileHistory.Save(self.config)
283 def OnProfileMRU(self, e):
284 fileNum = e.GetId() - self.ID_MRU_PROFILE1
285 path = self.profileFileHistory.GetHistoryFile(fileNum)
287 self.profileFileHistory.AddFileToHistory(path) # move up the list
288 self.config.SetPath("/ProfileMRU")
289 self.profileFileHistory.Save(self.config)
292 profile.loadProfile(path)
293 self.updateProfileToControls()
295 def addToProfileMRU(self, file):
296 self.profileFileHistory.AddFileToHistory(file)
297 self.config.SetPath("/ProfileMRU")
298 self.profileFileHistory.Save(self.config)
301 def updateProfileToControls(self):
302 self.scene.updateProfileToControls()
303 self.normalSettingsPanel.updateProfileToControls()
304 self.simpleSettingsPanel.updateProfileToControls()
306 def OnLoadProfile(self, e):
307 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)
308 dlg.SetWildcard("ini files (*.ini)|*.ini")
309 if dlg.ShowModal() == wx.ID_OK:
310 profileFile = dlg.GetPath()
311 profile.loadProfile(profileFile)
312 self.updateProfileToControls()
314 # Update the Profile MRU
315 self.addToProfileMRU(profileFile)
318 def OnLoadProfileFromGcode(self, e):
319 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)
320 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
321 if dlg.ShowModal() == wx.ID_OK:
322 gcodeFile = dlg.GetPath()
323 f = open(gcodeFile, 'r')
326 if line.startswith(';CURA_PROFILE_STRING:'):
327 profile.loadProfileFromString(line[line.find(':')+1:].strip())
330 self.updateProfileToControls()
332 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)
335 def OnSaveProfile(self, e):
336 dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
337 dlg.SetWildcard("ini files (*.ini)|*.ini")
338 if dlg.ShowModal() == wx.ID_OK:
339 profileFile = dlg.GetPath()
340 profile.saveProfile(profileFile)
343 def OnResetProfile(self, e):
344 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)
345 result = dlg.ShowModal() == wx.ID_YES
348 profile.resetProfile()
349 self.updateProfileToControls()
351 def OnBatchRun(self, e):
352 br = batchRun.batchRunWindow(self)
356 def OnSimpleSwitch(self, e):
357 profile.putPreference('startMode', 'Simple')
358 self.updateSliceMode()
360 def OnNormalSwitch(self, e):
361 profile.putPreference('startMode', 'Normal')
362 self.updateSliceMode()
364 def OnDefaultMarlinFirmware(self, e):
365 firmwareInstall.InstallFirmware()
367 def OnCustomFirmware(self, e):
368 if profile.getPreference('machine_type') == 'ultimaker':
369 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)
370 dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
371 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
372 if dlg.ShowModal() == wx.ID_OK:
373 filename = dlg.GetPath()
374 if not(os.path.exists(filename)):
376 #For some reason my Ubuntu 10.10 crashes here.
377 firmwareInstall.InstallFirmware(filename)
379 def OnFirstRunWizard(self, e):
380 configWizard.configWizard()
381 self.updateProfileToControls()
383 def OnBedLevelWizard(self, e):
384 configWizard.bedLevelWizard()
386 def OnHeadOffsetWizard(self, e):
387 configWizard.headOffsetWizard()
389 def OnExpertOpen(self, e):
390 ecw = expertConfig.expertConfigWindow()
393 self.scene.sceneUpdated()
395 def OnMinecraftImport(self, e):
396 mi = minecraftImport.minecraftImportWindow(self)
400 def OnSuperformula(self, e):
401 sf = superformula.superformulaWindow(self)
402 #sf = superformula.superformulaEvolver(self)
406 def OnCheckForUpdate(self, e):
407 newVersion = version.checkForNewerVersion()
408 if newVersion is not None:
409 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:
410 webbrowser.open(newVersion)
412 wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
414 def OnAbout(self, e):
415 info = wx.AboutDialogInfo()
417 info.SetDescription('End solution for Open Source Fused Filament Fabrication 3D printing.')
418 info.SetWebSite('http://software.ultimaker.com/')
419 info.SetCopyright('Copyright (C) David Braam')
421 This program is free software: you can redistribute it and/or modify
422 it under the terms of the GNU Affero General Public License as published by
423 the Free Software Foundation, either version 3 of the License, or
424 (at your option) any later version.
426 This program is distributed in the hope that it will be useful,
427 but WITHOUT ANY WARRANTY; without even the implied warranty of
428 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
429 GNU Affero General Public License for more details.
431 You should have received a copy of the GNU Affero General Public License
432 along with this program. If not, see <http://www.gnu.org/licenses/>.
436 def OnClose(self, e):
437 profile.saveProfile(profile.getDefaultProfilePath())
439 # Save the window position, size & state from the preferences file
440 profile.putPreference('window_maximized', self.IsMaximized())
441 if not self.IsMaximized() and not self.IsIconized():
442 (posx, posy) = self.GetPosition()
443 profile.putPreference('window_pos_x', posx)
444 profile.putPreference('window_pos_y', posy)
445 (width, height) = self.GetSize()
446 profile.putPreference('window_width', width)
447 profile.putPreference('window_height', height)
449 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
450 isSimple = profile.getPreference('startMode') == 'Simple'
452 self.normalSashPos = self.splitter.GetSashPosition()
453 profile.putPreference('window_normal_sash', self.normalSashPos)
455 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
457 self.scene.OnPaint = lambda e : e
458 self.scene._slicer.cleanup()
464 class normalSettingsPanel(configBase.configPanelBase):
465 "Main user interface window"
466 def __init__(self, parent, callback = None):
467 super(normalSettingsPanel, self).__init__(parent, callback)
470 self.nb = wx.Notebook(self)
471 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
472 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
474 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
475 self._addSettingsToPanels('basic', left, right)
476 self.SizeLabelWidths(left, right)
478 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
479 self._addSettingsToPanels('advanced', left, right)
480 self.SizeLabelWidths(left, right)
483 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
484 if len(self.pluginPanel.pluginList) > 0:
485 self.nb.AddPage(self.pluginPanel, "Plugins")
487 self.pluginPanel.Show(False)
490 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
491 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
493 self.Bind(wx.EVT_SIZE, self.OnSize)
495 self.nb.SetSize(self.GetSize())
496 self.UpdateSize(self.printPanel)
497 self.UpdateSize(self.advancedPanel)
499 def _addSettingsToPanels(self, category, left, right):
500 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
504 for title in profile.getSubCategoriesFor(category):
505 n += 1 + len(profile.getSettingsForCategory(category, title))
508 configBase.TitleRow(p, title)
509 for s in profile.getSettingsForCategory(category, title):
510 if s.checkConditions():
511 configBase.SettingRow(p, s.getName())
513 def SizeLabelWidths(self, left, right):
514 leftWidth = self.getLabelColumnWidth(left)
515 rightWidth = self.getLabelColumnWidth(right)
516 maxWidth = max(leftWidth, rightWidth)
517 self.setLabelColumnWidth(left, maxWidth)
518 self.setLabelColumnWidth(right, maxWidth)
521 # Make the size of the Notebook control the same size as this control
522 self.nb.SetSize(self.GetSize())
524 # Propegate the OnSize() event (just in case)
527 # Perform out resize magic
528 self.UpdateSize(self.printPanel)
529 self.UpdateSize(self.advancedPanel)
531 def UpdateSize(self, configPanel):
532 sizer = configPanel.GetSizer()
536 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
539 # if width(col1) > (best_width(col1) + best_width(col1)):
540 # switch to horizontal
543 col1 = configPanel.leftPanel
544 colSize1 = col1.GetSize()
545 colBestSize1 = col1.GetBestSize()
546 col2 = configPanel.rightPanel
547 colSize2 = col2.GetSize()
548 colBestSize2 = col2.GetBestSize()
550 orientation = sizer.GetOrientation()
552 if orientation == wx.HORIZONTAL:
553 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
555 sizer = wx.BoxSizer(wx.VERTICAL)
556 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
557 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
558 configPanel.SetSizer(sizer)
564 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
566 sizer = wx.BoxSizer(wx.HORIZONTAL)
567 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
568 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
569 configPanel.SetSizer(sizer)
575 def updateProfileToControls(self):
576 super(normalSettingsPanel, self).updateProfileToControls()
577 self.alterationPanel.updateProfileToControls()
578 self.pluginPanel.updateProfileToControls()