chiark / gitweb /
Add up/down buttons to allow for Z endstop adjustment.
[cura.git] / Cura / gui / configWizard.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import os
4 import webbrowser
5 import threading
6 import time
7 import math
8
9 import wx
10 import wx.wizard
11
12 from Cura.gui import firmwareInstall
13 from Cura.gui import printWindow
14 from Cura.util import machineCom
15 from Cura.util import profile
16 from Cura.util import gcodeGenerator
17 from Cura.util import resources
18
19
20 class InfoBox(wx.Panel):
21         def __init__(self, parent):
22                 super(InfoBox, self).__init__(parent)
23                 self.SetBackgroundColour('#FFFF80')
24
25                 self.sizer = wx.GridBagSizer(5, 5)
26                 self.SetSizer(self.sizer)
27
28                 self.attentionBitmap = wx.Bitmap(resources.getPathForImage('attention.png'))
29                 self.errorBitmap = wx.Bitmap(resources.getPathForImage('error.png'))
30                 self.readyBitmap = wx.Bitmap(resources.getPathForImage('ready.png'))
31                 self.busyBitmap = [
32                         wx.Bitmap(resources.getPathForImage('busy-0.png')),
33                         wx.Bitmap(resources.getPathForImage('busy-1.png')),
34                         wx.Bitmap(resources.getPathForImage('busy-2.png')),
35                         wx.Bitmap(resources.getPathForImage('busy-3.png'))
36                 ]
37
38                 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
39                 self.text = wx.StaticText(self, -1, '')
40                 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
41                 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
42                 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
43                 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
44                 self.sizer.AddGrowableCol(1)
45
46                 self.extraInfoButton.Show(False)
47
48                 self.extraInfoUrl = ''
49                 self.busyState = None
50                 self.timer = wx.Timer(self)
51                 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
52                 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
53                 self.timer.Start(100)
54
55         def SetInfo(self, info):
56                 self.SetBackgroundColour('#FFFF80')
57                 self.text.SetLabel(info)
58                 self.extraInfoButton.Show(False)
59                 self.Refresh()
60
61         def SetError(self, info, extraInfoUrl):
62                 self.extraInfoUrl = extraInfoUrl
63                 self.SetBackgroundColour('#FF8080')
64                 self.text.SetLabel(info)
65                 self.extraInfoButton.Show(True)
66                 self.Layout()
67                 self.SetErrorIndicator()
68                 self.Refresh()
69
70         def SetAttention(self, info):
71                 self.SetBackgroundColour('#FFFF80')
72                 self.text.SetLabel(info)
73                 self.extraInfoButton.Show(False)
74                 self.SetAttentionIndicator()
75                 self.Layout()
76                 self.Refresh()
77
78         def SetBusy(self, info):
79                 self.SetInfo(info)
80                 self.SetBusyIndicator()
81
82         def SetBusyIndicator(self):
83                 self.busyState = 0
84                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
85
86         def doExtraInfo(self, e):
87                 webbrowser.open(self.extraInfoUrl)
88
89         def doBusyUpdate(self, e):
90                 if self.busyState is None:
91                         return
92                 self.busyState += 1
93                 if self.busyState >= len(self.busyBitmap):
94                         self.busyState = 0
95                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
96
97         def SetReadyIndicator(self):
98                 self.busyState = None
99                 self.bitmap.SetBitmap(self.readyBitmap)
100
101         def SetErrorIndicator(self):
102                 self.busyState = None
103                 self.bitmap.SetBitmap(self.errorBitmap)
104
105         def SetAttentionIndicator(self):
106                 self.busyState = None
107                 self.bitmap.SetBitmap(self.attentionBitmap)
108
109
110 class InfoPage(wx.wizard.WizardPageSimple):
111         def __init__(self, parent, title):
112                 wx.wizard.WizardPageSimple.__init__(self, parent)
113
114                 sizer = wx.GridBagSizer(5, 5)
115                 self.sizer = sizer
116                 self.SetSizer(sizer)
117
118                 title = wx.StaticText(self, -1, title)
119                 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
120                 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
121                 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
122                 sizer.AddGrowableCol(1)
123
124                 self.rowNr = 2
125
126         def AddText(self, info):
127                 text = wx.StaticText(self, -1, info)
128                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
129                 self.rowNr += 1
130                 return text
131
132         def AddSeperator(self):
133                 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
134                 self.rowNr += 1
135
136         def AddHiddenSeperator(self):
137                 self.AddText("")
138
139         def AddInfoBox(self):
140                 infoBox = InfoBox(self)
141                 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
142                 self.rowNr += 1
143                 return infoBox
144
145         def AddRadioButton(self, label, style=0):
146                 radio = wx.RadioButton(self, -1, label, style=style)
147                 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
148                 self.rowNr += 1
149                 return radio
150
151         def AddCheckbox(self, label, checked=False):
152                 check = wx.CheckBox(self, -1)
153                 text = wx.StaticText(self, -1, label)
154                 check.SetValue(checked)
155                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
156                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
157                 self.rowNr += 1
158                 return check
159
160         def AddButton(self, label):
161                 button = wx.Button(self, -1, label)
162                 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
163                 self.rowNr += 1
164                 return button
165
166         def AddDualButton(self, label1, label2):
167                 button1 = wx.Button(self, -1, label1)
168                 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
169                 button2 = wx.Button(self, -1, label2)
170                 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
171                 self.rowNr += 1
172                 return button1, button2
173
174         def AddTextCtrl(self, value):
175                 ret = wx.TextCtrl(self, -1, value)
176                 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
177                 self.rowNr += 1
178                 return ret
179
180         def AddLabelTextCtrl(self, info, value):
181                 text = wx.StaticText(self, -1, info)
182                 ret = wx.TextCtrl(self, -1, value)
183                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
184                 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
185                 self.rowNr += 1
186                 return ret
187
188         def AddTextCtrlButton(self, value, buttonText):
189                 text = wx.TextCtrl(self, -1, value)
190                 button = wx.Button(self, -1, buttonText)
191                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
192                 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
193                 self.rowNr += 1
194                 return text, button
195
196         def AddBitmap(self, bitmap):
197                 bitmap = wx.StaticBitmap(self, -1, bitmap)
198                 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
199                 self.rowNr += 1
200                 return bitmap
201
202         def AddCheckmark(self, label, bitmap):
203                 check = wx.StaticBitmap(self, -1, bitmap)
204                 text = wx.StaticText(self, -1, label)
205                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
206                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
207                 self.rowNr += 1
208                 return check
209
210         def AllowNext(self):
211                 return True
212
213         def AllowBack(self):
214                 return True
215
216         def StoreData(self):
217                 pass
218
219
220 class FirstInfoPage(InfoPage):
221         def __init__(self, parent, addNew):
222                 if addNew:
223                         super(FirstInfoPage, self).__init__(parent, _("Add new machine wizard"))
224                 else:
225                         super(FirstInfoPage, self).__init__(parent, _("First time run wizard"))
226                         self.AddText(_("Welcome, and thanks for trying Cura!"))
227                         self.AddSeperator()
228                 self.AddText(_("This wizard will help you in setting up Cura for your machine."))
229                 # self.AddText(_("This wizard will help you with the following steps:"))
230                 # self.AddText(_("* Configure Cura for your machine"))
231                 # self.AddText(_("* Optionally upgrade your firmware"))
232                 # self.AddText(_("* Optionally check if your machine is working safely"))
233                 # self.AddText(_("* Optionally level your printer bed"))
234
235                 #self.AddText('* Calibrate your machine')
236                 #self.AddText('* Do your first print')
237
238         def AllowBack(self):
239                 return False
240
241
242 class OtherMachineSelectPage(InfoPage):
243         def __init__(self, parent):
244                 super(OtherMachineSelectPage, self).__init__(parent, "Other machine information")
245                 self.AddText(_("The following pre-defined machine profiles are available"))
246                 self.AddText(_("Note that these profiles are not guaranteed to give good results,\nor work at all. Extra tweaks might be required.\nIf you find issues with the predefined profiles,\nor want an extra profile.\nPlease report it at the github issue tracker."))
247                 self.options = []
248                 machines = resources.getDefaultMachineProfiles()
249                 machines.sort()
250                 for filename in machines:
251                         name = os.path.splitext(os.path.basename(filename))[0]
252                         item = self.AddRadioButton(name)
253                         item.filename = filename
254                         item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
255                         self.options.append(item)
256                 self.AddSeperator()
257                 item = self.AddRadioButton('Custom...')
258                 item.SetValue(True)
259                 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
260
261         def OnProfileSelect(self, e):
262                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
263
264         def OnOtherSelect(self, e):
265                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
266
267         def StoreData(self):
268                 for option in self.options:
269                         if option.GetValue():
270                                 profile.loadProfile(option.filename)
271                                 profile.loadMachineSettings(option.filename)
272
273 class OtherMachineInfoPage(InfoPage):
274         def __init__(self, parent):
275                 super(OtherMachineInfoPage, self).__init__(parent, "Cura Ready!")
276                 self.AddText(_("Cura is now ready to be used!"))
277
278 class CustomRepRapInfoPage(InfoPage):
279         def __init__(self, parent):
280                 super(CustomRepRapInfoPage, self).__init__(parent, "Custom RepRap information")
281                 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
282                 self.AddText(_("Be sure to review the default profile before running it on your machine."))
283                 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
284                 self.AddSeperator()
285                 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
286                 self.AddSeperator()
287                 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
288                 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
289                 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
290                 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "55")
291                 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
292                 self.heatedBed = self.AddCheckbox(_("Heated bed"))
293                 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
294
295         def StoreData(self):
296                 profile.putMachineSetting('machine_name', self.machineName.GetValue())
297                 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
298                 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
299                 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
300                 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
301                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
302                 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
303                 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
304                 profile.putMachineSetting('extruder_head_size_min_x', '0')
305                 profile.putMachineSetting('extruder_head_size_min_y', '0')
306                 profile.putMachineSetting('extruder_head_size_max_x', '0')
307                 profile.putMachineSetting('extruder_head_size_max_y', '0')
308                 profile.putMachineSetting('extruder_head_size_height', '0')
309                 profile.checkAndUpdateMachineName()
310
311 class MachineSelectPage(InfoPage):
312         def __init__(self, parent):
313                 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
314                 self.AddText(_("What kind of machine do you have:"))
315
316                 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2", style=wx.RB_GROUP)
317                 self.Ultimaker2Radio.SetValue(True)
318                 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
319                 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
320                 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
321                 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot)"))
322                 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
323                 self.AddSeperator()
324                 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
325                 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
326                 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
327                 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
328                 self.SubmitUserStats.SetValue(True)
329
330         def OnUltimaker2Select(self, e):
331                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
332
333         def OnUltimakerSelect(self, e):
334                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
335
336         def OnOtherSelect(self, e):
337                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
338
339         def AllowNext(self):
340                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
341                 return True
342
343         def StoreData(self):
344                 profile.putProfileSetting('retraction_enable', 'True')
345                 if self.Ultimaker2Radio.GetValue():
346                         profile.putMachineSetting('machine_width', '230')
347                         profile.putMachineSetting('machine_depth', '225')
348                         profile.putMachineSetting('machine_height', '205')
349                         profile.putMachineSetting('machine_name', 'ultimaker2')
350                         profile.putMachineSetting('machine_type', 'ultimaker2')
351                         profile.putMachineSetting('machine_center_is_zero', 'False')
352                         profile.putMachineSetting('has_heated_bed', 'True')
353                         profile.putMachineSetting('gcode_flavor', 'UltiGCode')
354                         profile.putMachineSetting('extruder_head_size_min_x', '40.0')
355                         profile.putMachineSetting('extruder_head_size_min_y', '10.0')
356                         profile.putMachineSetting('extruder_head_size_max_x', '60.0')
357                         profile.putMachineSetting('extruder_head_size_max_y', '30.0')
358                         profile.putMachineSetting('extruder_head_size_height', '55.0')
359                         profile.putProfileSetting('nozzle_size', '0.4')
360                         profile.putProfileSetting('fan_full_height', '5.0')
361                         profile.putMachineSetting('extruder_offset_x1', '18.0')
362                         profile.putMachineSetting('extruder_offset_y1', '0.0')
363                 elif self.UltimakerRadio.GetValue():
364                         profile.putMachineSetting('machine_width', '205')
365                         profile.putMachineSetting('machine_depth', '205')
366                         profile.putMachineSetting('machine_height', '200')
367                         profile.putMachineSetting('machine_name', 'ultimaker original')
368                         profile.putMachineSetting('machine_type', 'ultimaker')
369                         profile.putMachineSetting('machine_center_is_zero', 'False')
370                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
371                         profile.putProfileSetting('nozzle_size', '0.4')
372                         profile.putMachineSetting('extruder_head_size_min_x', '75.0')
373                         profile.putMachineSetting('extruder_head_size_min_y', '18.0')
374                         profile.putMachineSetting('extruder_head_size_max_x', '18.0')
375                         profile.putMachineSetting('extruder_head_size_max_y', '35.0')
376                         profile.putMachineSetting('extruder_head_size_height', '55.0')
377                 else:
378                         profile.putMachineSetting('machine_width', '80')
379                         profile.putMachineSetting('machine_depth', '80')
380                         profile.putMachineSetting('machine_height', '60')
381                         profile.putMachineSetting('machine_name', 'reprap')
382                         profile.putMachineSetting('machine_type', 'reprap')
383                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
384                         profile.putPreference('startMode', 'Normal')
385                         profile.putProfileSetting('nozzle_size', '0.5')
386                 profile.checkAndUpdateMachineName()
387                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
388                 if self.SubmitUserStats.GetValue():
389                         profile.putPreference('submit_slice_information', 'True')
390                 else:
391                         profile.putPreference('submit_slice_information', 'False')
392
393
394 class SelectParts(InfoPage):
395         def __init__(self, parent):
396                 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
397                 self.AddText(_("To assist you in having better default settings for your Ultimaker\nCura would like to know which upgrades you have in your machine."))
398                 self.AddSeperator()
399                 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
400                 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
401                 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
402                 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
403                 self.AddSeperator()
404                 self.AddText(_("If you have an Ultimaker bought after october 2012 you will have the\nExtruder drive upgrade. If you do not have this upgrade,\nit is highly recommended to improve reliability."))
405                 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
406                 self.springExtruder.SetValue(True)
407
408         def StoreData(self):
409                 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
410                 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
411                         profile.putMachineSetting('has_heated_bed', 'True')
412                 else:
413                         profile.putMachineSetting('has_heated_bed', 'False')
414                 if self.dualExtrusion.GetValue():
415                         profile.putMachineSetting('extruder_amount', '2')
416                         profile.putMachineSetting('machine_depth', '195')
417                 else:
418                         profile.putMachineSetting('extruder_amount', '1')
419                 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
420                         profile.putProfileSetting('retraction_enable', 'True')
421                 else:
422                         profile.putProfileSetting('retraction_enable', 'False')
423
424
425 class UltimakerFirmwareUpgradePage(InfoPage):
426         def __init__(self, parent):
427                 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
428                 self.AddText(_("Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work."))
429                 self.AddHiddenSeperator()
430                 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
431                 self.AddHiddenSeperator()
432                 self.AddText(_("Cura requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now."))
433                 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
434                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
435                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
436                 self.AddHiddenSeperator()
437                 self.AddText(_("Do not upgrade to this firmware if:"))
438                 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
439                 self.AddText(_("* Have other changes in the firmware"))
440 #               button = self.AddButton('Goto this page for a custom firmware')
441 #               button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
442
443         def AllowNext(self):
444                 return False
445
446         def OnUpgradeClick(self, e):
447                 if firmwareInstall.InstallFirmware():
448                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
449
450         def OnSkipClick(self, e):
451                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
452                 self.GetParent().ShowPage(self.GetNext())
453
454         def OnUrlClick(self, e):
455                 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
456
457 class UltimakerCheckupPage(InfoPage):
458         def __init__(self, parent):
459                 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
460
461                 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
462                 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
463                 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
464                 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
465                 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
466                 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
467                 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
468                 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
469                 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
470                 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
471
472                 self.AddText(
473                         _("It is a good idea to do a few sanity checks now on your Ultimaker.\nYou can skip these if you know your machine is functional."))
474                 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
475                 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
476                 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
477                 self.AddSeperator()
478                 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
479                 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
480                 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
481                 self.AddSeperator()
482                 self.infoBox = self.AddInfoBox()
483                 self.machineState = self.AddText("")
484                 self.temperatureLabel = self.AddText("")
485                 self.errorLogButton = self.AddButton(_("Show error log"))
486                 self.errorLogButton.Show(False)
487                 self.AddSeperator()
488                 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
489                 self.comm = None
490                 self.xMinStop = False
491                 self.xMaxStop = False
492                 self.yMinStop = False
493                 self.yMaxStop = False
494                 self.zMinStop = False
495                 self.zMaxStop = False
496
497                 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
498
499         def __del__(self):
500                 if self.comm is not None:
501                         self.comm.close()
502
503         def AllowNext(self):
504                 self.endstopBitmap.Show(False)
505                 return False
506
507         def OnSkipClick(self, e):
508                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
509                 self.GetParent().ShowPage(self.GetNext())
510
511         def OnCheckClick(self, e=None):
512                 self.errorLogButton.Show(False)
513                 if self.comm is not None:
514                         self.comm.close()
515                         del self.comm
516                         self.comm = None
517                         wx.CallAfter(self.OnCheckClick)
518                         return
519                 self.infoBox.SetBusy(_("Connecting to machine."))
520                 self.commState.SetBitmap(self.unknownBitmap)
521                 self.tempState.SetBitmap(self.unknownBitmap)
522                 self.stopState.SetBitmap(self.unknownBitmap)
523                 self.checkupState = 0
524                 self.checkExtruderNr = 0
525                 self.comm = machineCom.MachineCom(callbackObject=self)
526
527         def OnErrorLog(self, e):
528                 printWindow.LogWindow('\n'.join(self.comm.getLog()))
529
530         def mcLog(self, message):
531                 pass
532
533         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
534                 if not self.comm.isOperational():
535                         return
536                 if self.checkupState == 0:
537                         self.tempCheckTimeout = 20
538                         if temp[self.checkExtruderNr] > 70:
539                                 self.checkupState = 1
540                                 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
541                                 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
542                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
543                         else:
544                                 self.startTemp = temp[self.checkExtruderNr]
545                                 self.checkupState = 2
546                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
547                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
548                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
549                 elif self.checkupState == 1:
550                         if temp < 60:
551                                 self.startTemp = temp[self.checkExtruderNr]
552                                 self.checkupState = 2
553                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
554                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
555                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
556                 elif self.checkupState == 2:
557                         #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
558                         if temp[self.checkExtruderNr] > self.startTemp + 40:
559                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
560                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
561                                 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
562                                         self.checkExtruderNr = 0
563                                         self.checkupState = 3
564                                         wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
565                                         wx.CallAfter(self.endstopBitmap.Show, True)
566                                         wx.CallAfter(self.Layout)
567                                         self.comm.sendCommand('M119')
568                                         wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
569                                 else:
570                                         self.checkupState = 0
571                                         self.checkExtruderNr += 1
572                         else:
573                                 self.tempCheckTimeout -= 1
574                                 if self.tempCheckTimeout < 1:
575                                         self.checkupState = -1
576                                         wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
577                                         wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
578                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
579                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
580                 elif self.checkupState >= 3 and self.checkupState < 10:
581                         self.comm.sendCommand('M119')
582                 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
583
584         def mcStateChange(self, state):
585                 if self.comm is None:
586                         return
587                 if self.comm.isOperational():
588                         wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
589                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
590                 elif self.comm.isError():
591                         wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
592                         wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
593                         wx.CallAfter(self.endstopBitmap.Show, False)
594                         wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
595                         wx.CallAfter(self.errorLogButton.Show, True)
596                         wx.CallAfter(self.Layout)
597                 else:
598                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
599
600         def mcMessage(self, message):
601                 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
602                         for data in message.split(' '):
603                                 if ':' in data:
604                                         tag, value = data.split(':', 1)
605                                         if tag == 'x_min':
606                                                 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
607                                         if tag == 'x_max':
608                                                 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
609                                         if tag == 'y_min':
610                                                 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
611                                         if tag == 'y_max':
612                                                 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
613                                         if tag == 'z_min':
614                                                 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
615                                         if tag == 'z_max':
616                                                 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
617                         if ':' in message:
618                                 tag, value = map(str.strip, message.split(':', 1))
619                                 if tag == 'x_min':
620                                         self.xMinStop = (value == 'H' or value == 'TRIGGERED')
621                                 if tag == 'x_max':
622                                         self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
623                                 if tag == 'y_min':
624                                         self.yMinStop = (value == 'H' or value == 'TRIGGERED')
625                                 if tag == 'y_max':
626                                         self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
627                                 if tag == 'z_min':
628                                         self.zMinStop = (value == 'H' or value == 'TRIGGERED')
629                                 if tag == 'z_max':
630                                         self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
631                         if 'z_max' in message:
632                                 self.comm.sendCommand('M119')
633
634                         if self.checkupState == 3:
635                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
636                                         self.checkupState = 4
637                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
638                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
639                         elif self.checkupState == 4:
640                                 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
641                                         self.checkupState = 5
642                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
643                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
644                         elif self.checkupState == 5:
645                                 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
646                                         self.checkupState = 6
647                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
648                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
649                         elif self.checkupState == 6:
650                                 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
651                                         self.checkupState = 7
652                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
653                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
654                         elif self.checkupState == 7:
655                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
656                                         self.checkupState = 8
657                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
658                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
659                         elif self.checkupState == 8:
660                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
661                                         self.checkupState = 9
662                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
663                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
664                         elif self.checkupState == 9:
665                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
666                                         self.checkupState = 10
667                                         self.comm.close()
668                                         wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
669                                         wx.CallAfter(self.infoBox.SetReadyIndicator)
670                                         wx.CallAfter(self.endstopBitmap.Show, False)
671                                         wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
672                                         wx.CallAfter(self.OnSkipClick, None)
673
674         def mcProgress(self, lineNr):
675                 pass
676
677         def mcZChange(self, newZ):
678                 pass
679
680
681 class UltimakerCalibrationPage(InfoPage):
682         def __init__(self, parent):
683                 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
684
685                 self.AddText("Your Ultimaker requires some calibration.")
686                 self.AddText("This calibration is needed for a proper extrusion amount.")
687                 self.AddSeperator()
688                 self.AddText("The following values are needed:")
689                 self.AddText("* Diameter of filament")
690                 self.AddText("* Number of steps per mm of filament extrusion")
691                 self.AddSeperator()
692                 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
693                 self.AddSeperator()
694                 self.AddText("First we need the diameter of your filament:")
695                 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
696                 self.AddText(
697                         "If you do not own digital Calipers that can measure\nat least 2 digits then use 2.89mm.\nWhich is the average diameter of most filament.")
698                 self.AddText("Note: This value can be changed later at any time.")
699
700         def StoreData(self):
701                 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
702
703
704 class UltimakerCalibrateStepsPerEPage(InfoPage):
705         def __init__(self, parent):
706                 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
707
708                 #if profile.getMachineSetting('steps_per_e') == '0':
709                 #       profile.putMachineSetting('steps_per_e', '865.888')
710
711                 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
712                 self.AddText(_("First remove any filament from your machine."))
713                 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
714                 self.AddText(_("We'll push the filament 100mm"))
715                 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
716                 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
717                 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
718                 self.AddText(_("This results in the following steps per E:"))
719                 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
720                 self.AddText(_("You can repeat these steps to get better calibration."))
721                 self.AddSeperator()
722                 self.AddText(
723                         _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
724                 self.heatButton = self.AddButton(_("Heatup for filament removal"))
725
726                 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
727                 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
728                 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
729
730         def OnSaveLengthClick(self, e):
731                 currentEValue = float(self.stepsPerEInput.GetValue())
732                 realExtrudeLength = float(self.lengthInput.GetValue())
733                 newEValue = currentEValue * 100 / realExtrudeLength
734                 self.stepsPerEInput.SetValue(str(newEValue))
735                 self.lengthInput.SetValue("100")
736
737         def OnExtrudeClick(self, e):
738                 t = threading.Thread(target=self.OnExtrudeRun)
739                 t.daemon = True
740                 t.start()
741
742         def OnExtrudeRun(self):
743                 self.heatButton.Enable(False)
744                 self.extrudeButton.Enable(False)
745                 currentEValue = float(self.stepsPerEInput.GetValue())
746                 self.comm = machineCom.MachineCom()
747                 if not self.comm.isOpen():
748                         wx.MessageBox(
749                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
750                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
751                         self.heatButton.Enable(True)
752                         self.extrudeButton.Enable(True)
753                         return
754                 while True:
755                         line = self.comm.readline()
756                         if line == '':
757                                 return
758                         if 'start' in line:
759                                 break
760                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
761                 time.sleep(3)
762
763                 self.sendGCommand('M302') #Disable cold extrusion protection
764                 self.sendGCommand("M92 E%f" % (currentEValue))
765                 self.sendGCommand("G92 E0")
766                 self.sendGCommand("G1 E100 F600")
767                 time.sleep(15)
768                 self.comm.close()
769                 self.extrudeButton.Enable()
770                 self.heatButton.Enable()
771
772         def OnHeatClick(self, e):
773                 t = threading.Thread(target=self.OnHeatRun)
774                 t.daemon = True
775                 t.start()
776
777         def OnHeatRun(self):
778                 self.heatButton.Enable(False)
779                 self.extrudeButton.Enable(False)
780                 self.comm = machineCom.MachineCom()
781                 if not self.comm.isOpen():
782                         wx.MessageBox(
783                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
784                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
785                         self.heatButton.Enable(True)
786                         self.extrudeButton.Enable(True)
787                         return
788                 while True:
789                         line = self.comm.readline()
790                         if line == '':
791                                 self.heatButton.Enable(True)
792                                 self.extrudeButton.Enable(True)
793                                 return
794                         if 'start' in line:
795                                 break
796                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
797                 time.sleep(3)
798
799                 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
800                 wx.MessageBox(
801                         'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
802                         'Machine heatup', wx.OK | wx.ICON_INFORMATION)
803                 self.sendGCommand('M104 S0')
804                 time.sleep(1)
805                 self.comm.close()
806                 self.heatButton.Enable(True)
807                 self.extrudeButton.Enable(True)
808
809         def sendGCommand(self, cmd):
810                 self.comm.sendCommand(cmd) #Disable cold extrusion protection
811                 while True:
812                         line = self.comm.readline()
813                         if line == '':
814                                 return
815                         if line.startswith('ok'):
816                                 break
817
818         def StoreData(self):
819                 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
820
821 class Ultimaker2ReadyPage(InfoPage):
822         def __init__(self, parent):
823                 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
824                 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
825                 self.AddText('Cura is now ready to be used with your Ultimaker2.')
826                 self.AddSeperator()
827
828 class configWizard(wx.wizard.Wizard):
829         def __init__(self, addNew = False):
830                 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
831
832                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
833                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
834
835                 self.firstInfoPage = FirstInfoPage(self, addNew)
836                 self.machineSelectPage = MachineSelectPage(self)
837                 self.ultimakerSelectParts = SelectParts(self)
838                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
839                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
840                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
841                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
842                 self.bedLevelPage = bedLevelWizardMain(self)
843                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
844                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
845                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
846                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
847
848                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
849
850                 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
851                 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
852                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
853                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
854                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
855                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
856                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
857                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
858
859                 self.FitToPage(self.firstInfoPage)
860                 self.GetPageAreaSizer().Add(self.firstInfoPage)
861
862                 self.RunWizard(self.firstInfoPage)
863                 self.Destroy()
864
865         def OnPageChanging(self, e):
866                 e.GetPage().StoreData()
867
868         def OnPageChanged(self, e):
869                 if e.GetPage().AllowNext():
870                         self.FindWindowById(wx.ID_FORWARD).Enable()
871                 else:
872                         self.FindWindowById(wx.ID_FORWARD).Disable()
873                 if e.GetPage().AllowBack():
874                         self.FindWindowById(wx.ID_BACKWARD).Enable()
875                 else:
876                         self.FindWindowById(wx.ID_BACKWARD).Disable()
877
878 class bedLevelWizardMain(InfoPage):
879         def __init__(self, parent):
880                 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
881
882                 self.AddText('This wizard will help you in leveling your printer bed')
883                 self.AddSeperator()
884                 self.AddText('It will do the following steps')
885                 self.AddText('* Move the printer head to each corner')
886                 self.AddText('  and let you adjust the height of the bed to the nozzle')
887                 self.AddText('* Print a line around the bed to check if it is level')
888                 self.AddSeperator()
889
890                 self.connectButton = self.AddButton('Connect to printer')
891                 self.comm = None
892
893                 self.infoBox = self.AddInfoBox()
894                 self.resumeButton = self.AddButton('Resume')
895                 self.upButton, self.downButton = self.AddDualButton('Up 0.2mm', 'Down 0.2mm')
896                 self.upButton2, self.downButton2 = self.AddDualButton('Up 10mm', 'Down 10mm')
897                 self.resumeButton.Enable(False)
898
899                 self.upButton.Enable(False)
900                 self.downButton.Enable(False)
901                 self.upButton2.Enable(False)
902                 self.downButton2.Enable(False)
903
904                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
905                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
906                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
907                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
908                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
909                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
910
911         def OnConnect(self, e = None):
912                 if self.comm is not None:
913                         self.comm.close()
914                         del self.comm
915                         self.comm = None
916                         wx.CallAfter(self.OnConnect)
917                         return
918                 self.connectButton.Enable(False)
919                 self.comm = machineCom.MachineCom(callbackObject=self)
920                 self.infoBox.SetBusy('Connecting to machine.')
921                 self._wizardState = 0
922
923         def OnBedUp(self, e):
924                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
925                 self.comm.sendCommand('G92 Z10')
926                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
927                 self.comm.sendCommand('M400')
928
929         def OnBedDown(self, e):
930                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
931                 self.comm.sendCommand('G92 Z10')
932                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
933                 self.comm.sendCommand('M400')
934
935         def OnBedUp2(self, e):
936                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
937                 self.comm.sendCommand('G92 Z10')
938                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
939                 self.comm.sendCommand('M400')
940
941         def OnBedDown2(self, e):
942                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
943                 self.comm.sendCommand('G92 Z10')
944                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
945                 self.comm.sendCommand('M400')
946
947         def AllowNext(self):
948                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
949                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
950                 return True
951
952         def OnResume(self, e):
953                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
954                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
955                 if self._wizardState == -1:
956                         wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
957                         wx.CallAfter(self.upButton.Enable, False)
958                         wx.CallAfter(self.downButton.Enable, False)
959                         wx.CallAfter(self.upButton2.Enable, False)
960                         wx.CallAfter(self.downButton2.Enable, False)
961                         self.comm.sendCommand('M105')
962                         self.comm.sendCommand('G28')
963                         self._wizardState = 1
964                 elif self._wizardState == 2:
965                         if profile.getMachineSetting('has_heated_bed') == 'True':
966                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back center...')
967                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
968                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
969                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
970                                 self.comm.sendCommand('M400')
971                                 self._wizardState = 3
972                         else:
973                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
974                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
975                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
976                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
977                                 self.comm.sendCommand('M400')
978                                 self._wizardState = 3
979                 elif self._wizardState == 4:
980                         if profile.getMachineSetting('has_heated_bed') == 'True':
981                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
982                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
983                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
984                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
985                                 self.comm.sendCommand('M400')
986                                 self._wizardState = 7
987                         else:
988                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
989                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
990                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
991                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
992                                 self.comm.sendCommand('M400')
993                                 self._wizardState = 5
994                 elif self._wizardState == 6:
995                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
996                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
997                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
998                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
999                         self.comm.sendCommand('M400')
1000                         self._wizardState = 7
1001                 elif self._wizardState == 8:
1002                         wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
1003                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1004                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1005                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1006                         self._wizardState = 9
1007                 elif self._wizardState == 10:
1008                         self._wizardState = 11
1009                         wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
1010                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1011                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1012                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1013                         w = profile.getMachineSettingFloat('machine_width') - 10
1014                         d = profile.getMachineSettingFloat('machine_depth')
1015                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1016                         filamentArea = math.pi * filamentRadius * filamentRadius
1017                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1018                         eValue = 0.0
1019
1020                         gcodeList = [
1021                                 'G1 Z2 F%d' % (feedZ),
1022                                 'G92 E0',
1023                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1024                                 'G1 Z0.3 F%d' % (feedZ)]
1025                         eValue += 5.0
1026                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1027
1028                         for i in xrange(0, 3):
1029                                 dist = 5.0 + 0.4 * float(i)
1030                                 eValue += (d - 2.0*dist) * ePerMM
1031                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1032                                 eValue += (w - 2.0*dist) * ePerMM
1033                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1034                                 eValue += (d - 2.0*dist) * ePerMM
1035                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1036                                 eValue += (w - 2.0*dist) * ePerMM
1037                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1038
1039                         gcodeList.append('M400')
1040                         self.comm.printGCode(gcodeList)
1041                 self.resumeButton.Enable(False)
1042
1043         def mcLog(self, message):
1044                 print 'Log:', message
1045
1046         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1047                 if self._wizardState == 1:
1048                         self._wizardState = 2
1049                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
1050                         wx.CallAfter(self.resumeButton.Enable, True)
1051                 elif self._wizardState == 3:
1052                         self._wizardState = 4
1053                         if profile.getMachineSetting('has_heated_bed') == 'True':
1054                                 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.')
1055                         else:
1056                                 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
1057                         wx.CallAfter(self.resumeButton.Enable, True)
1058                 elif self._wizardState == 5:
1059                         self._wizardState = 6
1060                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
1061                         wx.CallAfter(self.resumeButton.Enable, True)
1062                 elif self._wizardState == 7:
1063                         self._wizardState = 8
1064                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
1065                         wx.CallAfter(self.resumeButton.Enable, True)
1066                 elif self._wizardState == 9:
1067                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1068                                 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1069                         else:
1070                                 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
1071                                 wx.CallAfter(self.resumeButton.Enable, True)
1072                                 self._wizardState = 10
1073
1074         def mcStateChange(self, state):
1075                 if self.comm is None:
1076                         return
1077                 if self.comm.isOperational():
1078                         if self._wizardState == 0:
1079                                 wx.CallAfter(self.infoBox.SetAttention, 'Use the up/down buttons to move the bed and adjust your Z endstop.')
1080                                 wx.CallAfter(self.upButton.Enable, True)
1081                                 wx.CallAfter(self.downButton.Enable, True)
1082                                 wx.CallAfter(self.upButton2.Enable, True)
1083                                 wx.CallAfter(self.downButton2.Enable, True)
1084                                 wx.CallAfter(self.resumeButton.Enable, True)
1085                                 self._wizardState = -1
1086                         elif self._wizardState == 11 and not self.comm.isPrinting():
1087                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1088                                 self.comm.sendCommand('G92 E0')
1089                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1090                                 self.comm.sendCommand('M104 S0')
1091                                 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
1092                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1093                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1094                                 wx.CallAfter(self.connectButton.Enable, True)
1095                                 self._wizardState = 12
1096                 elif self.comm.isError():
1097                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1098
1099         def mcMessage(self, message):
1100                 pass
1101
1102         def mcProgress(self, lineNr):
1103                 pass
1104
1105         def mcZChange(self, newZ):
1106                 pass
1107
1108 class headOffsetCalibrationPage(InfoPage):
1109         def __init__(self, parent):
1110                 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1111
1112                 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
1113                 self.AddSeperator()
1114
1115                 self.connectButton = self.AddButton('Connect to printer')
1116                 self.comm = None
1117
1118                 self.infoBox = self.AddInfoBox()
1119                 self.textEntry = self.AddTextCtrl('')
1120                 self.textEntry.Enable(False)
1121                 self.resumeButton = self.AddButton('Resume')
1122                 self.resumeButton.Enable(False)
1123
1124                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1125                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1126
1127         def AllowBack(self):
1128                 return True
1129
1130         def OnConnect(self, e = None):
1131                 if self.comm is not None:
1132                         self.comm.close()
1133                         del self.comm
1134                         self.comm = None
1135                         wx.CallAfter(self.OnConnect)
1136                         return
1137                 self.connectButton.Enable(False)
1138                 self.comm = machineCom.MachineCom(callbackObject=self)
1139                 self.infoBox.SetBusy('Connecting to machine.')
1140                 self._wizardState = 0
1141
1142         def OnResume(self, e):
1143                 if self._wizardState == 2:
1144                         self._wizardState = 3
1145                         wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1146
1147                         w = profile.getMachineSettingFloat('machine_width')
1148                         d = profile.getMachineSettingFloat('machine_depth')
1149
1150                         gcode = gcodeGenerator.gcodeGenerator()
1151                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1152                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1153                         gcode.addCmd('T0')
1154                         gcode.addPrime(15)
1155                         gcode.addCmd('T1')
1156                         gcode.addPrime(15)
1157
1158                         gcode.addCmd('T0')
1159                         gcode.addMove(w/2, 5)
1160                         gcode.addMove(z=0.2)
1161                         gcode.addPrime()
1162                         gcode.addExtrude(w/2, d-5.0)
1163                         gcode.addRetract()
1164                         gcode.addMove(5, d/2)
1165                         gcode.addPrime()
1166                         gcode.addExtrude(w-5.0, d/2)
1167                         gcode.addRetract(15)
1168
1169                         gcode.addCmd('T1')
1170                         gcode.addMove(w/2, 5)
1171                         gcode.addPrime()
1172                         gcode.addExtrude(w/2, d-5.0)
1173                         gcode.addRetract()
1174                         gcode.addMove(5, d/2)
1175                         gcode.addPrime()
1176                         gcode.addExtrude(w-5.0, d/2)
1177                         gcode.addRetract(15)
1178                         gcode.addCmd('T0')
1179
1180                         gcode.addMove(z=25)
1181                         gcode.addMove(0, 0)
1182                         gcode.addCmd('M400')
1183
1184                         self.comm.printGCode(gcode.list())
1185                         self.resumeButton.Enable(False)
1186                 elif self._wizardState == 4:
1187                         try:
1188                                 float(self.textEntry.GetValue())
1189                         except ValueError:
1190                                 return
1191                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1192                         self._wizardState = 5
1193                         self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1194                         self.textEntry.SetValue('0.0')
1195                         self.textEntry.Enable(True)
1196                 elif self._wizardState == 5:
1197                         try:
1198                                 float(self.textEntry.GetValue())
1199                         except ValueError:
1200                                 return
1201                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1202                         self._wizardState = 6
1203                         self.infoBox.SetBusy('Printing the fine calibration lines.')
1204                         self.textEntry.SetValue('')
1205                         self.textEntry.Enable(False)
1206                         self.resumeButton.Enable(False)
1207
1208                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1209                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1210                         gcode = gcodeGenerator.gcodeGenerator()
1211                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1212                         gcode.setPrintSpeed(25)
1213                         gcode.addHome()
1214                         gcode.addCmd('T0')
1215                         gcode.addMove(50, 40, 0.2)
1216                         gcode.addPrime(15)
1217                         for n in xrange(0, 10):
1218                                 gcode.addExtrude(50 + n * 10, 150)
1219                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1220                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1221                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1222                         gcode.addMove(40, 50)
1223                         for n in xrange(0, 10):
1224                                 gcode.addExtrude(150, 50 + n * 10)
1225                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1226                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1227                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1228                         gcode.addRetract(15)
1229
1230                         gcode.addCmd('T1')
1231                         gcode.addMove(50 - x, 30 - y, 0.2)
1232                         gcode.addPrime(15)
1233                         for n in xrange(0, 10):
1234                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1235                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1236                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1237                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1238                         gcode.addMove(30 - x, 50 - y, 0.2)
1239                         for n in xrange(0, 10):
1240                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1241                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1242                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1243                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1244                         gcode.addRetract(15)
1245                         gcode.addMove(z=15)
1246                         gcode.addCmd('M400')
1247                         gcode.addCmd('M104 T0 S0')
1248                         gcode.addCmd('M104 T1 S0')
1249                         self.comm.printGCode(gcode.list())
1250                 elif self._wizardState == 7:
1251                         try:
1252                                 n = int(self.textEntry.GetValue()) - 1
1253                         except:
1254                                 return
1255                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1256                         x += -1.0 + n * 0.1
1257                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1258                         self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1259                         self.textEntry.SetValue('10')
1260                         self._wizardState = 8
1261                 elif self._wizardState == 8:
1262                         try:
1263                                 n = int(self.textEntry.GetValue()) - 1
1264                         except:
1265                                 return
1266                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1267                         y += -1.0 + n * 0.1
1268                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1269                         self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1270                         self.infoBox.SetReadyIndicator()
1271                         self._wizardState = 8
1272                         self.comm.close()
1273                         self.resumeButton.Enable(False)
1274
1275         def mcLog(self, message):
1276                 print 'Log:', message
1277
1278         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1279                 if self._wizardState == 1:
1280                         if temp[0] >= 210 and temp[1] >= 210:
1281                                 self._wizardState = 2
1282                                 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1283                                 wx.CallAfter(self.resumeButton.Enable, True)
1284                                 wx.CallAfter(self.resumeButton.SetFocus)
1285
1286         def mcStateChange(self, state):
1287                 if self.comm is None:
1288                         return
1289                 if self.comm.isOperational():
1290                         if self._wizardState == 0:
1291                                 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1292                                 self.comm.sendCommand('M105')
1293                                 self.comm.sendCommand('M104 S220 T0')
1294                                 self.comm.sendCommand('M104 S220 T1')
1295                                 self.comm.sendCommand('G28')
1296                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1297                                 self._wizardState = 1
1298                         if not self.comm.isPrinting():
1299                                 if self._wizardState == 3:
1300                                         self._wizardState = 4
1301                                         wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1302                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1303                                         wx.CallAfter(self.textEntry.Enable, True)
1304                                         wx.CallAfter(self.resumeButton.Enable, True)
1305                                         wx.CallAfter(self.resumeButton.SetFocus)
1306                                 elif self._wizardState == 6:
1307                                         self._wizardState = 7
1308                                         wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1309                                         wx.CallAfter(self.textEntry.SetValue, '10')
1310                                         wx.CallAfter(self.textEntry.Enable, True)
1311                                         wx.CallAfter(self.resumeButton.Enable, True)
1312                                         wx.CallAfter(self.resumeButton.SetFocus)
1313
1314                 elif self.comm.isError():
1315                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1316
1317         def mcMessage(self, message):
1318                 pass
1319
1320         def mcProgress(self, lineNr):
1321                 pass
1322
1323         def mcZChange(self, newZ):
1324                 pass
1325
1326 class bedLevelWizard(wx.wizard.Wizard):
1327         def __init__(self):
1328                 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1329
1330                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1331                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1332
1333                 self.mainPage = bedLevelWizardMain(self)
1334                 self.headOffsetCalibration = None
1335
1336                 self.FitToPage(self.mainPage)
1337                 self.GetPageAreaSizer().Add(self.mainPage)
1338
1339                 self.RunWizard(self.mainPage)
1340                 self.Destroy()
1341
1342         def OnPageChanging(self, e):
1343                 e.GetPage().StoreData()
1344
1345         def OnPageChanged(self, e):
1346                 if e.GetPage().AllowNext():
1347                         self.FindWindowById(wx.ID_FORWARD).Enable()
1348                 else:
1349                         self.FindWindowById(wx.ID_FORWARD).Disable()
1350                 if e.GetPage().AllowBack():
1351                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1352                 else:
1353                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1354
1355 class headOffsetWizard(wx.wizard.Wizard):
1356         def __init__(self):
1357                 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1358
1359                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1360                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1361
1362                 self.mainPage = headOffsetCalibrationPage(self)
1363
1364                 self.FitToPage(self.mainPage)
1365                 self.GetPageAreaSizer().Add(self.mainPage)
1366
1367                 self.RunWizard(self.mainPage)
1368                 self.Destroy()
1369
1370         def OnPageChanging(self, e):
1371                 e.GetPage().StoreData()
1372
1373         def OnPageChanged(self, e):
1374                 if e.GetPage().AllowNext():
1375                         self.FindWindowById(wx.ID_FORWARD).Enable()
1376                 else:
1377                         self.FindWindowById(wx.ID_FORWARD).Disable()
1378                 if e.GetPage().AllowBack():
1379                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1380                 else:
1381                         self.FindWindowById(wx.ID_BACKWARD).Disable()