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.tools import batchRun
18 from Cura.gui.util import dropTarget
19 from Cura.gui.tools import minecraftImport
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 - ' + 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)
57 i = self.fileMenu.Append(-1, 'Clear platform')
58 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
60 self.fileMenu.AppendSeparator()
61 i = self.fileMenu.Append(-1, 'Print...\tCTRL+P')
62 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
63 i = self.fileMenu.Append(-1, 'Save GCode...')
64 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
65 i = self.fileMenu.Append(-1, 'Show slice engine log...')
66 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
68 self.fileMenu.AppendSeparator()
69 i = self.fileMenu.Append(-1, 'Open Profile...')
70 self.normalModeOnlyItems.append(i)
71 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
72 i = self.fileMenu.Append(-1, 'Save Profile...')
73 self.normalModeOnlyItems.append(i)
74 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
75 i = self.fileMenu.Append(-1, 'Load Profile from GCode...')
76 self.normalModeOnlyItems.append(i)
77 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
78 self.fileMenu.AppendSeparator()
79 i = self.fileMenu.Append(-1, 'Reset Profile to default')
80 self.normalModeOnlyItems.append(i)
81 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
83 self.fileMenu.AppendSeparator()
84 i = self.fileMenu.Append(-1, 'Preferences...\tCTRL+,')
85 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
86 self.fileMenu.AppendSeparator()
89 modelHistoryMenu = wx.Menu()
90 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Model Files", modelHistoryMenu)
91 self.modelFileHistory.UseMenu(modelHistoryMenu)
92 self.modelFileHistory.AddFilesToMenu()
93 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
96 profileHistoryMenu = wx.Menu()
97 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Profile Files", profileHistoryMenu)
98 self.profileFileHistory.UseMenu(profileHistoryMenu)
99 self.profileFileHistory.AddFilesToMenu()
100 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
102 self.fileMenu.AppendSeparator()
103 i = self.fileMenu.Append(wx.ID_EXIT, 'Quit')
104 self.Bind(wx.EVT_MENU, self.OnQuit, i)
105 self.menubar.Append(self.fileMenu, '&File')
107 toolsMenu = wx.Menu()
108 i = toolsMenu.Append(-1, 'Switch to quickprint...')
109 self.switchToQuickprintMenuItem = i
110 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
111 i = toolsMenu.Append(-1, 'Switch to full settings...')
112 self.switchToNormalMenuItem = i
113 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
114 toolsMenu.AppendSeparator()
115 #i = toolsMenu.Append(-1, 'Batch run...')
116 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
117 #self.normalModeOnlyItems.append(i)
118 if minecraftImport.hasMinecraft():
119 i = toolsMenu.Append(-1, 'Minecraft import...')
120 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
121 self.menubar.Append(toolsMenu, 'Tools')
123 expertMenu = wx.Menu()
124 i = expertMenu.Append(-1, 'Open expert settings...')
125 self.normalModeOnlyItems.append(i)
126 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
127 expertMenu.AppendSeparator()
128 if firmwareInstall.getDefaultFirmware() is not None:
129 i = expertMenu.Append(-1, 'Install default Marlin firmware')
130 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
131 i = expertMenu.Append(-1, 'Install custom firmware')
132 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
133 expertMenu.AppendSeparator()
134 i = expertMenu.Append(-1, 'Run first run wizard...')
135 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
136 i = expertMenu.Append(-1, 'Run bed leveling wizard...')
137 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
138 if self.extruderCount > 1:
139 i = expertMenu.Append(-1, 'Run head offset wizard...')
140 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
141 self.menubar.Append(expertMenu, 'Expert')
144 i = helpMenu.Append(-1, 'Online documentation...')
145 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
146 i = helpMenu.Append(-1, 'Report a problem...')
147 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
148 i = helpMenu.Append(-1, 'Check for update...')
149 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
150 i = helpMenu.Append(-1, 'About Cura...')
151 self.Bind(wx.EVT_MENU, self.OnAbout, i)
152 self.menubar.Append(helpMenu, 'Help')
153 self.SetMenuBar(self.menubar)
155 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
156 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
157 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
158 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
161 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
162 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
164 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
165 self.leftSizer.Add(self.simpleSettingsPanel)
166 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
167 self.leftPane.SetSizer(self.leftSizer)
170 self.scene = sceneView.SceneView(self.rightPane)
172 #Main sizer, to position the preview window, buttons and tab control
173 sizer = wx.BoxSizer()
174 self.rightPane.SetSizer(sizer)
175 sizer.Add(self.scene, 1, flag=wx.EXPAND)
178 sizer = wx.BoxSizer(wx.VERTICAL)
180 sizer.Add(self.splitter, 1, wx.EXPAND)
184 self.updateProfileToControls()
186 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
188 self.simpleSettingsPanel.Show(False)
189 self.normalSettingsPanel.Show(False)
191 # Set default window size & position
192 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
195 # Restore the window position, size & state from the preferences file
197 if profile.getPreference('window_maximized') == 'True':
200 posx = int(profile.getPreference('window_pos_x'))
201 posy = int(profile.getPreference('window_pos_y'))
202 width = int(profile.getPreference('window_width'))
203 height = int(profile.getPreference('window_height'))
204 if posx > 0 or posy > 0:
205 self.SetPosition((posx,posy))
206 if width > 0 and height > 0:
207 self.SetSize((width,height))
209 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
211 self.normalSashPos = 0
213 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
214 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
216 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
218 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
220 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
222 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
223 self.SetSize((800,600))
226 self.updateSliceMode()
228 def updateSliceMode(self):
229 isSimple = profile.getPreference('startMode') == 'Simple'
231 self.normalSettingsPanel.Show(not isSimple)
232 self.simpleSettingsPanel.Show(isSimple)
233 self.leftPane.Layout()
235 for i in self.normalModeOnlyItems:
236 i.Enable(not isSimple)
237 self.switchToQuickprintMenuItem.Enable(not isSimple)
238 self.switchToNormalMenuItem.Enable(isSimple)
240 # Set splitter sash position & size
242 # Save normal mode sash
243 self.normalSashPos = self.splitter.GetSashPosition()
245 # Change location of sash to width of quick mode pane
246 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
247 self.splitter.SetSashPosition(width, True)
250 self.splitter.SetSashSize(0)
252 self.splitter.SetSashPosition(self.normalSashPos, True)
254 self.splitter.SetSashSize(4)
255 self.scene.updateProfileToControls()
257 def OnPreferences(self, e):
258 prefDialog = preferencesDialog.preferencesDialog(self)
262 def OnDropFiles(self, files):
264 profile.setPluginConfig([])
265 self.updateProfileToControls()
266 self.scene.loadScene(files)
268 def OnModelMRU(self, e):
269 fileNum = e.GetId() - self.ID_MRU_MODEL1
270 path = self.modelFileHistory.GetHistoryFile(fileNum)
272 self.modelFileHistory.AddFileToHistory(path) # move up the list
273 self.config.SetPath("/ModelMRU")
274 self.modelFileHistory.Save(self.config)
277 profile.putPreference('lastFile', path)
279 self.scene.loadScene(filelist)
281 def addToModelMRU(self, file):
282 self.modelFileHistory.AddFileToHistory(file)
283 self.config.SetPath("/ModelMRU")
284 self.modelFileHistory.Save(self.config)
287 def OnProfileMRU(self, e):
288 fileNum = e.GetId() - self.ID_MRU_PROFILE1
289 path = self.profileFileHistory.GetHistoryFile(fileNum)
291 self.profileFileHistory.AddFileToHistory(path) # move up the list
292 self.config.SetPath("/ProfileMRU")
293 self.profileFileHistory.Save(self.config)
296 profile.loadProfile(path)
297 self.updateProfileToControls()
299 def addToProfileMRU(self, file):
300 self.profileFileHistory.AddFileToHistory(file)
301 self.config.SetPath("/ProfileMRU")
302 self.profileFileHistory.Save(self.config)
305 def updateProfileToControls(self):
306 self.scene.updateProfileToControls()
307 self.normalSettingsPanel.updateProfileToControls()
308 self.simpleSettingsPanel.updateProfileToControls()
310 def OnLoadProfile(self, e):
311 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)
312 dlg.SetWildcard("ini files (*.ini)|*.ini")
313 if dlg.ShowModal() == wx.ID_OK:
314 profileFile = dlg.GetPath()
315 profile.loadProfile(profileFile)
316 self.updateProfileToControls()
318 # Update the Profile MRU
319 self.addToProfileMRU(profileFile)
322 def OnLoadProfileFromGcode(self, e):
323 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)
324 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
325 if dlg.ShowModal() == wx.ID_OK:
326 gcodeFile = dlg.GetPath()
327 f = open(gcodeFile, 'r')
330 if line.startswith(';CURA_PROFILE_STRING:'):
331 profile.loadProfileFromString(line[line.find(':')+1:].strip())
334 self.updateProfileToControls()
336 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)
339 def OnSaveProfile(self, e):
340 dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
341 dlg.SetWildcard("ini files (*.ini)|*.ini")
342 if dlg.ShowModal() == wx.ID_OK:
343 profileFile = dlg.GetPath()
344 profile.saveProfile(profileFile)
347 def OnResetProfile(self, e):
348 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)
349 result = dlg.ShowModal() == wx.ID_YES
352 profile.resetProfile()
353 self.updateProfileToControls()
355 def OnBatchRun(self, e):
356 br = batchRun.batchRunWindow(self)
360 def OnSimpleSwitch(self, e):
361 profile.putPreference('startMode', 'Simple')
362 self.updateSliceMode()
364 def OnNormalSwitch(self, e):
365 profile.putPreference('startMode', 'Normal')
366 self.updateSliceMode()
368 def OnDefaultMarlinFirmware(self, e):
369 firmwareInstall.InstallFirmware()
371 def OnCustomFirmware(self, e):
372 if profile.getPreference('machine_type') == 'ultimaker':
373 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)
374 dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
375 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
376 if dlg.ShowModal() == wx.ID_OK:
377 filename = dlg.GetPath()
378 if not(os.path.exists(filename)):
380 #For some reason my Ubuntu 10.10 crashes here.
381 firmwareInstall.InstallFirmware(filename)
383 def OnFirstRunWizard(self, e):
384 configWizard.configWizard()
385 self.updateProfileToControls()
387 def OnBedLevelWizard(self, e):
388 configWizard.bedLevelWizard()
390 def OnHeadOffsetWizard(self, e):
391 configWizard.headOffsetWizard()
393 def OnExpertOpen(self, e):
394 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
398 def OnMinecraftImport(self, e):
399 mi = minecraftImport.minecraftImportWindow(self)
403 def OnCheckForUpdate(self, e):
404 newVersion = version.checkForNewerVersion()
405 if newVersion is not None:
406 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:
407 webbrowser.open(newVersion)
409 wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
411 def OnAbout(self, e):
412 info = wx.AboutDialogInfo()
414 info.SetDescription('End solution for Open Source Fused Filament Fabrication 3D printing.')
415 info.SetWebSite('http://software.ultimaker.com/')
416 info.SetCopyright('Copyright (C) David Braam')
418 This program is free software: you can redistribute it and/or modify
419 it under the terms of the GNU Affero General Public License as published by
420 the Free Software Foundation, either version 3 of the License, or
421 (at your option) any later version.
423 This program is distributed in the hope that it will be useful,
424 but WITHOUT ANY WARRANTY; without even the implied warranty of
425 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
426 GNU Affero General Public License for more details.
428 You should have received a copy of the GNU Affero General Public License
429 along with this program. If not, see <http://www.gnu.org/licenses/>.
433 def OnClose(self, e):
434 profile.saveProfile(profile.getDefaultProfilePath())
436 # Save the window position, size & state from the preferences file
437 profile.putPreference('window_maximized', self.IsMaximized())
438 if not self.IsMaximized() and not self.IsIconized():
439 (posx, posy) = self.GetPosition()
440 profile.putPreference('window_pos_x', posx)
441 profile.putPreference('window_pos_y', posy)
442 (width, height) = self.GetSize()
443 profile.putPreference('window_width', width)
444 profile.putPreference('window_height', height)
446 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
447 isSimple = profile.getPreference('startMode') == 'Simple'
449 self.normalSashPos = self.splitter.GetSashPosition()
450 profile.putPreference('window_normal_sash', self.normalSashPos)
452 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
454 self.scene.OnPaint = lambda e : e
455 self.scene._slicer.cleanup()
461 class normalSettingsPanel(configBase.configPanelBase):
462 "Main user interface window"
463 def __init__(self, parent, callback = None):
464 super(normalSettingsPanel, self).__init__(parent, callback)
467 self.nb = wx.Notebook(self)
468 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
469 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
471 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
472 self._addSettingsToPanels('basic', left, right)
473 self.SizeLabelWidths(left, right)
475 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
476 self._addSettingsToPanels('advanced', left, right)
477 self.SizeLabelWidths(left, right)
480 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
481 if len(self.pluginPanel.pluginList) > 0:
482 self.nb.AddPage(self.pluginPanel, "Plugins")
484 self.pluginPanel.Show(False)
487 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
488 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
490 self.Bind(wx.EVT_SIZE, self.OnSize)
492 self.nb.SetSize(self.GetSize())
493 self.UpdateSize(self.printPanel)
494 self.UpdateSize(self.advancedPanel)
496 def _addSettingsToPanels(self, category, left, right):
497 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
501 for title in profile.getSubCategoriesFor(category):
502 n += 1 + len(profile.getSettingsForCategory(category, title))
505 configBase.TitleRow(p, title)
506 for s in profile.getSettingsForCategory(category, title):
507 if s.checkConditions():
508 configBase.SettingRow(p, s.getName())
510 def SizeLabelWidths(self, left, right):
511 leftWidth = self.getLabelColumnWidth(left)
512 rightWidth = self.getLabelColumnWidth(right)
513 maxWidth = max(leftWidth, rightWidth)
514 self.setLabelColumnWidth(left, maxWidth)
515 self.setLabelColumnWidth(right, maxWidth)
518 # Make the size of the Notebook control the same size as this control
519 self.nb.SetSize(self.GetSize())
521 # Propegate the OnSize() event (just in case)
524 # Perform out resize magic
525 self.UpdateSize(self.printPanel)
526 self.UpdateSize(self.advancedPanel)
528 def UpdateSize(self, configPanel):
529 sizer = configPanel.GetSizer()
533 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
536 # if width(col1) > (best_width(col1) + best_width(col1)):
537 # switch to horizontal
540 col1 = configPanel.leftPanel
541 colSize1 = col1.GetSize()
542 colBestSize1 = col1.GetBestSize()
543 col2 = configPanel.rightPanel
544 colSize2 = col2.GetSize()
545 colBestSize2 = col2.GetBestSize()
547 orientation = sizer.GetOrientation()
549 if orientation == wx.HORIZONTAL:
550 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
552 sizer = wx.BoxSizer(wx.VERTICAL)
553 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
554 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
555 configPanel.SetSizer(sizer)
561 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
563 sizer = wx.BoxSizer(wx.HORIZONTAL)
564 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
565 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
566 configPanel.SetSizer(sizer)
572 def updateProfileToControls(self):
573 super(normalSettingsPanel, self).updateProfileToControls()
574 self.alterationPanel.updateProfileToControls()
575 self.pluginPanel.updateProfileToControls()