chiark / gitweb /
Fix merge problem
[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 from Cura.util import version
19
20
21 class InfoBox(wx.Panel):
22         def __init__(self, parent):
23                 super(InfoBox, self).__init__(parent)
24                 self.SetBackgroundColour('#FFFF80')
25
26                 self.sizer = wx.GridBagSizer(5, 5)
27                 self.SetSizer(self.sizer)
28
29                 self.attentionBitmap = wx.Bitmap(resources.getPathForImage('attention.png'))
30                 self.errorBitmap = wx.Bitmap(resources.getPathForImage('error.png'))
31                 self.readyBitmap = wx.Bitmap(resources.getPathForImage('ready.png'))
32                 self.busyBitmap = [
33                         wx.Bitmap(resources.getPathForImage('busy-0.png')),
34                         wx.Bitmap(resources.getPathForImage('busy-1.png')),
35                         wx.Bitmap(resources.getPathForImage('busy-2.png')),
36                         wx.Bitmap(resources.getPathForImage('busy-3.png'))
37                 ]
38
39                 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
40                 self.text = wx.StaticText(self, -1, '')
41                 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
42                 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
43                 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
44                 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
45                 self.sizer.AddGrowableCol(1)
46
47                 self.extraInfoButton.Show(False)
48
49                 self.extraInfoUrl = ''
50                 self.busyState = None
51                 self.timer = wx.Timer(self)
52                 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
53                 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
54                 self.timer.Start(100)
55
56         def SetInfo(self, info):
57                 self.SetBackgroundColour('#FFFF80')
58                 self.text.SetLabel(info)
59                 self.extraInfoButton.Show(False)
60                 self.Refresh()
61
62         def SetError(self, info, extraInfoUrl):
63                 self.extraInfoUrl = extraInfoUrl
64                 self.SetBackgroundColour('#FF8080')
65                 self.text.SetLabel(info)
66                 if extraInfoUrl:
67                         self.extraInfoButton.Show(True)
68                 self.Layout()
69                 self.SetErrorIndicator()
70                 self.Refresh()
71
72         def SetAttention(self, info):
73                 self.SetBackgroundColour('#FFFF80')
74                 self.text.SetLabel(info)
75                 self.extraInfoButton.Show(False)
76                 self.SetAttentionIndicator()
77                 self.Layout()
78                 self.Refresh()
79
80         def SetBusy(self, info):
81                 self.SetInfo(info)
82                 self.SetBusyIndicator()
83
84         def SetBusyIndicator(self):
85                 self.busyState = 0
86                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
87
88         def doExtraInfo(self, e):
89                 webbrowser.open(self.extraInfoUrl)
90
91         def doBusyUpdate(self, e):
92                 if self.busyState is None:
93                         return
94                 self.busyState += 1
95                 if self.busyState >= len(self.busyBitmap):
96                         self.busyState = 0
97                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
98
99         def SetReadyIndicator(self):
100                 self.busyState = None
101                 self.bitmap.SetBitmap(self.readyBitmap)
102
103         def SetErrorIndicator(self):
104                 self.busyState = None
105                 self.bitmap.SetBitmap(self.errorBitmap)
106
107         def SetAttentionIndicator(self):
108                 self.busyState = None
109                 self.bitmap.SetBitmap(self.attentionBitmap)
110
111 class ImageButton(wx.Panel):
112         DefaultOverlay = wx.Bitmap(resources.getPathForImage('ImageButton_Overlay.png'))
113         IB_GROUP=1
114         __groups__ = {}
115         __last_group__ = None
116         def __init__(self, parent, label, bitmap, extra_label=None, overlay=DefaultOverlay, style=None):
117                 super(ImageButton, self).__init__(parent)
118
119                 if style is ImageButton.IB_GROUP:
120                         ImageButton.__last_group__ = self
121                         ImageButton.__groups__[self] = [self]
122                         self.group = self
123                 else:
124                         if ImageButton.__last_group__:
125                                 ImageButton.__groups__[ImageButton.__last_group__].append(self)
126                                 self.group = ImageButton.__last_group__
127                         else:
128                                 self.group = None
129                 self.sizer = wx.StaticBoxSizer(wx.StaticBox(self), wx.VERTICAL)
130                 self.SetSizer(self.sizer)
131                 self.bitmap = bitmap
132                 self.original_overlay = overlay
133                 self.overlay = self.createOverlay(bitmap, overlay)
134                 self.text = wx.StaticText(self, -1, label)
135                 self.bmp = wx.StaticBitmap(self, -1, self.bitmap)
136                 if extra_label:
137                         self.extra_text = wx.StaticText(self, -1, extra_label)
138                 else:
139                         self.extra_text = None
140                 self.selected = False
141                 self.callback = None
142
143                 self.sizer.Add(self.text, 0, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER, border=5)
144                 self.sizer.Add(self.bmp, 1, flag=wx.ALL|wx.ALIGN_CENTER|wx.EXPAND, border=5)
145                 if self.extra_text:
146                         self.sizer.Add(self.extra_text, 0, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER, border=5)
147                 self.bmp.Bind(wx.EVT_LEFT_UP, self.OnLeftClick)
148
149         def __del__(self):
150                 if self.group:
151                         ImageButton.__groups__[self.group].remove(self)
152                         if self == self.group:
153                                 for ib in ImageButton.__groups__[self.group]:
154                                         ib.group = None
155                                 del ImageButton.__groups__[self.group]
156                                 if ImageButton.__last_group__ == self:
157                                         ImageButton.__last_group__ = None
158
159         def TriggerGroupCallbacks(self):
160                 if self.group:
161                         for ib in ImageButton.__groups__[self.group]:
162                                 if ib.GetValue() and ib.callback:
163                                         ib.callback()
164                                         break
165                 else:
166                         if self.GetValue() and self.callback:
167                                 self.callback()
168
169         def OnLeftClick(self, e):
170                 self.SetValue(True)
171
172         def GetValue(self):
173                 return self.selected
174
175         def SetValue(self, value):
176                 old_value = self.selected
177                 self.selected = bool(value)
178                 self.bmp.SetBitmap(self.overlay if self.GetValue() else self.bitmap)
179                 if self.selected and self.group:
180                         for ib in ImageButton.__groups__[self.group]:
181                                 if ib == self:
182                                         continue
183                                 ib.SetValue(False)
184                 self.Layout()
185                 if self.callback and not old_value and self.selected:
186                         self.callback()
187
188         def SetLabel(self, label):
189                 self.text.SetLabel(label)
190                 self.Layout()
191
192         def SetExtraLabel(self, label):
193                 if self.extra_text:
194                         self.extra_text.SetLabel(label)
195                 else:
196                         self.extra_text = wx.StaticText(self, -1, label)
197                         self.sizer.Add(self.extra_text, 0, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER, border=5)
198                 self.Layout()
199
200         def SetBitmap(self, bitmap):
201                 self.bitmap = bitmap
202                 self.overlay = self.createOverlay(bitmap, self.original_overlay)
203                 self.bmp.SetBitmap(self.overlay if self.GetValue() else self.bitmap)
204                 self.Layout()
205
206         def SetOverlay(self, overlay):
207                 self.original_overlay = overlay
208                 self.overlay = self.createOverlay(self.bitmap, self.original_overlay)
209                 self.bmp.SetBitmap(self.overlay if self.GetValue() else self.bitmap)
210                 self.Layout()
211
212         def OnSelected(self, callback):
213                 self.callback = callback
214
215         def createOverlay(self, bitmap, overlay):
216                 result = bitmap.GetSubBitmap(wx.Rect(0, 0, *bitmap.Size))
217                 (width, height) = bitmap.GetSize()
218                 overlay_image = wx.ImageFromBitmap(overlay)
219                 overlay_image = overlay_image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
220                 overlay_scaled = wx.BitmapFromImage(overlay_image)
221                 dc = wx.MemoryDC()
222                 dc.SelectObject(result)
223                 dc.DrawBitmap(overlay_scaled, 0, 0)
224                 dc.SelectObject(wx.NullBitmap)
225                 return result
226
227
228 class InfoPage(wx.wizard.WizardPageSimple):
229         def __init__(self, parent, title):
230                 wx.wizard.WizardPageSimple.__init__(self, parent)
231
232                 parent.GetPageAreaSizer().Add(self)
233                 sizer = wx.GridBagSizer(5, 5)
234                 self.sizer = sizer
235                 self.SetSizer(sizer)
236
237                 self.title = wx.StaticText(self, -1, title)
238                 font = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)
239                 self.title.SetFont(font)
240                 # HACK ALERT: For some reason, the StaticText keeps its same size as if
241                 # the font was not modified, this causes the text to wrap and to
242                 # get out of bounds of the widgets area and hide other widgets.
243                 # The only way I found for the widget to get its right size was to calculate
244                 # the new font's extent and set the min size on the widget
245                 self.title.SetMinSize(self.GetTextExtent(font, title))
246                 sizer.Add(self.title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
247                 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
248                 sizer.AddGrowableCol(1)
249
250                 self.rowNr = 2
251
252         def GetTextExtent(self, font, text):
253                 dc = wx.ScreenDC()
254                 dc.SetFont(font)
255                 w,h = dc.GetTextExtent(text)
256                 return (w, h)
257
258         def AddText(self, info):
259                 text = wx.StaticText(self, -1, info)
260                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
261                 self.rowNr += 1
262                 return text
263
264         def AddSeperator(self):
265                 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
266                 self.rowNr += 1
267
268         def AddHiddenSeperator(self):
269                 self.AddText("")
270
271         def AddInfoBox(self):
272                 infoBox = InfoBox(self)
273                 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
274                 self.rowNr += 1
275                 return infoBox
276
277         def AddRadioButton(self, label, style=0):
278                 radio = wx.RadioButton(self, -1, label, style=style)
279                 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
280                 self.rowNr += 1
281                 return radio
282
283         def AddCheckbox(self, label, checked=False):
284                 check = wx.CheckBox(self, -1)
285                 text = wx.StaticText(self, -1, label)
286                 check.SetValue(checked)
287                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
288                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
289                 self.rowNr += 1
290                 return check
291
292         def AddButton(self, label):
293                 button = wx.Button(self, -1, label)
294                 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
295                 self.rowNr += 1
296                 return button
297
298         def AddDualButton(self, label1, label2):
299                 button1 = wx.Button(self, -1, label1)
300                 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
301                 button2 = wx.Button(self, -1, label2)
302                 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
303                 self.rowNr += 1
304                 return button1, button2
305
306         def AddTextCtrl(self, value):
307                 ret = wx.TextCtrl(self, -1, value)
308                 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
309                 self.rowNr += 1
310                 return ret
311
312         def AddLabelTextCtrl(self, info, value):
313                 text = wx.StaticText(self, -1, info)
314                 ret = wx.TextCtrl(self, -1, value)
315                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
316                 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
317                 self.rowNr += 1
318                 return ret
319
320         def AddTextCtrlButton(self, value, buttonText):
321                 text = wx.TextCtrl(self, -1, value)
322                 button = wx.Button(self, -1, buttonText)
323                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
324                 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
325                 self.rowNr += 1
326                 return text, button
327
328         def AddBitmap(self, bitmap):
329                 bitmap = wx.StaticBitmap(self, -1, bitmap)
330                 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
331                 self.rowNr += 1
332                 return bitmap
333
334         def AddPanel(self):
335                 panel = wx.Panel(self, -1)
336                 sizer = wx.GridBagSizer(2, 2)
337                 panel.SetSizer(sizer)
338                 self.GetSizer().Add(panel, pos=(self.rowNr, 0), span=(1, 2), flag=wx.ALL | wx.EXPAND)
339                 self.rowNr += 1
340                 return panel
341
342         def AddCheckmark(self, label, bitmap):
343                 check = wx.StaticBitmap(self, -1, bitmap)
344                 text = wx.StaticText(self, -1, label)
345                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
346                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
347                 self.rowNr += 1
348                 return check
349
350         def AddCombo(self, label, options):
351                 combo = wx.ComboBox(self, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
352                 text = wx.StaticText(self, -1, label)
353                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER)
354                 self.GetSizer().Add(combo, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
355                 self.rowNr += 1
356                 return combo
357
358         def AddImageButton(self, panel, x, y, label, filename, image_size=None,
359                                            extra_label=None, overlay=ImageButton.DefaultOverlay, style=None):
360                 ib = ImageButton(panel, label, self.GetBitmap(filename, image_size), extra_label, overlay, style)
361                 panel.GetSizer().Add(ib, pos=(x, y), flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10)
362                 return ib
363
364         def GetBitmap(self, filename, image_size):
365                 if image_size == None:
366                         return wx.Bitmap(resources.getPathForImage(filename))
367                 else:
368                         image = wx.Image(resources.getPathForImage(filename))
369                         image_scaled = image.Scale(image_size[0], image_size[1], wx.IMAGE_QUALITY_HIGH)
370                         return wx.BitmapFromImage(image_scaled)
371
372         def AllowNext(self):
373                 return True
374
375         def AllowBack(self):
376                 return True
377
378         def StoreData(self):
379                 pass
380
381 class PrintrbotPage(InfoPage):
382         def __init__(self, parent):
383                 self._printer_info = [
384                         # X, Y, Z, Nozzle Size, Filament Diameter, PrintTemperature, Print Speed, Travel Speed, Retract speed, Retract amount, use bed level sensor
385                         ("Simple Metal", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, True),
386                         ("Metal Plus", 250, 250, 250, 0.4, 1.75, 208, 40, 70, 30, 1, True),
387                         ("Simple Makers Kit", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, True),
388                         (":" + _("Older models"),),
389                         ("Original", 130, 130, 130, 0.5, 2.95, 208, 40, 70, 30, 1, False),
390                         ("Simple Maker's Edition v1", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
391                         ("Simple Maker's Edition v2 (2013 Printrbot Simple)", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
392                         ("Simple Maker's Edition v3 (2014 Printrbot Simple)", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
393                         ("Jr v1", 115, 120, 80, 0.4, 1.75, 208, 40, 70, 30, 1, False),
394                         ("Jr v2", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
395                         ("LC v1", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
396                         ("LC v2", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
397                         ("Plus v1", 200, 200, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
398                         ("Plus v2", 200, 200, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
399                         ("Plus v2.1", 185, 220, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
400                         ("Plus v2.2 (Model 1404/140422/140501/140507)", 250, 250, 250, 0.4, 1.75, 208, 40, 70, 30, 1, True),
401                         ("Go v2 Large", 505, 306, 310, 0.4, 1.75, 208, 35, 70, 30, 1, True),
402                 ]
403
404                 super(PrintrbotPage, self).__init__(parent, _("Printrbot Selection"))
405                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Printrbot_logo.png')))
406                 self.AddText(_("Select which Printrbot machine you have:"))
407                 self._items = []
408                 for printer in self._printer_info:
409                         if printer[0].startswith(":"):
410                                 self.AddSeperator()
411                                 self.AddText(printer[0][1:])
412                         else:
413                                 item = self.AddRadioButton(printer[0])
414                                 item.data = printer[1:]
415                                 self._items.append(item)
416
417         def StoreData(self):
418                 profile.putMachineSetting('machine_name', 'Printrbot ???')
419                 for item in self._items:
420                         if item.GetValue():
421                                 data = item.data
422                                 profile.putMachineSetting('machine_name', 'Printrbot ' + item.GetLabel())
423                                 profile.putMachineSetting('machine_width', data[0])
424                                 profile.putMachineSetting('machine_depth', data[1])
425                                 profile.putMachineSetting('machine_height', data[2])
426                                 profile.putProfileSetting('nozzle_size', data[3])
427                                 profile.putProfileSetting('filament_diameter', data[4])
428                                 profile.putProfileSetting('print_temperature', data[5])
429                                 profile.putProfileSetting('print_speed', data[6])
430                                 profile.putProfileSetting('travel_speed', data[7])
431                                 profile.putProfileSetting('retraction_speed', data[8])
432                                 profile.putProfileSetting('retraction_amount', data[9])
433                                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
434                                 profile.putMachineSetting('has_heated_bed', 'False')
435                                 profile.putMachineSetting('machine_center_is_zero', 'False')
436                                 profile.putMachineSetting('extruder_head_size_min_x', '0')
437                                 profile.putMachineSetting('extruder_head_size_min_y', '0')
438                                 profile.putMachineSetting('extruder_head_size_max_x', '0')
439                                 profile.putMachineSetting('extruder_head_size_max_y', '0')
440                                 profile.putMachineSetting('extruder_head_size_height', '0')
441                                 if data[10]:
442                                         profile.setAlterationFile('start.gcode', """;Sliced at: {day} {date} {time}
443 ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}
444 ;Print time: {print_time}
445 ;Filament used: {filament_amount}m {filament_weight}g
446 ;Filament cost: {filament_cost}
447 ;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line
448 ;M109 S{print_temperature} ;Uncomment to add your own temperature line
449 G21        ;metric values
450 G90        ;absolute positioning
451 M82        ;set extruder to absolute mode
452 M107       ;start with the fan off
453 G28 X0 Y0  ;move X/Y to min endstops
454 G28 Z0     ;move Z to min endstops
455 G29        ;Run the auto bed leveling
456 G1 Z15.0 F{travel_speed} ;move the platform down 15mm
457 G92 E0                  ;zero the extruded length
458 G1 F200 E3              ;extrude 3mm of feed stock
459 G92 E0                  ;zero the extruded length again
460 G1 F{travel_speed}
461 ;Put printing message on LCD screen
462 M117 Printing...
463 """)
464
465 class OtherMachineSelectPage(InfoPage):
466         def __init__(self, parent):
467                 super(OtherMachineSelectPage, self).__init__(parent, _("Other machine information"))
468                 self.AddText(_("The following pre-defined machine profiles are available"))
469                 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."))
470                 self.options = []
471                 machines = resources.getDefaultMachineProfiles()
472                 machines.sort()
473                 for filename in machines:
474                         name = os.path.splitext(os.path.basename(filename))[0]
475                         item = self.AddRadioButton(name)
476                         item.filename = filename
477                         item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
478                         self.options.append(item)
479                 self.AddSeperator()
480                 item = self.AddRadioButton(_('Custom...'))
481                 item.SetValue(True)
482                 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
483
484         def OnProfileSelect(self, e):
485                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
486
487         def OnOtherSelect(self, e):
488                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
489
490         def StoreData(self):
491                 for option in self.options:
492                         if option.GetValue():
493                                 profile.loadProfile(option.filename)
494                                 profile.loadMachineSettings(option.filename)
495
496 class OtherMachineInfoPage(InfoPage):
497         def __init__(self, parent):
498                 super(OtherMachineInfoPage, self).__init__(parent, _("Cura Ready!"))
499                 self.AddText(_("Cura is now ready to be used!"))
500
501 class CustomRepRapInfoPage(InfoPage):
502         def __init__(self, parent):
503                 super(CustomRepRapInfoPage, self).__init__(parent, _("Custom RepRap information"))
504                 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
505                 self.AddText(_("Be sure to review the default profile before running it on your machine."))
506                 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
507                 self.AddSeperator()
508                 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
509                 self.AddSeperator()
510                 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
511                 self.machineWidth = self.AddLabelTextCtrl(_("Machine width X (mm)"), "80")
512                 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth Y (mm)"), "80")
513                 self.machineHeight = self.AddLabelTextCtrl(_("Machine height Z (mm)"), "55")
514                 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
515                 self.heatedBed = self.AddCheckbox(_("Heated bed"))
516                 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
517
518         def StoreData(self):
519                 profile.putMachineSetting('machine_name', self.machineName.GetValue())
520                 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
521                 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
522                 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
523                 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
524                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
525                 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
526                 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
527                 profile.putMachineSetting('extruder_head_size_min_x', '0')
528                 profile.putMachineSetting('extruder_head_size_min_y', '0')
529                 profile.putMachineSetting('extruder_head_size_max_x', '0')
530                 profile.putMachineSetting('extruder_head_size_max_y', '0')
531                 profile.putMachineSetting('extruder_head_size_height', '0')
532                 profile.checkAndUpdateMachineName()
533
534 class MachineSelectPage(InfoPage):
535         def __init__(self, parent):
536                 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
537                 self.AddText(_("What kind of machine do you have:"))
538
539                 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2")
540                 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
541                 self.Ultimaker2ExtRadio = self.AddRadioButton("Ultimaker2extended")
542                 self.Ultimaker2ExtRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
543                 self.Ultimaker2GoRadio = self.AddRadioButton("Ultimaker2go")
544                 self.Ultimaker2GoRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
545                 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
546                 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
547                 self.UltimakerOPRadio = self.AddRadioButton("Ultimaker Original+")
548                 self.UltimakerOPRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerOPSelect)
549                 self.PrintrbotRadio = self.AddRadioButton("Printrbot")
550                 self.PrintrbotRadio.Bind(wx.EVT_RADIOBUTTON, self.OnPrintrbotSelect)
551                 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot, Witbox)"))
552                 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
553
554         def OnUltimaker2Select(self, e):
555                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
556
557         def OnUltimakerSelect(self, e):
558                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
559
560         def OnUltimakerOPSelect(self, e):
561                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
562
563         def OnPrintrbotSelect(self, e):
564                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().printrbotSelectType)
565
566         def OnOtherSelect(self, e):
567                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
568
569         def StoreData(self):
570                 profile.putProfileSetting('retraction_enable', 'True')
571                 if self.Ultimaker2Radio.GetValue() or self.Ultimaker2GoRadio.GetValue() or self.Ultimaker2ExtRadio.GetValue():
572                         if self.Ultimaker2Radio.GetValue():
573                                 profile.putMachineSetting('machine_width', '230')
574                                 profile.putMachineSetting('machine_depth', '225')
575                                 profile.putMachineSetting('machine_height', '205')
576                                 profile.putMachineSetting('machine_name', 'ultimaker2')
577                                 profile.putMachineSetting('machine_type', 'ultimaker2')
578                                 profile.putMachineSetting('has_heated_bed', 'True')
579                         if self.Ultimaker2GoRadio.GetValue():
580                                 profile.putMachineSetting('machine_width', '120')
581                                 profile.putMachineSetting('machine_depth', '120')
582                                 profile.putMachineSetting('machine_height', '115')
583                                 profile.putMachineSetting('machine_name', 'ultimaker2go')
584                                 profile.putMachineSetting('machine_type', 'ultimaker2go')
585                                 profile.putMachineSetting('has_heated_bed', 'False')
586                         if self.Ultimaker2ExtRadio.GetValue():
587                                 profile.putMachineSetting('machine_width', '230')
588                                 profile.putMachineSetting('machine_depth', '225')
589                                 profile.putMachineSetting('machine_height', '315')
590                                 profile.putMachineSetting('machine_name', 'ultimaker2extended')
591                                 profile.putMachineSetting('machine_type', 'ultimaker2extended')
592                                 profile.putMachineSetting('has_heated_bed', 'False')
593                         profile.putMachineSetting('machine_center_is_zero', 'False')
594                         profile.putMachineSetting('gcode_flavor', 'UltiGCode')
595                         profile.putMachineSetting('extruder_head_size_min_x', '40.0')
596                         profile.putMachineSetting('extruder_head_size_min_y', '10.0')
597                         profile.putMachineSetting('extruder_head_size_max_x', '60.0')
598                         profile.putMachineSetting('extruder_head_size_max_y', '30.0')
599                         profile.putMachineSetting('extruder_head_size_height', '48.0')
600                         profile.putProfileSetting('nozzle_size', '0.4')
601                         profile.putProfileSetting('fan_full_height', '5.0')
602                         profile.putMachineSetting('extruder_offset_x1', '18.0')
603                         profile.putMachineSetting('extruder_offset_y1', '0.0')
604                 elif self.UltimakerRadio.GetValue():
605                         profile.putMachineSetting('machine_width', '205')
606                         profile.putMachineSetting('machine_depth', '205')
607                         profile.putMachineSetting('machine_height', '200')
608                         profile.putMachineSetting('machine_name', 'ultimaker original')
609                         profile.putMachineSetting('machine_type', 'ultimaker')
610                         profile.putMachineSetting('machine_center_is_zero', 'False')
611                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
612                         profile.putProfileSetting('nozzle_size', '0.4')
613                         profile.putMachineSetting('extruder_head_size_min_x', '75.0')
614                         profile.putMachineSetting('extruder_head_size_min_y', '18.0')
615                         profile.putMachineSetting('extruder_head_size_max_x', '18.0')
616                         profile.putMachineSetting('extruder_head_size_max_y', '35.0')
617                         profile.putMachineSetting('extruder_head_size_height', '55.0')
618                 elif self.UltimakerOPRadio.GetValue():
619                         profile.putMachineSetting('machine_width', '205')
620                         profile.putMachineSetting('machine_depth', '205')
621                         profile.putMachineSetting('machine_height', '200')
622                         profile.putMachineSetting('machine_name', 'ultimaker original+')
623                         profile.putMachineSetting('machine_type', 'ultimaker_plus')
624                         profile.putMachineSetting('machine_center_is_zero', 'False')
625                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
626                         profile.putProfileSetting('nozzle_size', '0.4')
627                         profile.putMachineSetting('extruder_head_size_min_x', '75.0')
628                         profile.putMachineSetting('extruder_head_size_min_y', '18.0')
629                         profile.putMachineSetting('extruder_head_size_max_x', '18.0')
630                         profile.putMachineSetting('extruder_head_size_max_y', '35.0')
631                         profile.putMachineSetting('extruder_head_size_height', '55.0')
632                         profile.putMachineSetting('has_heated_bed', 'True')
633                         profile.putMachineSetting('extruder_amount', '1')
634                         profile.putProfileSetting('retraction_enable', 'True')
635                 else:
636                         profile.putMachineSetting('machine_width', '80')
637                         profile.putMachineSetting('machine_depth', '80')
638                         profile.putMachineSetting('machine_height', '60')
639                         profile.putMachineSetting('machine_name', 'reprap')
640                         profile.putMachineSetting('machine_type', 'reprap')
641                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
642                         profile.putPreference('startMode', 'Normal')
643                         profile.putProfileSetting('nozzle_size', '0.5')
644                 profile.checkAndUpdateMachineName()
645                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
646
647 class SelectParts(InfoPage):
648         def __init__(self, parent):
649                 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
650                 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."))
651                 self.AddSeperator()
652                 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
653                 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
654                 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
655                 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
656                 self.AddSeperator()
657                 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."))
658                 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
659                 self.springExtruder.SetValue(True)
660
661         def StoreData(self):
662                 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
663                 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
664                         profile.putMachineSetting('has_heated_bed', 'True')
665                 else:
666                         profile.putMachineSetting('has_heated_bed', 'False')
667                 if self.dualExtrusion.GetValue():
668                         profile.putMachineSetting('extruder_amount', '2')
669                         profile.putMachineSetting('machine_depth', '195')
670                 else:
671                         profile.putMachineSetting('extruder_amount', '1')
672                 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
673                         profile.putProfileSetting('retraction_enable', 'True')
674                 else:
675                         profile.putProfileSetting('retraction_enable', 'False')
676
677
678 class UltimakerFirmwareUpgradePage(InfoPage):
679         def __init__(self, parent):
680                 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
681                 self.AddText(_("Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the stepper motors, regulates the temperature\nand ultimately makes your printer work."))
682                 self.AddHiddenSeperator()
683                 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
684                 self.AddHiddenSeperator()
685                 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."))
686                 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Upgrade to Marlin firmware'), _('Skip upgrade'))
687                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
688                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
689                 self.AddHiddenSeperator()
690                 if profile.getMachineSetting('machine_type') == 'ultimaker':
691                         self.AddText(_("Do not upgrade to this firmware if:"))
692                         self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
693                         self.AddText(_("* Build your own heated bed"))
694                         self.AddText(_("* Have other changes in the firmware"))
695 #               button = self.AddButton('Goto this page for a custom firmware')
696 #               button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
697
698         def AllowNext(self):
699                 return False
700
701         def OnUpgradeClick(self, e):
702                 if firmwareInstall.InstallFirmware():
703                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
704
705         def OnSkipClick(self, e):
706                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
707                 self.GetParent().ShowPage(self.GetNext())
708
709         def OnUrlClick(self, e):
710                 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
711
712 class UltimakerCheckupPage(InfoPage):
713         def __init__(self, parent):
714                 super(UltimakerCheckupPage, self).__init__(parent, _("Ultimaker Checkup"))
715
716                 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
717                 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
718                 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
719                 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
720                 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
721                 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
722                 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
723                 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
724                 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
725                 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
726
727                 self.AddText(
728                         _("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."))
729                 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
730                 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
731                 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
732                 self.AddSeperator()
733                 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
734                 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
735                 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
736                 self.AddSeperator()
737                 self.infoBox = self.AddInfoBox()
738                 self.machineState = self.AddText("")
739                 self.temperatureLabel = self.AddText("")
740                 self.errorLogButton = self.AddButton(_("Show error log"))
741                 self.errorLogButton.Show(False)
742                 self.AddSeperator()
743                 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
744                 self.comm = None
745                 self.xMinStop = False
746                 self.xMaxStop = False
747                 self.yMinStop = False
748                 self.yMaxStop = False
749                 self.zMinStop = False
750                 self.zMaxStop = False
751
752                 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
753
754         def __del__(self):
755                 if self.comm is not None:
756                         self.comm.close()
757
758         def AllowNext(self):
759                 self.endstopBitmap.Show(False)
760                 return False
761
762         def OnSkipClick(self, e):
763                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
764                 self.GetParent().ShowPage(self.GetNext())
765
766         def OnCheckClick(self, e=None):
767                 self.errorLogButton.Show(False)
768                 if self.comm is not None:
769                         self.comm.close()
770                         del self.comm
771                         self.comm = None
772                         wx.CallAfter(self.OnCheckClick)
773                         return
774                 self.infoBox.SetBusy(_("Connecting to machine."))
775                 self.commState.SetBitmap(self.unknownBitmap)
776                 self.tempState.SetBitmap(self.unknownBitmap)
777                 self.stopState.SetBitmap(self.unknownBitmap)
778                 self.checkupState = 0
779                 self.checkExtruderNr = 0
780                 self.comm = machineCom.MachineCom(callbackObject=self)
781
782         def OnErrorLog(self, e):
783                 printWindow.LogWindow('\n'.join(self.comm.getLog()))
784
785         def mcLog(self, message):
786                 pass
787
788         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
789                 if not self.comm.isOperational():
790                         return
791                 if self.checkupState == 0:
792                         self.tempCheckTimeout = 20
793                         if temp[self.checkExtruderNr] > 70:
794                                 self.checkupState = 1
795                                 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
796                                 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
797                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
798                         else:
799                                 self.startTemp = temp[self.checkExtruderNr]
800                                 self.checkupState = 2
801                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
802                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
803                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
804                 elif self.checkupState == 1:
805                         if temp[self.checkExtruderNr] < 60:
806                                 self.startTemp = temp[self.checkExtruderNr]
807                                 self.checkupState = 2
808                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
809                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
810                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
811                 elif self.checkupState == 2:
812                         #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
813                         if temp[self.checkExtruderNr] > self.startTemp + 40:
814                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
815                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
816                                 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
817                                         self.checkExtruderNr = 0
818                                         self.checkupState = 3
819                                         wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
820                                         wx.CallAfter(self.endstopBitmap.Show, True)
821                                         wx.CallAfter(self.Layout)
822                                         self.comm.sendCommand('M119')
823                                         wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
824                                 else:
825                                         self.checkupState = 0
826                                         self.checkExtruderNr += 1
827                         else:
828                                 self.tempCheckTimeout -= 1
829                                 if self.tempCheckTimeout < 1:
830                                         self.checkupState = -1
831                                         wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
832                                         wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
833                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
834                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
835                 elif self.checkupState >= 3 and self.checkupState < 10:
836                         self.comm.sendCommand('M119')
837                 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
838
839         def mcStateChange(self, state):
840                 if self.comm is None:
841                         return
842                 if self.comm.isOperational():
843                         wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
844                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
845                 elif self.comm.isError():
846                         wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
847                         wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
848                         wx.CallAfter(self.endstopBitmap.Show, False)
849                         wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
850                         wx.CallAfter(self.errorLogButton.Show, True)
851                         wx.CallAfter(self.Layout)
852                 else:
853                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
854
855         def mcMessage(self, message):
856                 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
857                         for data in message.split(' '):
858                                 if ':' in data:
859                                         tag, value = data.split(':', 1)
860                                         if tag == 'x_min':
861                                                 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
862                                         if tag == 'x_max':
863                                                 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
864                                         if tag == 'y_min':
865                                                 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
866                                         if tag == 'y_max':
867                                                 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
868                                         if tag == 'z_min':
869                                                 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
870                                         if tag == 'z_max':
871                                                 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
872                         if ':' in message:
873                                 tag, value = map(str.strip, message.split(':', 1))
874                                 if tag == 'x_min':
875                                         self.xMinStop = (value == 'H' or value == 'TRIGGERED')
876                                 if tag == 'x_max':
877                                         self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
878                                 if tag == 'y_min':
879                                         self.yMinStop = (value == 'H' or value == 'TRIGGERED')
880                                 if tag == 'y_max':
881                                         self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
882                                 if tag == 'z_min':
883                                         self.zMinStop = (value == 'H' or value == 'TRIGGERED')
884                                 if tag == 'z_max':
885                                         self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
886                         if 'z_max' in message:
887                                 self.comm.sendCommand('M119')
888
889                         if self.checkupState == 3:
890                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
891                                         if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
892                                                 self.checkupState = 5
893                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
894                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
895                                         else:
896                                                 self.checkupState = 4
897                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
898                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
899                         elif self.checkupState == 4:
900                                 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
901                                         self.checkupState = 5
902                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
903                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
904                         elif self.checkupState == 5:
905                                 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
906                                         self.checkupState = 6
907                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
908                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
909                         elif self.checkupState == 6:
910                                 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
911                                         if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
912                                                 self.checkupState = 8
913                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
914                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
915                                         else:
916                                                 self.checkupState = 7
917                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
918                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
919                         elif self.checkupState == 7:
920                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
921                                         self.checkupState = 8
922                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
923                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
924                         elif self.checkupState == 8:
925                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
926                                         if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
927                                                 self.checkupState = 10
928                                                 self.comm.close()
929                                                 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
930                                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
931                                                 wx.CallAfter(self.endstopBitmap.Show, False)
932                                                 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
933                                                 wx.CallAfter(self.OnSkipClick, None)
934                                         else:
935                                                 self.checkupState = 9
936                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
937                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
938                         elif self.checkupState == 9:
939                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
940                                         self.checkupState = 10
941                                         self.comm.close()
942                                         wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
943                                         wx.CallAfter(self.infoBox.SetReadyIndicator)
944                                         wx.CallAfter(self.endstopBitmap.Show, False)
945                                         wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
946                                         wx.CallAfter(self.OnSkipClick, None)
947
948         def mcProgress(self, lineNr):
949                 pass
950
951         def mcZChange(self, newZ):
952                 pass
953
954
955 class UltimakerCalibrationPage(InfoPage):
956         def __init__(self, parent):
957                 super(UltimakerCalibrationPage, self).__init__(parent, _("Ultimaker Calibration"))
958
959                 self.AddText("Your Ultimaker requires some calibration.")
960                 self.AddText("This calibration is needed for a proper extrusion amount.")
961                 self.AddSeperator()
962                 self.AddText("The following values are needed:")
963                 self.AddText("* Diameter of filament")
964                 self.AddText("* Number of steps per mm of filament extrusion")
965                 self.AddSeperator()
966                 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
967                 self.AddSeperator()
968                 self.AddText("First we need the diameter of your filament:")
969                 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
970                 self.AddText(
971                         "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.")
972                 self.AddText("Note: This value can be changed later at any time.")
973
974         def StoreData(self):
975                 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
976
977
978 class UltimakerCalibrateStepsPerEPage(InfoPage):
979         def __init__(self, parent):
980                 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, _("Ultimaker Calibration"))
981
982                 #if profile.getMachineSetting('steps_per_e') == '0':
983                 #       profile.putMachineSetting('steps_per_e', '865.888')
984
985                 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
986                 self.AddText(_("First remove any filament from your machine."))
987                 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
988                 self.AddText(_("We'll push the filament 100mm"))
989                 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
990                 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
991                 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
992                 self.AddText(_("This results in the following steps per E:"))
993                 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
994                 self.AddText(_("You can repeat these steps to get better calibration."))
995                 self.AddSeperator()
996                 self.AddText(
997                         _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
998                 self.heatButton = self.AddButton(_("Heatup for filament removal"))
999
1000                 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
1001                 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
1002                 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
1003
1004         def OnSaveLengthClick(self, e):
1005                 currentEValue = float(self.stepsPerEInput.GetValue())
1006                 realExtrudeLength = float(self.lengthInput.GetValue())
1007                 newEValue = currentEValue * 100 / realExtrudeLength
1008                 self.stepsPerEInput.SetValue(str(newEValue))
1009                 self.lengthInput.SetValue("100")
1010
1011         def OnExtrudeClick(self, e):
1012                 t = threading.Thread(target=self.OnExtrudeRun)
1013                 t.daemon = True
1014                 t.start()
1015
1016         def OnExtrudeRun(self):
1017                 self.heatButton.Enable(False)
1018                 self.extrudeButton.Enable(False)
1019                 currentEValue = float(self.stepsPerEInput.GetValue())
1020                 self.comm = machineCom.MachineCom()
1021                 if not self.comm.isOpen():
1022                         wx.MessageBox(
1023                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
1024                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
1025                         self.heatButton.Enable(True)
1026                         self.extrudeButton.Enable(True)
1027                         return
1028                 while True:
1029                         line = self.comm.readline()
1030                         if line == '':
1031                                 return
1032                         if 'start' in line:
1033                                 break
1034                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
1035                 time.sleep(3)
1036
1037                 self.sendGCommand('M302') #Disable cold extrusion protection
1038                 self.sendGCommand("M92 E%f" % (currentEValue))
1039                 self.sendGCommand("G92 E0")
1040                 self.sendGCommand("G1 E100 F600")
1041                 time.sleep(15)
1042                 self.comm.close()
1043                 self.extrudeButton.Enable()
1044                 self.heatButton.Enable()
1045
1046         def OnHeatClick(self, e):
1047                 t = threading.Thread(target=self.OnHeatRun)
1048                 t.daemon = True
1049                 t.start()
1050
1051         def OnHeatRun(self):
1052                 self.heatButton.Enable(False)
1053                 self.extrudeButton.Enable(False)
1054                 self.comm = machineCom.MachineCom()
1055                 if not self.comm.isOpen():
1056                         wx.MessageBox(
1057                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
1058                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
1059                         self.heatButton.Enable(True)
1060                         self.extrudeButton.Enable(True)
1061                         return
1062                 while True:
1063                         line = self.comm.readline()
1064                         if line == '':
1065                                 self.heatButton.Enable(True)
1066                                 self.extrudeButton.Enable(True)
1067                                 return
1068                         if 'start' in line:
1069                                 break
1070                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
1071                 time.sleep(3)
1072
1073                 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
1074                 wx.MessageBox(
1075                         'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
1076                         'Machine heatup', wx.OK | wx.ICON_INFORMATION)
1077                 self.sendGCommand('M104 S0')
1078                 time.sleep(1)
1079                 self.comm.close()
1080                 self.heatButton.Enable(True)
1081                 self.extrudeButton.Enable(True)
1082
1083         def sendGCommand(self, cmd):
1084                 self.comm.sendCommand(cmd) #Disable cold extrusion protection
1085                 while True:
1086                         line = self.comm.readline()
1087                         if line == '':
1088                                 return
1089                         if line.startswith('ok'):
1090                                 break
1091
1092         def StoreData(self):
1093                 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
1094
1095 class Ultimaker2ReadyPage(InfoPage):
1096         def __init__(self, parent):
1097                 super(Ultimaker2ReadyPage, self).__init__(parent, _("Ultimaker2"))
1098                 self.AddText(_('Congratulations on your the purchase of your brand new Ultimaker2.'))
1099                 self.AddText(_('Cura is now ready to be used with your Ultimaker2.'))
1100                 self.AddSeperator()
1101
1102 class LulzbotMachineSelectPage(InfoPage):
1103         IMAGE_WIDTH=300
1104         IMAGE_HEIGHT=200
1105
1106         def __init__(self, parent):
1107                 super(LulzbotMachineSelectPage, self).__init__(parent, _("Select your machine"))
1108
1109                 self.panel = self.AddPanel()
1110
1111                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1112                 self.LulzbotMini = self.AddImageButton(self.panel, 0, 0, _("LulzBot Mini"),
1113                                                                                            'Lulzbot_mini.jpg', image_size, style=ImageButton.IB_GROUP)
1114                 self.LulzbotMini.OnSelected(self.OnLulzbotMiniSelected)
1115                 
1116                 self.LulzbotTaz6 = self.AddImageButton(self.panel, 0, 1, _("LulzBot TAZ 6"),
1117                                                                                            'Lulzbot_TAZ6.jpg', image_size)
1118                 self.LulzbotTaz6.OnSelected(self.OnLulzbotTaz6Selected)
1119                 
1120                 self.LulzbotTaz = self.AddImageButton(self.panel, 1, 0, _("LulzBot TAZ 4 or 5"),
1121                                                                                            'Lulzbot_TAZ5.jpg', image_size)
1122                 self.LulzbotTaz.OnSelected(self.OnLulzbotTazSelected)
1123                 
1124                 self.OtherPrinters = self.AddImageButton(self.panel, 1, 1, _("Other Printers"),
1125                                                                                                  'Generic-3D-Printer.png', image_size)
1126                 self.OtherPrinters.OnSelected(self.OnOthersSelected)
1127                 self.LulzbotMini.SetValue(True)
1128
1129         def OnPageShown(self):
1130                 self.LulzbotMini.TriggerGroupCallbacks()
1131
1132         def OnOthersSelected(self):
1133                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().machineSelectPage)
1134
1135         def OnLulzbotMiniSelected(self):
1136                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotMiniToolheadPage)
1137                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotMiniToolheadPage,
1138                                                                                  self.GetParent().lulzbotReadyPage)
1139
1140         def OnLulzbotTaz6Selected(self):
1141                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz6ToolheadPage)
1142
1143         def OnLulzbotTazSelected(self):
1144                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTazSelectPage)
1145
1146         def AllowNext(self):
1147                 return True
1148
1149         def AllowBack(self):
1150                 return False
1151
1152         def StoreData(self):
1153                 if self.LulzbotTaz.GetValue() or self.LulzbotMini.GetValue() or self.LulzbotTaz6.GetValue():
1154                         if self.LulzbotTaz.GetValue():
1155                                 profile.putMachineSetting('machine_width', '290')
1156                                 profile.putMachineSetting('machine_depth', '275')
1157                                 profile.putMachineSetting('machine_height', '250')
1158                                 profile.putMachineSetting('serial_baud', '115200')
1159                                 profile.putMachineSetting('extruder_head_size_min_x', '0.0')
1160                                 profile.putMachineSetting('extruder_head_size_max_x', '0.0')
1161                                 profile.putMachineSetting('extruder_head_size_min_y', '0.0')
1162                                 profile.putMachineSetting('extruder_head_size_max_y', '0.0')
1163                                 profile.putMachineSetting('extruder_head_size_height', '0.0')
1164                         elif self.LulzbotTaz6.GetValue():
1165                                 profile.putMachineSetting('machine_width', '280')
1166                                 profile.putMachineSetting('machine_depth', '280')
1167                                 profile.putMachineSetting('machine_height', '250')
1168                                 profile.putMachineSetting('serial_baud', '250000')
1169                                 profile.putMachineSetting('extruder_head_size_min_x', '0.0')
1170                                 profile.putMachineSetting('extruder_head_size_max_x', '0.0')
1171                                 profile.putMachineSetting('extruder_head_size_min_y', '0.0')
1172                                 profile.putMachineSetting('extruder_head_size_max_y', '0.0')
1173                                 profile.putMachineSetting('extruder_head_size_height', '0.0')
1174                         else:
1175                                 # Nozzle diameter and machine type will be set in the toolhead selection page
1176                                 profile.putMachineSetting('machine_name', 'LulzBot Mini')
1177                                 profile.putMachineSetting('machine_width', '155')
1178                                 profile.putMachineSetting('machine_depth', '155')
1179                                 profile.putMachineSetting('machine_height', '158')
1180                                 profile.putMachineSetting('serial_baud', '115200')
1181                                 profile.putMachineSetting('extruder_head_size_min_x', '40')
1182                                 profile.putMachineSetting('extruder_head_size_max_x', '75')
1183                                 profile.putMachineSetting('extruder_head_size_min_y', '25')
1184                                 profile.putMachineSetting('extruder_head_size_max_y', '55')
1185                                 profile.putMachineSetting('extruder_head_size_height', '17')
1186
1187                         profile.putMachineSetting('machine_center_is_zero', 'False')
1188                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
1189                         profile.putMachineSetting('has_heated_bed', 'True')
1190                         profile.putProfileSetting('retraction_enable', 'True')
1191                         profile.putPreference('startMode', 'Simple')
1192                         profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
1193                         profile.checkAndUpdateMachineName()
1194
1195 class LulzbotReadyPage(InfoPage):
1196         def __init__(self, parent):
1197                 super(LulzbotReadyPage, self).__init__(parent, _("LulzBot TAZ/Mini"))
1198                 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
1199                 self.AddSeperator()
1200                 self.AddText(_('For more information about using Cura with your LulzBot'))
1201                 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
1202                 self.AddSeperator()
1203
1204 class LulzbotMiniToolheadSelectPage(InfoPage):
1205         def __init__(self, parent, allowBack = True):
1206                 super(LulzbotMiniToolheadSelectPage, self).__init__(parent, _("LulzBot Mini Tool Head Selection"))
1207
1208                 self.allowBack = allowBack
1209                 self.panel = self.AddPanel()
1210                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1211                 self.standard = self.AddImageButton(self.panel, 0, 0, _('Standard LulzBot Mini'),
1212                                                                                         'Lulzbot_mini.jpg', image_size,
1213                                                                                         style=ImageButton.IB_GROUP)
1214                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('LulzBot Mini with Flexystruder'),
1215                                                                                         'Lulzbot_Toolhead_Mini_Flexystruder.jpg', image_size)
1216                 self.standard.SetValue(True)
1217
1218         def AllowBack(self):
1219                 return self.allowBack
1220
1221         def StoreData(self):
1222                 if self.standard.GetValue():
1223                         profile.putProfileSetting('nozzle_size', '0.5')
1224                         profile.putMachineSetting('extruder_amount', '1')
1225                         profile.putMachineSetting('toolhead', 'Single Extruder v2')
1226                         profile.putMachineSetting('toolhead_shortname', '')
1227                         profile.putMachineSetting('machine_type', 'lulzbot_mini')
1228                 else:
1229                         profile.putProfileSetting('nozzle_size', '0.6')
1230                         profile.putMachineSetting('extruder_amount', '1')
1231                         profile.putMachineSetting('toolhead', 'Flexystruder v2')
1232                         profile.putMachineSetting('toolhead_shortname', 'Flexystruder')
1233                         profile.putMachineSetting('machine_type', 'lulzbot_mini_flexystruder')
1234
1235 class LulzbotTaz6ToolheadSelectPage(InfoPage):
1236         def __init__(self, parent, allowBack = True):
1237                 super(LulzbotTaz6ToolheadSelectPage, self).__init__(parent, _("LulzBot TAZ 6 Tool Head Selection"))
1238
1239                 self.panel = self.AddPanel()
1240                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1241                 self.taz6 = self.AddImageButton(self.panel, 0, 0, _('Tilapia'),
1242                                                                                 'Lulzbot_Toolhead_TAZ_Tilapia.jpg', image_size,
1243                                                                                 style=ImageButton.IB_GROUP)
1244                 self.taz6.OnSelected(self.OnTilapiaSelected)
1245                 self.taz6.SetValue(True)
1246
1247         def OnPageShown(self):
1248                 self.taz6.TriggerGroupCallbacks()
1249
1250         def OnTilapiaSelected(self):
1251                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1252
1253         def StoreData(self):
1254                 profile.putProfileSetting('nozzle_size',  '0.5')
1255                 profile.putMachineSetting('extruder_amount', '1')
1256                 profile.putMachineSetting('toolhead', 'Single Extruder Tilapia')
1257                 profile.putMachineSetting('toolhead_shortname', 'Tilapia')
1258                 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_6_Single_Tilapia')
1259                 profile.putMachineSetting('machine_name', 'LulzBot TAZ 6')
1260
1261 class LulzbotTazSelectPage(InfoPage):
1262         def __init__(self, parent):
1263                 super(LulzbotTazSelectPage, self).__init__(parent, _("LulzBot TAZ 4-5 Selection"))
1264
1265                 self.panel = self.AddPanel()
1266                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1267                 self.taz5 = self.AddImageButton(self.panel, 0, 0, _('Stock TAZ 5 (PEI && v2)'),
1268                                                                                 'Lulzbot_TAZ_5_Hex_and_PEI.jpg', image_size,
1269                                                                                 style=ImageButton.IB_GROUP)
1270                 self.taz5.OnSelected(self.OnTaz5Selected)
1271                 self.taz4 = self.AddImageButton(self.panel, 0, 1, _('Stock TAZ 4 (PET && v1)'),
1272                                                                                 'Lulzbot_TAZ_4_Buda_and_PET.jpg', image_size)
1273                 self.taz4.OnSelected(self.OnTaz4Selected)
1274                 self.modified = self.AddImageButton(self.panel, 1, 0, _('Modified LulzBot TAZ 4 or 5'),
1275                                                                                         'Lulzbot_TAZ5.jpg', image_size)
1276                 self.modified.OnSelected(self.OnModifiedSelected)
1277                 self.taz5.SetValue(True)
1278
1279         def OnPageShown(self):
1280                 self.taz5.TriggerGroupCallbacks()
1281
1282         def OnTaz5Selected(self):
1283                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1284                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage,
1285                                                                                  self.GetParent().lulzbotReadyPage)
1286
1287         def OnTaz4Selected(self):
1288                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1289
1290         def OnModifiedSelected(self):
1291                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTazBedSelectPage)
1292                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTazBedSelectPage,
1293                                                                                  self.GetParent().lulzbotTazHotendPage)
1294
1295         def StoreData(self):
1296                 if self.taz5.GetValue():
1297                         profile.putProfileSetting('nozzle_size',  '0.5')
1298                         profile.putMachineSetting('extruder_amount', '1')
1299                         profile.putMachineSetting('toolhead', 'Single Extruder V2')
1300                         profile.putMachineSetting('toolhead_shortname', '')
1301                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_SingleV2')
1302                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5')
1303                 elif self.taz4.GetValue():
1304                         profile.putProfileSetting('nozzle_size', '0.35')
1305                         profile.putMachineSetting('extruder_amount', '1')
1306                         profile.putMachineSetting('toolhead', 'Single Extruder V1')
1307                         profile.putMachineSetting('toolhead_shortname', '')
1308                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4_SingleV1')
1309                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1310
1311 class LulzbotTazBedSelectPage(InfoPage):
1312         def __init__(self, parent):
1313                 super(LulzbotTazBedSelectPage, self).__init__(parent, _("Bed Surface"))
1314
1315                 self.panel = self.AddPanel()
1316                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1317                 self.pei = self.AddImageButton(self.panel, 0, 0, _('PEI'),
1318                                                                            'Lulzbot_TAZ_PEI_Bed.jpg', image_size,
1319                                                                            style=ImageButton.IB_GROUP)
1320                 self.pet = self.AddImageButton(self.panel, 0, 1, _('PET'),
1321                                                                            'Lulzbot_TAZ_PET_Bed.jpg', image_size)
1322                 self.pei.SetValue(True)
1323
1324         def StoreData(self):
1325                 if self.pei.GetValue():
1326                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1327                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5')
1328                 else:
1329                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4')
1330                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1331
1332 class LulzbotTazToolheadSelectPage(InfoPage):
1333         def __init__(self, parent):
1334                 super(LulzbotTazToolheadSelectPage, self).__init__(parent, _("LulzBot TAZ Tool Head Selection"))
1335
1336                 self.panel = self.AddPanel()
1337                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1338                 self.single = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v1'),
1339                                                                                         'Lulzbot_Toolhead_TAZ_Single_v1.jpg', image_size,
1340                                                                                         style=ImageButton.IB_GROUP)
1341                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v1'),
1342                                                                                         'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg', image_size)
1343                 self.dually = self.AddImageButton(self.panel, 1, 0, _('Dual Extruder v1'),
1344                                                                                         'Lulzbot_Toolhead_TAZ_Dual_Extruder_v1.jpg', image_size)
1345                 self.flexydually = self.AddImageButton(self.panel, 1, 1, _('FlexyDually v1'),
1346                                                                                         'Lulzbot_Toolhead_TAZ_FlexyDually_v1.jpg', image_size)
1347                 self.SetVersion(1)
1348                 self.single.SetValue(True)
1349
1350         def SetVersion(self, version):
1351                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1352                 self.single.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Single_v%d.jpg' % version, image_size))
1353                 self.single.SetLabel(_('Single Extruder v%d' % version))
1354                 self.flexy.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Flexystruder_v%d.jpg' % version, image_size))
1355                 self.flexy.SetLabel(_('Flexystruder v%d' % version))
1356                 self.dually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Dual_Extruder_v%d.jpg' % version, image_size))
1357                 self.dually.SetLabel(_('Dual Extruder v%d' % version))
1358                 self.flexydually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_FlexyDually_v%d.jpg' % version, image_size))
1359                 self.flexydually.SetLabel(_('FlexyDually v%d' % version))
1360                 self.version = version
1361                 if version == 1:
1362                         self.single.OnSelected(None)
1363                         self.flexy.OnSelected(None)
1364                         self.dually.OnSelected(None)
1365                         self.flexydually.OnSelected(None)
1366                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1367                 elif version == 2:
1368                         self.single.OnSelected(self.OnSingleV2)
1369                         self.flexy.OnSelected(self.OnNonSingle)
1370                         self.dually.OnSelected(self.OnNonSingle)
1371                         self.flexydually.OnSelected(self.OnNonSingle)
1372                         if self.single.GetValue():
1373                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1374                                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage, self.GetParent().lulzbotFirmwarePage)
1375                         else:
1376                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1377                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotFirmwarePage, self.GetParent().lulzbotReadyPage)
1378
1379         def OnSingleV2(self):
1380                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1381                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage, self.GetParent().lulzbotFirmwarePage)
1382
1383         def OnNonSingle(self):
1384                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1385
1386         def StoreData(self):
1387                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1388                         taz_version = 4
1389                 else:
1390                         taz_version = 5
1391                 version = (taz_version, self.version)
1392                 if self.single.GetValue():
1393                         profile.putProfileSetting('nozzle_size', '0.5' if self.version == 2 else '0.35')
1394                         profile.putMachineSetting('extruder_amount', '1')
1395                         profile.putMachineSetting('toolhead', 'Single Extruder V%d' % self.version)
1396                         profile.putMachineSetting('toolhead_shortname', '')
1397                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_SingleV%d' % version)
1398                 elif self.flexy.GetValue():
1399                         profile.putProfileSetting('nozzle_size', '0.6')
1400                         profile.putMachineSetting('extruder_amount', '1')
1401                         profile.putMachineSetting('toolhead', 'Flexystruder V%d' % self.version)
1402                         profile.putMachineSetting('toolhead_shortname', 'Flexystruder v%d' % self.version)
1403                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexystruderV%d' % version)
1404                 elif self.dually.GetValue():
1405                         profile.putProfileSetting('nozzle_size', '0.5')
1406                         profile.putMachineSetting('extruder_amount', '2')
1407                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1408                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1409                         profile.putMachineSetting('toolhead', 'Dual Extruder V%d' % self.version)
1410                         profile.putMachineSetting('toolhead_shortname', 'Dual v%d' % self.version)
1411                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_DualV%d' % version)
1412                 elif self.flexydually.GetValue():
1413                         profile.putProfileSetting('nozzle_size', '0.6')
1414                         profile.putMachineSetting('extruder_amount', '2')
1415                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1416                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1417                         profile.putMachineSetting('toolhead', 'FlexyDually V%d' % self.version)
1418                         profile.putMachineSetting('toolhead_shortname', 'FlexyDually v%d' % self.version)
1419                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexyDuallyV%d' % version)
1420
1421 class LulzbotHotendSelectPage(InfoPage):
1422         def __init__(self, parent, allowBack = True):
1423                 super(LulzbotHotendSelectPage, self).__init__(parent, _("LulzBot Tool Head Hot end Selection"))
1424
1425                 self.allowBack = allowBack
1426                 self.panel = self.AddPanel()
1427                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1428                 self.v1 = self.AddImageButton(self.panel, 0, 0, _('v1 (Budaschnozzle)'),
1429                                                                                         'Lulzbot_Toolhead_v1.jpg', image_size,
1430                                                                                         style=ImageButton.IB_GROUP)
1431                 self.v2 = self.AddImageButton(self.panel, 0, 1, _('v2 (LulzBot Hexagon)'),
1432                                                                                         'Lulzbot_Toolhead_v2.jpg', image_size)
1433                 self.v1.SetValue(True)
1434
1435         def AllowBack(self):
1436                 return self.allowBack
1437
1438         def StoreData(self):
1439                 self.GetParent().lulzbotTazToolheadPage.SetVersion(1 if self.v1.GetValue() else 2)
1440
1441 class LulzbotTaz5NozzleSelectPage(InfoPage):
1442         url2='http://lulzbot.com/printer-identification'
1443
1444         def __init__(self, parent):
1445                 super(LulzbotTaz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ Single v2 Nozzle Selection"))
1446
1447                 self.AddText(_('Please select your LulzBot Hexagon Hot End\'s nozzle diameter:'))
1448                 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1449                 self.Nozzle35Radio.SetValue(True)
1450                 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1451                 self.AddText(_(' '))
1452                 self.AddSeperator()
1453
1454                 self.AddText(_('If you are not sure which nozzle diameter you have,'))
1455                 self.AddText(_('please check this webpage: '))
1456                 button = self.AddButton(LulzbotTaz5NozzleSelectPage.url2)
1457                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1458
1459         def OnUrlClick(self, e):
1460                 webbrowser.open(LulzbotTaz5NozzleSelectPage.url2)
1461
1462         def StoreData(self):
1463                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1464                         taz_version = 4
1465                 else:
1466                         taz_version = 5
1467                 if self.Nozzle35Radio.GetValue():
1468                         profile.putProfileSetting('nozzle_size', '0.35')
1469                         profile.putMachineSetting('toolhead', 'Single Extruder v2 (0.35mm nozzle)')
1470                         profile.putMachineSetting('toolhead_shortname', '0.35 nozzle')
1471                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_035nozzle' % taz_version)
1472
1473                 else:
1474                         profile.putProfileSetting('nozzle_size', '0.5')
1475                         profile.putMachineSetting('toolhead', 'Single Extruder v2 (0.5mm nozzle)')
1476                         profile.putMachineSetting('toolhead_shortname', '0.5 nozzle')
1477                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_05nozzle' % taz_version)
1478
1479 class LulzbotFirmwareUpdatePage(InfoPage):
1480         def __init__(self, parent):
1481                 super(LulzbotFirmwareUpdatePage, self).__init__(parent, _("LulzBot Firmware Update"))
1482
1483                 self.AddText(_("Your LulzBot printer\'s firmware will now be updated.\n" +
1484                 "Note: this will overwrite your existing firmware."))
1485                 self.AddSeperator()
1486                 self.AddText(_("Follow these steps to prevent writing firmware to the wrong device:\n" +
1487                                            "    1) Unplug all USB devices from your computer\n" +
1488                                            "    2) Plug your 3D Printer into the computer with a USB cable\n" +
1489                                            "    3) Turn on your 3D Printer\n" +
1490                                            "    4) Click \"Flash the firmware\""))
1491                 self.AddHiddenSeperator()
1492                 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Flash the firmware'), _('Skip upgrade'))
1493                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
1494                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
1495
1496         def AllowNext(self):
1497                 return version.isDevVersion()
1498
1499         def OnUpgradeClick(self, e):
1500                 if firmwareInstall.InstallFirmware():
1501                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1502                         self.GetParent().ShowPage(self.GetNext())
1503
1504         def OnSkipClick(self, e):
1505                 dlg = wx.MessageDialog(self,
1506                         _("CAUTION: Updating firmware is necessary when changing the\n" \
1507                           "tool head on your LulzBot desktop 3D Printer." \
1508                           "\n\n" +
1509                           "Are you sure you want to skip the firmware update?"),
1510                         _('Skip firmware update?'),
1511                         wx.YES_NO | wx.ICON_EXCLAMATION)
1512                 skip = dlg.ShowModal() == wx.ID_YES
1513                 dlg.Destroy()
1514                 if skip:
1515                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1516                         self.GetParent().ShowPage(self.GetNext())
1517
1518 class LulzbotChangeToolheadWizard(wx.wizard.Wizard):
1519         def __init__(self):
1520                 super(LulzbotChangeToolheadWizard, self).__init__(None, -1, _("Change LulzBot Tool Head Wizard"))
1521
1522                 self._nozzle_size = profile.getProfileSettingFloat('nozzle_size')
1523                 self._machine_name = profile.getMachineSetting('machine_name')
1524                 self._machine_type = profile.getMachineSetting('machine_type')
1525                 self._extruder_amount = int(profile.getMachineSettingFloat('extruder_amount'))
1526
1527                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1528                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1529                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1530
1531                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1532                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1533                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self, False)
1534                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1535                 self.lulzbotTaz6ToolheadPage = LulzbotTaz6ToolheadSelectPage(self, False)
1536                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self, False)
1537                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1538                 self.lulzbotTazBedSelectPage = LulzbotTazBedSelectPage(self)
1539                 self.lulzbotTazSelectPage = LulzbotTazSelectPage(self)
1540
1541                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotReadyPage)
1542                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1543
1544                 if profile.getMachineSetting('machine_type').startswith('lulzbot_mini'):
1545                         self.RunWizard(self.lulzbotMiniToolheadPage)
1546                 elif profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_5') or \
1547                      profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1548                         self.RunWizard(self.lulzbotTazHotendPage)
1549                 elif profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_6'):
1550                         self.RunWizard(self.lulzbotTaz6ToolheadPage)
1551                 self.Destroy()
1552
1553         def OnPageChanging(self, e):
1554                 e.GetPage().StoreData()
1555
1556         def OnPageChanged(self, e):
1557                 if e.GetPage().AllowNext():
1558                         self.FindWindowById(wx.ID_FORWARD).Enable()
1559                 else:
1560                         self.FindWindowById(wx.ID_FORWARD).Disable()
1561                 if e.GetPage().AllowBack():
1562                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1563                 else:
1564                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1565                 if hasattr(e.GetPage(), 'OnPageShown'):
1566                         e.GetPage().OnPageShown()
1567
1568         def OnCancel(self, e):
1569                 profile.putProfileSetting('nozzle_size', self._nozzle_size)
1570                 profile.putMachineSetting('machine_name', self._machine_name)
1571                 profile.putMachineSetting('machine_type', self._machine_type)
1572                 profile.putMachineSetting('extruder_amount', self._extruder_amount)
1573
1574 class ConfigWizard(wx.wizard.Wizard):
1575         def __init__(self, addNew = False):
1576                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1577
1578                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1579                 if addNew:
1580                         profile.setActiveMachine(profile.getMachineCount())
1581
1582                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1583                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1584                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1585
1586                 self.machineSelectPage = MachineSelectPage(self)
1587                 self.ultimakerSelectParts = SelectParts(self)
1588                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1589                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1590                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1591                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1592                 self.bedLevelPage = bedLevelWizardMain(self)
1593                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1594                 self.printrbotSelectType = PrintrbotPage(self)
1595                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1596                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1597                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1598
1599                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1600                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1601                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1602                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self)
1603                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1604                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self)
1605                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1606                 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1607                 self.lulzbotTazBedSelectPage = LulzbotTazBedSelectPage(self)
1608                 self.lulzbotTazSelectPage = LulzbotTazSelectPage(self)
1609                 self.lulzbotTaz6ToolheadPage = LulzbotTaz6ToolheadSelectPage(self)
1610
1611                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotMiniToolheadPage)
1612                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotReadyPage)
1613                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1614                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1615                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1616                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1617                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1618                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1619                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1620
1621                 self.RunWizard(self.lulzbotMachineSelectPage)
1622                 self.Destroy()
1623
1624         def OnPageChanging(self, e):
1625                 e.GetPage().StoreData()
1626
1627         def OnPageChanged(self, e):
1628                 if e.GetPage().AllowNext():
1629                         self.FindWindowById(wx.ID_FORWARD).Enable()
1630                 else:
1631                         self.FindWindowById(wx.ID_FORWARD).Disable()
1632                 if e.GetPage().AllowBack():
1633                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1634                 else:
1635                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1636                 if hasattr(e.GetPage(), 'OnPageShown'):
1637                         e.GetPage().OnPageShown()
1638
1639         def OnCancel(self, e):
1640                 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1641                 profile.setActiveMachine(self._old_machine_index)
1642                 profile.removeMachine(new_machine_index)
1643
1644 class bedLevelWizardMain(InfoPage):
1645         def __init__(self, parent):
1646                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1647
1648                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1649                 self.AddSeperator()
1650                 self.AddText(_('It will do the following steps'))
1651                 self.AddText(_('* Move the printer head to each corner'))
1652                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1653                 self.AddText(_('* Print a line around the bed to check if it is level'))
1654                 self.AddSeperator()
1655
1656                 self.connectButton = self.AddButton(_('Connect to printer'))
1657                 self.comm = None
1658
1659                 self.infoBox = self.AddInfoBox()
1660                 self.resumeButton = self.AddButton(_('Resume'))
1661                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1662                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1663                 self.resumeButton.Enable(False)
1664
1665                 self.upButton.Enable(False)
1666                 self.downButton.Enable(False)
1667                 self.upButton2.Enable(False)
1668                 self.downButton2.Enable(False)
1669
1670                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1671                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1672                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1673                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1674                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1675                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1676
1677         def OnConnect(self, e = None):
1678                 if self.comm is not None:
1679                         self.comm.close()
1680                         del self.comm
1681                         self.comm = None
1682                         wx.CallAfter(self.OnConnect)
1683                         return
1684                 self.connectButton.Enable(False)
1685                 self.comm = machineCom.MachineCom(callbackObject=self)
1686                 self.infoBox.SetBusy(_('Connecting to machine.'))
1687                 self._wizardState = 0
1688
1689         def OnBedUp(self, e):
1690                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1691                 self.comm.sendCommand('G92 Z10')
1692                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1693                 self.comm.sendCommand('M400')
1694
1695         def OnBedDown(self, e):
1696                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1697                 self.comm.sendCommand('G92 Z10')
1698                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1699                 self.comm.sendCommand('M400')
1700
1701         def OnBedUp2(self, e):
1702                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1703                 self.comm.sendCommand('G92 Z10')
1704                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1705                 self.comm.sendCommand('M400')
1706
1707         def OnBedDown2(self, e):
1708                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1709                 self.comm.sendCommand('G92 Z10')
1710                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1711                 self.comm.sendCommand('M400')
1712
1713         def AllowNext(self):
1714                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1715                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1716                 return True
1717
1718         def OnResume(self, e):
1719                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1720                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1721                 if self._wizardState == -1:
1722                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1723                         wx.CallAfter(self.upButton.Enable, False)
1724                         wx.CallAfter(self.downButton.Enable, False)
1725                         wx.CallAfter(self.upButton2.Enable, False)
1726                         wx.CallAfter(self.downButton2.Enable, False)
1727                         self.comm.sendCommand('M105')
1728                         self.comm.sendCommand('G28')
1729                         self._wizardState = 1
1730                 elif self._wizardState == 2:
1731                         if profile.getMachineSetting('has_heated_bed') == 'True':
1732                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1733                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1734                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1735                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1736                                 self.comm.sendCommand('M400')
1737                                 self._wizardState = 3
1738                         else:
1739                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1740                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1741                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1742                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1743                                 self.comm.sendCommand('M400')
1744                                 self._wizardState = 3
1745                 elif self._wizardState == 4:
1746                         if profile.getMachineSetting('has_heated_bed') == 'True':
1747                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1748                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1749                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1750                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1751                                 self.comm.sendCommand('M400')
1752                                 self._wizardState = 7
1753                         else:
1754                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1755                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1756                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1757                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1758                                 self.comm.sendCommand('M400')
1759                                 self._wizardState = 5
1760                 elif self._wizardState == 6:
1761                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1762                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1763                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1764                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1765                         self.comm.sendCommand('M400')
1766                         self._wizardState = 7
1767                 elif self._wizardState == 8:
1768                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1769                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1770                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1771                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1772                         self._wizardState = 9
1773                 elif self._wizardState == 10:
1774                         self._wizardState = 11
1775                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1776                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1777                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1778                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1779                         w = profile.getMachineSettingFloat('machine_width') - 10
1780                         d = profile.getMachineSettingFloat('machine_depth')
1781                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1782                         filamentArea = math.pi * filamentRadius * filamentRadius
1783                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1784                         eValue = 0.0
1785
1786                         gcodeList = [
1787                                 'G1 Z2 F%d' % (feedZ),
1788                                 'G92 E0',
1789                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1790                                 'G1 Z0.3 F%d' % (feedZ)]
1791                         eValue += 5.0
1792                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1793
1794                         for i in xrange(0, 3):
1795                                 dist = 5.0 + 0.4 * float(i)
1796                                 eValue += (d - 2.0*dist) * ePerMM
1797                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1798                                 eValue += (w - 2.0*dist) * ePerMM
1799                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1800                                 eValue += (d - 2.0*dist) * ePerMM
1801                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1802                                 eValue += (w - 2.0*dist) * ePerMM
1803                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1804
1805                         gcodeList.append('M400')
1806                         self.comm.printGCode(gcodeList)
1807                 self.resumeButton.Enable(False)
1808
1809         def mcLog(self, message):
1810                 print 'Log:', message
1811
1812         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1813                 if self._wizardState == 1:
1814                         self._wizardState = 2
1815                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1816                         wx.CallAfter(self.resumeButton.Enable, True)
1817                 elif self._wizardState == 3:
1818                         self._wizardState = 4
1819                         if profile.getMachineSetting('has_heated_bed') == 'True':
1820                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1821                         else:
1822                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1823                         wx.CallAfter(self.resumeButton.Enable, True)
1824                 elif self._wizardState == 5:
1825                         self._wizardState = 6
1826                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1827                         wx.CallAfter(self.resumeButton.Enable, True)
1828                 elif self._wizardState == 7:
1829                         self._wizardState = 8
1830                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1831                         wx.CallAfter(self.resumeButton.Enable, True)
1832                 elif self._wizardState == 9:
1833                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1834                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1835                         else:
1836                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1837                                 wx.CallAfter(self.resumeButton.Enable, True)
1838                                 self._wizardState = 10
1839
1840         def mcStateChange(self, state):
1841                 if self.comm is None:
1842                         return
1843                 if self.comm.isOperational():
1844                         if self._wizardState == 0:
1845                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1846                                 wx.CallAfter(self.upButton.Enable, True)
1847                                 wx.CallAfter(self.downButton.Enable, True)
1848                                 wx.CallAfter(self.upButton2.Enable, True)
1849                                 wx.CallAfter(self.downButton2.Enable, True)
1850                                 wx.CallAfter(self.resumeButton.Enable, True)
1851                                 self._wizardState = -1
1852                         elif self._wizardState == 11 and not self.comm.isPrinting():
1853                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1854                                 self.comm.sendCommand('G92 E0')
1855                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1856                                 self.comm.sendCommand('M104 S0')
1857                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1858                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1859                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1860                                 wx.CallAfter(self.connectButton.Enable, True)
1861                                 self._wizardState = 12
1862                 elif self.comm.isError():
1863                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1864
1865         def mcMessage(self, message):
1866                 pass
1867
1868         def mcProgress(self, lineNr):
1869                 pass
1870
1871         def mcZChange(self, newZ):
1872                 pass
1873
1874 class headOffsetCalibrationPage(InfoPage):
1875         def __init__(self, parent):
1876                 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1877
1878                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1879                 self.AddSeperator()
1880
1881                 self.connectButton = self.AddButton(_('Connect to printer'))
1882                 self.comm = None
1883
1884                 self.infoBox = self.AddInfoBox()
1885                 self.textEntry = self.AddTextCtrl('')
1886                 self.textEntry.Enable(False)
1887                 self.resumeButton = self.AddButton(_('Resume'))
1888                 self.resumeButton.Enable(False)
1889
1890                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1891                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1892
1893         def AllowBack(self):
1894                 return True
1895
1896         def OnConnect(self, e = None):
1897                 if self.comm is not None:
1898                         self.comm.close()
1899                         del self.comm
1900                         self.comm = None
1901                         wx.CallAfter(self.OnConnect)
1902                         return
1903                 self.connectButton.Enable(False)
1904                 self.comm = machineCom.MachineCom(callbackObject=self)
1905                 self.infoBox.SetBusy(_('Connecting to machine.'))
1906                 self._wizardState = 0
1907
1908         def OnResume(self, e):
1909                 if self._wizardState == 2:
1910                         self._wizardState = 3
1911                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1912
1913                         w = profile.getMachineSettingFloat('machine_width')
1914                         d = profile.getMachineSettingFloat('machine_depth')
1915
1916                         gcode = gcodeGenerator.gcodeGenerator()
1917                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1918                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1919                         gcode.addCmd('T0')
1920                         gcode.addPrime(15)
1921                         gcode.addCmd('T1')
1922                         gcode.addPrime(15)
1923
1924                         gcode.addCmd('T0')
1925                         gcode.addMove(w/2, 5)
1926                         gcode.addMove(z=0.2)
1927                         gcode.addPrime()
1928                         gcode.addExtrude(w/2, d-5.0)
1929                         gcode.addRetract()
1930                         gcode.addMove(5, d/2)
1931                         gcode.addPrime()
1932                         gcode.addExtrude(w-5.0, d/2)
1933                         gcode.addRetract(15)
1934
1935                         gcode.addCmd('T1')
1936                         gcode.addMove(w/2, 5)
1937                         gcode.addPrime()
1938                         gcode.addExtrude(w/2, d-5.0)
1939                         gcode.addRetract()
1940                         gcode.addMove(5, d/2)
1941                         gcode.addPrime()
1942                         gcode.addExtrude(w-5.0, d/2)
1943                         gcode.addRetract(15)
1944                         gcode.addCmd('T0')
1945
1946                         gcode.addMove(z=25)
1947                         gcode.addMove(0, 0)
1948                         gcode.addCmd('M400')
1949
1950                         self.comm.printGCode(gcode.list())
1951                         self.resumeButton.Enable(False)
1952                 elif self._wizardState == 4:
1953                         try:
1954                                 float(self.textEntry.GetValue())
1955                         except ValueError:
1956                                 return
1957                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1958                         self._wizardState = 5
1959                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1960                         self.textEntry.SetValue('0.0')
1961                         self.textEntry.Enable(True)
1962                 elif self._wizardState == 5:
1963                         try:
1964                                 float(self.textEntry.GetValue())
1965                         except ValueError:
1966                                 return
1967                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1968                         self._wizardState = 6
1969                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1970                         self.textEntry.SetValue('')
1971                         self.textEntry.Enable(False)
1972                         self.resumeButton.Enable(False)
1973
1974                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1975                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1976                         gcode = gcodeGenerator.gcodeGenerator()
1977                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1978                         gcode.setPrintSpeed(25)
1979                         gcode.addHome()
1980                         gcode.addCmd('T0')
1981                         gcode.addMove(50, 40, 0.2)
1982                         gcode.addPrime(15)
1983                         for n in xrange(0, 10):
1984                                 gcode.addExtrude(50 + n * 10, 150)
1985                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1986                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1987                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1988                         gcode.addMove(40, 50)
1989                         for n in xrange(0, 10):
1990                                 gcode.addExtrude(150, 50 + n * 10)
1991                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1992                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1993                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1994                         gcode.addRetract(15)
1995
1996                         gcode.addCmd('T1')
1997                         gcode.addMove(50 - x, 30 - y, 0.2)
1998                         gcode.addPrime(15)
1999                         for n in xrange(0, 10):
2000                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
2001                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
2002                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
2003                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
2004                         gcode.addMove(30 - x, 50 - y, 0.2)
2005                         for n in xrange(0, 10):
2006                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
2007                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
2008                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
2009                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
2010                         gcode.addRetract(15)
2011                         gcode.addMove(z=15)
2012                         gcode.addCmd('M400')
2013                         gcode.addCmd('M104 T0 S0')
2014                         gcode.addCmd('M104 T1 S0')
2015                         self.comm.printGCode(gcode.list())
2016                 elif self._wizardState == 7:
2017                         try:
2018                                 n = int(self.textEntry.GetValue()) - 1
2019                         except:
2020                                 return
2021                         x = profile.getMachineSettingFloat('extruder_offset_x1')
2022                         x += -1.0 + n * 0.1
2023                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
2024                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
2025                         self.textEntry.SetValue('10')
2026                         self._wizardState = 8
2027                 elif self._wizardState == 8:
2028                         try:
2029                                 n = int(self.textEntry.GetValue()) - 1
2030                         except:
2031                                 return
2032                         y = profile.getMachineSettingFloat('extruder_offset_y1')
2033                         y += -1.0 + n * 0.1
2034                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
2035                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
2036                         self.infoBox.SetReadyIndicator()
2037                         self._wizardState = 8
2038                         self.comm.close()
2039                         self.resumeButton.Enable(False)
2040
2041         def mcLog(self, message):
2042                 print 'Log:', message
2043
2044         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
2045                 if self._wizardState == 1:
2046                         if temp[0] >= 210 and temp[1] >= 210:
2047                                 self._wizardState = 2
2048                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
2049                                 wx.CallAfter(self.resumeButton.Enable, True)
2050                                 wx.CallAfter(self.resumeButton.SetFocus)
2051
2052         def mcStateChange(self, state):
2053                 if self.comm is None:
2054                         return
2055                 if self.comm.isOperational():
2056                         if self._wizardState == 0:
2057                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
2058                                 self.comm.sendCommand('M105')
2059                                 self.comm.sendCommand('M104 S220 T0')
2060                                 self.comm.sendCommand('M104 S220 T1')
2061                                 self.comm.sendCommand('G28')
2062                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
2063                                 self._wizardState = 1
2064                         if not self.comm.isPrinting():
2065                                 if self._wizardState == 3:
2066                                         self._wizardState = 4
2067                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
2068                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
2069                                         wx.CallAfter(self.textEntry.Enable, True)
2070                                         wx.CallAfter(self.resumeButton.Enable, True)
2071                                         wx.CallAfter(self.resumeButton.SetFocus)
2072                                 elif self._wizardState == 6:
2073                                         self._wizardState = 7
2074                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
2075                                         wx.CallAfter(self.textEntry.SetValue, '10')
2076                                         wx.CallAfter(self.textEntry.Enable, True)
2077                                         wx.CallAfter(self.resumeButton.Enable, True)
2078                                         wx.CallAfter(self.resumeButton.SetFocus)
2079
2080                 elif self.comm.isError():
2081                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
2082
2083         def mcMessage(self, message):
2084                 pass
2085
2086         def mcProgress(self, lineNr):
2087                 pass
2088
2089         def mcZChange(self, newZ):
2090                 pass
2091
2092 class bedLevelWizard(wx.wizard.Wizard):
2093         def __init__(self):
2094                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
2095
2096                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
2097                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
2098
2099                 self.mainPage = bedLevelWizardMain(self)
2100                 self.headOffsetCalibration = None
2101
2102                 self.RunWizard(self.mainPage)
2103                 self.Destroy()
2104
2105         def OnPageChanging(self, e):
2106                 e.GetPage().StoreData()
2107
2108         def OnPageChanged(self, e):
2109                 if e.GetPage().AllowNext():
2110                         self.FindWindowById(wx.ID_FORWARD).Enable()
2111                 else:
2112                         self.FindWindowById(wx.ID_FORWARD).Disable()
2113                 if e.GetPage().AllowBack():
2114                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2115                 else:
2116                         self.FindWindowById(wx.ID_BACKWARD).Disable()
2117
2118 class headOffsetWizard(wx.wizard.Wizard):
2119         def __init__(self):
2120                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
2121
2122                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
2123                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
2124
2125                 self.mainPage = headOffsetCalibrationPage(self)
2126
2127                 self.RunWizard(self.mainPage)
2128                 self.Destroy()
2129
2130         def OnPageChanging(self, e):
2131                 e.GetPage().StoreData()
2132
2133         def OnPageChanged(self, e):
2134                 if e.GetPage().AllowNext():
2135                         self.FindWindowById(wx.ID_FORWARD).Enable()
2136                 else:
2137                         self.FindWindowById(wx.ID_FORWARD).Disable()
2138                 if e.GetPage().AllowBack():
2139                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2140                 else:
2141                         self.FindWindowById(wx.ID_BACKWARD).Disable()