chiark / gitweb /
Move the validators from gui to util, they do not have gui specific code.
[cura.git] / Cura / gui / mainWindow.py
1 from __future__ import absolute_import
2 import __init__
3
4 import wx, os, platform, types, webbrowser
5
6 from gui import configBase
7 from gui import expertConfig
8 from gui import preview3d
9 from gui import sliceProgessPanel
10 from gui import alterationPanel
11 from gui import preferencesDialog
12 from gui import configWizard
13 from gui import firmwareInstall
14 from gui import printWindow
15 from gui import simpleMode
16 from gui import projectPlanner
17 from gui import batchRun
18 from gui import flatSlicerWindow
19 from gui import icon
20 from gui import dropTarget
21 from util import validators
22 from util import profile
23 from util import version
24 from util import sliceRun
25
26 def main():
27         #app = wx.App(False)
28         if profile.getPreference('wizardDone') == 'False':
29                 configWizard.configWizard()
30                 profile.putPreference("wizardDone", "True")
31         if profile.getPreference('startMode') == 'Simple':
32                 simpleMode.simpleModeWindow()
33         else:
34                 mainWindow()
35         #app.MainLoop()
36
37 class mainWindow(configBase.configWindowBase):
38         "Main user interface window"
39         def __init__(self):
40                 super(mainWindow, self).__init__(title='Cura - ' + version.getVersion())
41                 
42                 wx.EVT_CLOSE(self, self.OnClose)
43                 #self.SetIcon(icon.getMainIcon())
44                 
45                 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, '.stl'))
46                 
47                 menubar = wx.MenuBar()
48                 fileMenu = wx.Menu()
49                 i = fileMenu.Append(-1, 'Load model file...')
50                 self.Bind(wx.EVT_MENU, lambda e: self._showModelLoadDialog(1), i)
51                 fileMenu.AppendSeparator()
52                 i = fileMenu.Append(-1, 'Open Profile...')
53                 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
54                 i = fileMenu.Append(-1, 'Save Profile...')
55                 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
56                 i = fileMenu.Append(-1, 'Load Profile from GCode...')
57                 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
58                 fileMenu.AppendSeparator()
59                 i = fileMenu.Append(-1, 'Reset Profile to default')
60                 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
61                 fileMenu.AppendSeparator()
62                 i = fileMenu.Append(-1, 'Batch run...')
63                 self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
64                 i = fileMenu.Append(-1, 'Preferences...')
65                 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
66                 fileMenu.AppendSeparator()
67                 i = fileMenu.Append(-1, 'Open project planner...')
68                 self.Bind(wx.EVT_MENU, self.OnProjectPlanner, i)
69                 fileMenu.AppendSeparator()
70                 i = fileMenu.Append(wx.ID_EXIT, 'Quit')
71                 self.Bind(wx.EVT_MENU, self.OnQuit, i)
72                 menubar.Append(fileMenu, '&File')
73                 
74                 simpleMenu = wx.Menu()
75                 i = simpleMenu.Append(-1, 'Switch to Quickprint...')
76                 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
77                 menubar.Append(simpleMenu, 'Simple')
78                 
79                 expertMenu = wx.Menu()
80                 i = expertMenu.Append(-1, 'Open expert settings...')
81                 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
82 #               i = expertMenu.Append(-1, 'Open SVG (2D) slicer...')
83 #               self.Bind(wx.EVT_MENU, self.OnSVGSlicerOpen, i)
84                 expertMenu.AppendSeparator()
85                 i = expertMenu.Append(-1, 'Install default Marlin firmware')
86                 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
87                 i = expertMenu.Append(-1, 'Install custom firmware')
88                 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
89                 expertMenu.AppendSeparator()
90                 i = expertMenu.Append(-1, 'ReRun first run wizard...')
91                 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
92                 menubar.Append(expertMenu, 'Expert')
93                 
94                 helpMenu = wx.Menu()
95                 i = helpMenu.Append(-1, 'Online documentation...')
96                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/wiki'), i)
97                 i = helpMenu.Append(-1, 'Report a problem...')
98                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
99                 menubar.Append(helpMenu, 'Help')
100                 self.SetMenuBar(menubar)
101                 
102                 if profile.getPreference('lastFile') != '':
103                         self.filelist = profile.getPreference('lastFile').split(';')
104                         self.SetTitle(self.filelist[-1] + ' - Cura - ' + version.getVersion())
105                 else:
106                         self.filelist = []
107                 self.progressPanelList = []
108
109                 #Preview window
110                 self.preview3d = preview3d.previewPanel(self)
111
112                 #Main tabs
113                 nb = wx.Notebook(self)
114                 
115                 (left, right) = self.CreateConfigTab(nb, 'Print config')
116                 
117                 configBase.TitleRow(left, "Accuracy")
118                 c = configBase.SettingRow(left, "Layer height (mm)", 'layer_height', '0.2', 'Layer height in millimeters.\n0.2 is a good value for quick prints.\n0.1 gives high quality prints.')
119                 validators.validFloat(c, 0.0001)
120                 validators.warningAbove(c, lambda : (float(profile.getProfileSetting('nozzle_size')) * 80.0 / 100.0), "Thicker layers then %.2fmm (80%% nozzle size) usually give bad results and are not recommended.")
121                 c = configBase.SettingRow(left, "Wall thickness (mm)", 'wall_thickness', '0.8', 'Thickness of the walls.\nThis is used in combination with the nozzle size to define the number\nof perimeter lines and the thickness of those perimeter lines.')
122                 validators.validFloat(c, 0.0001)
123                 validators.wallThicknessValidator(c)
124                 
125                 configBase.TitleRow(left, "Fill")
126                 c = configBase.SettingRow(left, "Bottom/Top thickness (mm)", 'solid_layer_thickness', '0.6', 'This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value.\nHaving this value a multiply of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.')
127                 validators.validFloat(c, 0.0)
128                 c = configBase.SettingRow(left, "Fill Density (%)", 'fill_density', '20', 'This controls how densily filled the insides of your print will be. For a solid part use 100%, for an empty part use 0%. A value around 20% is usually enough')
129                 validators.validFloat(c, 0.0, 100.0)
130                 
131                 configBase.TitleRow(left, "Skirt")
132                 c = configBase.SettingRow(left, "Line count", 'skirt_line_count', '1', 'The skirt is a line drawn around the object at the first layer. This helps to prime your extruder, and to see if the object fits on your platform.\nSetting this to 0 will disable the skirt. Multiple skirt lines can help priming your extruder better for small objects.')
133                 validators.validInt(c, 0, 10)
134                 c = configBase.SettingRow(left, "Start distance (mm)", 'skirt_gap', '6.0', 'The distance between the skirt and the first layer.\nThis is the minimal distance, multiple skirt lines will be put outwards from this distance.')
135                 validators.validFloat(c, 0.0)
136
137                 configBase.TitleRow(right, "Speed && Temperature")
138                 c = configBase.SettingRow(right, "Print speed (mm/s)", 'print_speed', '50', 'Speed at which printing happens. A well adjusted Ultimaker can reach 150mm/s, but for good quality prints you want to print slower. Printing speed depends on a lot of factors. So you will be experimenting with optimal settings for this.')
139                 validators.validFloat(c, 1.0)
140                 validators.warningAbove(c, 150.0, "It is highly unlikely that your machine can achieve a printing speed above 150mm/s")
141                 validators.printSpeedValidator(c)
142                 
143                 #configBase.TitleRow(right, "Temperature")
144                 c = configBase.SettingRow(right, "Printing temperature", 'print_temperature', '0', 'Temperature used for printing. Set at 0 to pre-heat yourself')
145                 validators.validFloat(c, 0.0, 340.0)
146                 validators.warningAbove(c, 260.0, "Temperatures above 260C could damage your machine, be careful!")
147                 if profile.getPreference('has_heated_bed') == 'True':
148                         c = configBase.SettingRow(right, "Bed temperature", 'print_bed_temperature', '0', 'Temperature used for the heated printer bed. Set at 0 to pre-heat yourself')
149                         validators.validFloat(c, 0.0, 340.0)
150                 
151                 configBase.TitleRow(right, "Support")
152                 c = configBase.SettingRow(right, "Support type", 'support', ['None', 'Exterior Only', 'Everywhere', 'Empty Layers Only'], 'Type of support structure build.\n"Exterior only" is the most commonly used support setting.\n\nNone does not do any support.\nExterior only only creates support on the outside.\nEverywhere creates support even on the insides of the model.\nOnly on empty layers is for stacked objects.')
153                 c = configBase.SettingRow(right, "Add raft", 'enable_raft', False, 'A raft is a few layers of lines below the bottom of the object. It prevents warping. Full raft settings can be found in the expert settings.\nFor PLA this is usually not required. But if you print with ABS it is almost required.')
154
155                 configBase.TitleRow(right, "Filament")
156                 c = configBase.SettingRow(right, "Diameter (mm)", 'filament_diameter', '2.89', 'Diameter of your filament, as accurately as possible.\nIf you cannot measure this value you will have to callibrate it, a higher number means less extrusion, a smaller number generates more extrusion.')
157                 validators.validFloat(c, 1.0)
158                 validators.warningAbove(c, 3.5, "Are you sure your filament is that thick? Normal filament is around 3mm or 1.75mm.")
159                 c = configBase.SettingRow(right, "Packing Density", 'filament_density', '1.00', 'Packing density of your filament. This should be 1.00 for PLA and 0.85 for ABS')
160                 validators.validFloat(c, 0.5, 1.5)
161                 
162                 (left, right) = self.CreateConfigTab(nb, 'Advanced config')
163                 
164                 configBase.TitleRow(left, "Machine size")
165                 c = configBase.SettingRow(left, "Nozzle size (mm)", 'nozzle_size', '0.4', 'The nozzle size is very important, this is used to calculate the line width of the infill, and used to calculate the amount of outside wall lines and thickness for the wall thickness you entered in the print settings.')
166                 validators.validFloat(c, 0.1, 10.0)
167                 c = configBase.SettingRow(left, "Machine center X (mm)", 'machine_center_x', '100', 'The center of your machine, your print will be placed at this location')
168                 validators.validInt(c, 10)
169                 configBase.settingNotify(c, self.preview3d.updateCenterX)
170                 c = configBase.SettingRow(left, "Machine center Y (mm)", 'machine_center_y', '100', 'The center of your machine, your print will be placed at this location')
171                 validators.validInt(c, 10)
172                 configBase.settingNotify(c, self.preview3d.updateCenterY)
173
174                 configBase.TitleRow(left, "Retraction")
175                 c = configBase.SettingRow(left, "Minimal travel (mm)", 'retraction_min_travel', '5.0', 'Minimal amount of travel needed for a retraction to happen at all. To make sure you do not get a lot of retractions in a small area')
176                 validators.validFloat(c, 0.0)
177                 c = configBase.SettingRow(left, "Speed (mm/s)", 'retraction_speed', '40.0', 'Speed at which the filament is retracted, a higher retraction speed works better. But a very high retraction speed can lead to filament grinding.')
178                 validators.validFloat(c, 0.1)
179                 c = configBase.SettingRow(left, "Distance (mm)", 'retraction_amount', '0.0', 'Amount of retraction, set at 0 for no retraction at all. A value of 2.0mm seems to generate good results.')
180                 validators.validFloat(c, 0.0)
181                 c = configBase.SettingRow(left, "Extra length on start (mm)", 'retraction_extra', '0.0', 'Extra extrusion amount when restarting after a retraction, to better "Prime" your extruder after retraction.')
182                 validators.validFloat(c, 0.0)
183
184                 configBase.TitleRow(right, "Speed")
185                 c = configBase.SettingRow(right, "Travel speed (mm/s)", 'travel_speed', '150', 'Speed at which travel moves are done, a high quality build Ultimaker can reach speeds of 250mm/s. But some machines might miss steps then.')
186                 validators.validFloat(c, 1.0)
187                 validators.warningAbove(c, 300.0, "It is highly unlikely that your machine can achieve a travel speed above 300mm/s")
188                 c = configBase.SettingRow(right, "Max Z speed (mm/s)", 'max_z_speed', '1.0', 'Speed at which Z moves are done. When you Z axis is properly lubercated you can increase this for less Z blob.')
189                 validators.validFloat(c, 0.5)
190                 c = configBase.SettingRow(right, "Bottom layer speed (mm/s)", 'bottom_layer_speed', '25', 'Print speed for the bottom layer, you want to print the first layer slower so it sticks better to the printer bed.')
191                 validators.validFloat(c, 0.0)
192
193                 configBase.TitleRow(right, "Cool")
194                 c = configBase.SettingRow(right, "Minimal layer time (sec)", 'cool_min_layer_time', '10', 'Minimum time spend in a layer, gives the layer time to cool down before the next layer is put on top. If the layer will be placed down too fast the printer will slow down to make sure it has spend atleast this amount of seconds printing this layer.')
195                 validators.validFloat(c, 0.0)
196                 c = configBase.SettingRow(right, "Enable cooling fan", 'fan_enabled', True, 'Enable the cooling fan during the print. The extra cooling from the cooling fan is essensial during faster prints.')
197
198                 configBase.TitleRow(right, "Accuracy")
199                 c = configBase.SettingRow(right, "Initial layer thickness (mm)", 'bottom_thickness', '0.0', 'Layer thickness of the bottom layer. A thicker bottom layer makes sticking to the bed easier. Set to 0.0 to have the bottom layer thickness the same as the other layers.')
200                 validators.validFloat(c, 0.0)
201                 validators.warningAbove(c, lambda : (float(profile.getProfileSetting('nozzle_size')) * 3.0 / 4.0), "A bottom layer of more then %.2fmm (3/4 nozzle size) usually give bad results and is not recommended.")
202                 c = configBase.SettingRow(right, "Enable 'skin'", 'enable_skin', False, 'Skin prints the outer lines of the prints twice, each time with half the thickness. This gives the illusion of a higher print quality.')
203
204                 self.alterationPanel = alterationPanel.alterationPanel(nb)
205                 nb.AddPage(self.alterationPanel, "Start/End-GCode")
206
207                 # load and slice buttons.
208                 loadButton = wx.Button(self, -1, 'Load Model')
209                 sliceButton = wx.Button(self, -1, 'Prepare print')
210                 printButton = wx.Button(self, -1, 'Print')
211                 self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(1), loadButton)
212                 self.Bind(wx.EVT_BUTTON, self.OnSlice, sliceButton)
213                 self.Bind(wx.EVT_BUTTON, self.OnPrint, printButton)
214
215                 extruderCount = int(profile.getPreference('extruder_amount'))
216                 if extruderCount > 1:
217                         loadButton2 = wx.Button(self, -1, 'Load Dual')
218                         self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(2), loadButton2)
219                 if extruderCount > 2:
220                         loadButton3 = wx.Button(self, -1, 'Load Triple')
221                         self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(3), loadButton3)
222                 if extruderCount > 3:
223                         loadButton4 = wx.Button(self, -1, 'Load Quad')
224                         self.Bind(wx.EVT_BUTTON, lambda e: self._showModelLoadDialog(4), loadButton4)
225
226                 #Also bind double clicking the 3D preview to load an STL file.
227                 self.preview3d.glCanvas.Bind(wx.EVT_LEFT_DCLICK, lambda e: self._showModelLoadDialog(1), self.preview3d.glCanvas)
228
229                 #Main sizer, to position the preview window, buttons and tab control
230                 sizer = wx.GridBagSizer()
231                 self.SetSizer(sizer)
232                 sizer.Add(nb, (0,0), span=(1,1), flag=wx.EXPAND)
233                 sizer.Add(self.preview3d, (0,1), span=(1,2+extruderCount), flag=wx.EXPAND)
234                 sizer.AddGrowableCol(2 + extruderCount)
235                 sizer.AddGrowableRow(0)
236                 sizer.Add(loadButton, (1,1), flag=wx.RIGHT, border=5)
237                 if extruderCount > 1:
238                         sizer.Add(loadButton2, (1,2), flag=wx.RIGHT, border=5)
239                 if extruderCount > 2:
240                         sizer.Add(loadButton3, (1,3), flag=wx.RIGHT, border=5)
241                 if extruderCount > 3:
242                         sizer.Add(loadButton4, (1,4), flag=wx.RIGHT, border=5)
243                 sizer.Add(sliceButton, (1,1+extruderCount), flag=wx.RIGHT, border=5)
244                 sizer.Add(printButton, (1,2+extruderCount), flag=wx.RIGHT, border=5)
245                 self.sizer = sizer
246
247                 if len(self.filelist) > 0:
248                         self.preview3d.loadModelFiles(self.filelist)
249
250                 self.updateProfileToControls()
251
252                 self.Fit()
253                 if wx.Display().GetClientArea().GetWidth() < self.GetSize().GetWidth():
254                         f = self.GetSize().GetWidth() - wx.Display().GetClientArea().GetWidth()
255                         self.preview3d.SetMinSize(self.preview3d.GetMinSize().DecBy(f, 0))
256                         self.Fit()
257                 self.SetMinSize(self.GetSize())
258                 self.Centre()
259                 self.Show(True)
260         
261         def OnLoadProfile(self, e):
262                 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)
263                 dlg.SetWildcard("ini files (*.ini)|*.ini")
264                 if dlg.ShowModal() == wx.ID_OK:
265                         profileFile = dlg.GetPath()
266                         profile.loadGlobalProfile(profileFile)
267                         self.updateProfileToControls()
268                 dlg.Destroy()
269
270         def OnLoadProfileFromGcode(self, e):
271                 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)
272                 dlg.SetWildcard("gcode files (*.gcode)|*.gcode")
273                 if dlg.ShowModal() == wx.ID_OK:
274                         gcodeFile = dlg.GetPath()
275                         f = open(gcodeFile, 'r')
276                         hasProfile = False
277                         for line in f:
278                                 if line.startswith(';CURA_PROFILE_STRING:'):
279                                         profile.loadGlobalProfileFromString(line[line.find(':')+1:].strip())
280                                         hasProfile = True
281                         if hasProfile:
282                                 self.updateProfileToControls()
283                         else:
284                                 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)
285                 dlg.Destroy()
286         
287         def OnSaveProfile(self, e):
288                 dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
289                 dlg.SetWildcard("ini files (*.ini)|*.ini")
290                 if dlg.ShowModal() == wx.ID_OK:
291                         profileFile = dlg.GetPath()
292                         profile.saveGlobalProfile(profileFile)
293                 dlg.Destroy()
294         
295         def OnResetProfile(self, e):
296                 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)
297                 result = dlg.ShowModal() == wx.ID_YES
298                 dlg.Destroy()
299                 if result:
300                         profile.resetGlobalProfile()
301                         if profile.getPreference('machine_type') == 'reprap':
302                                 profile.putProfileSetting('nozzle_size', '0.5')
303                                 profile.putProfileSetting('machine_center_x', '40')
304                                 profile.putProfileSetting('machine_center_y', '40')
305                         self.updateProfileToControls()
306         
307         def OnBatchRun(self, e):
308                 br = batchRun.batchRunWindow(self)
309                 br.Centre()
310                 br.Show(True)
311         
312         def OnPreferences(self, e):
313                 prefDialog = preferencesDialog.preferencesDialog(self)
314                 prefDialog.Centre()
315                 prefDialog.Show(True)
316         
317         def OnSimpleSwitch(self, e):
318                 profile.putPreference('startMode', 'Simple')
319                 simpleMode.simpleModeWindow()
320                 self.Close()
321         
322         def OnDefaultMarlinFirmware(self, e):
323                 firmwareInstall.InstallFirmware()
324
325         def OnCustomFirmware(self, e):
326                 dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
327                 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
328                 if dlg.ShowModal() == wx.ID_OK:
329                         filename = dlg.GetPath()
330                         if not(os.path.exists(filename)):
331                                 return
332                         #For some reason my Ubuntu 10.10 crashes here.
333                         firmwareInstall.InstallFirmware(filename)
334
335         def OnFirstRunWizard(self, e):
336                 configWizard.configWizard()
337                 self.updateProfileToControls()
338
339         def _showOpenDialog(self, title, wildcard = "STL files (*.stl)|*.stl;*.STL"):
340                 dlg=wx.FileDialog(self, title, os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
341                 dlg.SetWildcard(wildcard)
342                 if dlg.ShowModal() == wx.ID_OK:
343                         filename = dlg.GetPath()
344                         dlg.Destroy()
345                         if not(os.path.exists(filename)):
346                                 return False
347                         profile.putPreference('lastFile', filename)
348                         return filename
349                 dlg.Destroy()
350                 return False
351
352         def _showModelLoadDialog(self, amount):
353                 filelist = []
354                 for i in xrange(0, amount):
355                         filelist.append(self._showOpenDialog("Open file to print"))
356                         if filelist[-1] == False:
357                                 return
358                 self._loadModels(filelist)
359         
360         def _loadModels(self, filelist):
361                 self.filelist = filelist
362                 self.SetTitle(filelist[-1] + ' - Cura - ' + version.getVersion())
363                 profile.putPreference('lastFile', ';'.join(self.filelist))
364                 self.preview3d.loadModelFiles(self.filelist, True)
365                 self.preview3d.setViewMode("Normal")
366
367         def OnDropFiles(self, filenames):
368                 self._loadModels(filenames)
369
370         def OnLoadModel(self, e):
371                 self._showModelLoadDialog(1)
372         
373         def OnLoadModel2(self, e):
374                 self._showModelLoadDialog(2)
375
376         def OnLoadModel3(self, e):
377                 self._showModelLoadDialog(3)
378
379         def OnLoadModel4(self, e):
380                 self._showModelLoadDialog(4)
381         
382         def OnSlice(self, e):
383                 if len(self.filelist) < 1:
384                         wx.MessageBox('You need to load a file before you can slice it.', 'Print error', wx.OK | wx.ICON_INFORMATION)
385                         return
386                 #Create a progress panel and add it to the window. The progress panel will start the Skein operation.
387                 spp = sliceProgessPanel.sliceProgessPanel(self, self, self.filelist)
388                 self.sizer.Add(spp, (len(self.progressPanelList)+2,0), span=(1,4), flag=wx.EXPAND)
389                 self.sizer.Layout()
390                 newSize = self.GetSize();
391                 newSize.IncBy(0, spp.GetSize().GetHeight())
392                 self.SetSize(newSize)
393                 self.progressPanelList.append(spp)
394         
395         def OnPrint(self, e):
396                 if len(self.filelist) < 1:
397                         wx.MessageBox('You need to load a file and slice it before you can print it.', 'Print error', wx.OK | wx.ICON_INFORMATION)
398                         return
399                 if not os.path.exists(sliceRun.getExportFilename(self.filelist[0])):
400                         wx.MessageBox('You need to slice the file to GCode before you can print it.', 'Print error', wx.OK | wx.ICON_INFORMATION)
401                         return
402                 printWindow.printFile(sliceRun.getExportFilename(self.filelist[0]))
403
404         def OnExpertOpen(self, e):
405                 ecw = expertConfig.expertConfigWindow()
406                 ecw.Centre()
407                 ecw.Show(True)
408         
409         def OnProjectPlanner(self, e):
410                 pp = projectPlanner.projectPlanner()
411                 pp.Centre()
412                 pp.Show(True)
413
414         def OnSVGSlicerOpen(self, e):
415                 svgSlicer = flatSlicerWindow.flatSlicerWindow()
416                 svgSlicer.Centre()
417                 svgSlicer.Show(True)
418
419         def removeSliceProgress(self, spp):
420                 self.progressPanelList.remove(spp)
421                 newSize = self.GetSize();
422                 newSize.IncBy(0, -spp.GetSize().GetHeight())
423                 self.SetSize(newSize)
424                 spp.Show(False)
425                 self.sizer.Detach(spp)
426                 for spp in self.progressPanelList:
427                         self.sizer.Detach(spp)
428                 i = 2
429                 for spp in self.progressPanelList:
430                         self.sizer.Add(spp, (i,0), span=(1,4), flag=wx.EXPAND)
431                         i += 1
432                 self.sizer.Layout()
433
434         def OnQuit(self, e):
435                 self.Close()
436         
437         def OnClose(self, e):
438                 profile.saveGlobalProfile(profile.getDefaultProfilePath())
439                 self.Destroy()
440
441         def updateProfileToControls(self):
442                 super(mainWindow, self).updateProfileToControls()
443                 self.preview3d.updateProfileToControls()
444                 self.alterationPanel.updateProfileToControls()