chiark / gitweb /
plugins: Support user configuration of default values
[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.AddText('')
1203                 self.AddText(_('Please contact support if you have problems operating'))
1204                 self.AddText(_('your LulzBot 3D Printer www.LulzBot.com/support'))
1205                 self.AddSeperator()
1206
1207 class LulzbotMiniToolheadSelectPage(InfoPage):
1208         def __init__(self, parent, allowBack = True):
1209                 super(LulzbotMiniToolheadSelectPage, self).__init__(parent, _("LulzBot Mini Tool Head Selection"))
1210
1211                 self.allowBack = allowBack
1212                 self.panel = self.AddPanel()
1213                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1214                 self.standard = self.AddImageButton(self.panel, 0, 0, _('Standard LulzBot Mini'),
1215                                                                                         'Lulzbot_mini.jpg', image_size,
1216                                                                                         style=ImageButton.IB_GROUP)
1217                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('LulzBot Mini with Flexystruder'),
1218                                                                                         'Lulzbot_Toolhead_Mini_Flexystruder.jpg', image_size)
1219                 self.standard.SetValue(True)
1220
1221         def AllowBack(self):
1222                 return self.allowBack
1223
1224         def StoreData(self):
1225                 if self.standard.GetValue():
1226                         profile.putProfileSetting('nozzle_size', '0.5')
1227                         profile.putMachineSetting('extruder_amount', '1')
1228                         profile.putMachineSetting('toolhead', 'Single Extruder v2')
1229                         profile.putMachineSetting('toolhead_shortname', '')
1230                         profile.putMachineSetting('machine_type', 'lulzbot_mini')
1231                 else:
1232                         profile.putProfileSetting('nozzle_size', '0.6')
1233                         profile.putMachineSetting('extruder_amount', '1')
1234                         profile.putMachineSetting('toolhead', 'Flexystruder v2')
1235                         profile.putMachineSetting('toolhead_shortname', 'Flexystruder')
1236                         profile.putMachineSetting('machine_type', 'lulzbot_mini_flexystruder')
1237
1238 class LulzbotTaz6ToolheadSelectPage(InfoPage):
1239         def __init__(self, parent, allowBack = True):
1240                 super(LulzbotTaz6ToolheadSelectPage, self).__init__(parent, _("LulzBot TAZ 6 Tool Head Selection"))
1241
1242                 self.panel = self.AddPanel()
1243                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1244                 self.taz6 = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v2.1'),
1245                                                                                 'Lulzbot_Toolhead_TAZ_Tilapia.jpg', image_size,
1246                                                                                 style=ImageButton.IB_GROUP)
1247                 self.taz6.OnSelected(self.OnTilapiaSelected)
1248                 self.taz6.SetValue(True)
1249
1250         def OnPageShown(self):
1251                 self.taz6.TriggerGroupCallbacks()
1252
1253         def OnTilapiaSelected(self):
1254                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1255
1256         def StoreData(self):
1257                 profile.putProfileSetting('nozzle_size',  '0.5')
1258                 profile.putMachineSetting('extruder_amount', '1')
1259                 profile.putMachineSetting('toolhead', 'Single Extruder v2.1')
1260                 profile.putMachineSetting('toolhead_shortname', 'Single v2.1')
1261                 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_6_Single_v2.1')
1262                 profile.putMachineSetting('machine_name', 'LulzBot TAZ 6')
1263
1264 class LulzbotTazSelectPage(InfoPage):
1265         def __init__(self, parent):
1266                 super(LulzbotTazSelectPage, self).__init__(parent, _("LulzBot TAZ 4-5 Selection"))
1267
1268                 self.panel = self.AddPanel()
1269                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1270                 self.taz5 = self.AddImageButton(self.panel, 0, 0, _('Stock TAZ 5 (PEI && v2)'),
1271                                                                                 'Lulzbot_TAZ_5_Hex_and_PEI.jpg', image_size,
1272                                                                                 style=ImageButton.IB_GROUP)
1273                 self.taz5.OnSelected(self.OnTaz5Selected)
1274                 self.taz4 = self.AddImageButton(self.panel, 0, 1, _('Stock TAZ 4 (PET && v1)'),
1275                                                                                 'Lulzbot_TAZ_4_Buda_and_PET.jpg', image_size)
1276                 self.taz4.OnSelected(self.OnTaz4Selected)
1277                 self.modified = self.AddImageButton(self.panel, 1, 0, _('Modified LulzBot TAZ 4 or 5'),
1278                                                                                         'Lulzbot_TAZ5.jpg', image_size)
1279                 self.modified.OnSelected(self.OnModifiedSelected)
1280                 self.taz5.SetValue(True)
1281
1282         def OnPageShown(self):
1283                 self.taz5.TriggerGroupCallbacks()
1284
1285         def OnTaz5Selected(self):
1286                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1287                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage,
1288                                                                                  self.GetParent().lulzbotReadyPage)
1289
1290         def OnTaz4Selected(self):
1291                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1292
1293         def OnModifiedSelected(self):
1294                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTazBedSelectPage)
1295                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTazBedSelectPage,
1296                                                                                  self.GetParent().lulzbotTazHotendPage)
1297
1298         def StoreData(self):
1299                 if self.taz5.GetValue():
1300                         profile.putProfileSetting('nozzle_size',  '0.5')
1301                         profile.putMachineSetting('extruder_amount', '1')
1302                         profile.putMachineSetting('toolhead', 'Single Extruder V2')
1303                         profile.putMachineSetting('toolhead_shortname', '')
1304                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_SingleV2')
1305                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5')
1306                 elif self.taz4.GetValue():
1307                         profile.putProfileSetting('nozzle_size', '0.35')
1308                         profile.putMachineSetting('extruder_amount', '1')
1309                         profile.putMachineSetting('toolhead', 'Single Extruder V1')
1310                         profile.putMachineSetting('toolhead_shortname', '')
1311                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4_SingleV1')
1312                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1313
1314 class LulzbotTazBedSelectPage(InfoPage):
1315         def __init__(self, parent):
1316                 super(LulzbotTazBedSelectPage, self).__init__(parent, _("Bed Surface"))
1317
1318                 self.panel = self.AddPanel()
1319                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1320                 self.pei = self.AddImageButton(self.panel, 0, 0, _('PEI'),
1321                                                                            'Lulzbot_TAZ_PEI_Bed.jpg', image_size,
1322                                                                            style=ImageButton.IB_GROUP)
1323                 self.pet = self.AddImageButton(self.panel, 0, 1, _('PET'),
1324                                                                            'Lulzbot_TAZ_PET_Bed.jpg', image_size)
1325                 self.pei.SetValue(True)
1326
1327         def StoreData(self):
1328                 if self.pei.GetValue():
1329                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1330                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5')
1331                 else:
1332                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4')
1333                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1334
1335 class LulzbotTazToolheadSelectPage(InfoPage):
1336         def __init__(self, parent):
1337                 super(LulzbotTazToolheadSelectPage, self).__init__(parent, _("LulzBot TAZ Tool Head Selection"))
1338
1339                 self.panel = self.AddPanel()
1340                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1341                 self.single = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v1'),
1342                                                                                         'Lulzbot_Toolhead_TAZ_Single_v1.jpg', image_size,
1343                                                                                         style=ImageButton.IB_GROUP)
1344                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v1'),
1345                                                                                         'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg', image_size)
1346                 self.dually = self.AddImageButton(self.panel, 1, 0, _('Dual Extruder v1'),
1347                                                                                         'Lulzbot_Toolhead_TAZ_Dual_Extruder_v1.jpg', image_size)
1348                 self.flexydually = self.AddImageButton(self.panel, 1, 1, _('FlexyDually v1'),
1349                                                                                         'Lulzbot_Toolhead_TAZ_FlexyDually_v1.jpg', image_size)
1350                 self.SetVersion(1)
1351                 self.single.SetValue(True)
1352
1353         def SetVersion(self, version):
1354                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1355                 self.single.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Single_v%d.jpg' % version, image_size))
1356                 self.single.SetLabel(_('Single Extruder v%d' % version))
1357                 self.flexy.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Flexystruder_v%d.jpg' % version, image_size))
1358                 self.flexy.SetLabel(_('Flexystruder v%d' % version))
1359                 self.dually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Dual_Extruder_v%d.jpg' % version, image_size))
1360                 self.dually.SetLabel(_('Dual Extruder v%d' % version))
1361                 self.flexydually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_FlexyDually_v%d.jpg' % version, image_size))
1362                 self.flexydually.SetLabel(_('FlexyDually v%d' % version))
1363                 self.version = version
1364                 if version == 1:
1365                         self.single.OnSelected(None)
1366                         self.flexy.OnSelected(None)
1367                         self.dually.OnSelected(None)
1368                         self.flexydually.OnSelected(None)
1369                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1370                 elif version == 2:
1371                         self.single.OnSelected(self.OnSingleV2)
1372                         self.flexy.OnSelected(self.OnNonSingle)
1373                         self.dually.OnSelected(self.OnNonSingle)
1374                         self.flexydually.OnSelected(self.OnNonSingle)
1375                         if self.single.GetValue():
1376                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1377                                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage, self.GetParent().lulzbotFirmwarePage)
1378                         else:
1379                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1380                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotFirmwarePage, self.GetParent().lulzbotReadyPage)
1381
1382         def OnSingleV2(self):
1383                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1384                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage, self.GetParent().lulzbotFirmwarePage)
1385
1386         def OnNonSingle(self):
1387                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1388
1389         def StoreData(self):
1390                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1391                         taz_version = 4
1392                 else:
1393                         taz_version = 5
1394                 version = (taz_version, self.version)
1395                 if self.single.GetValue():
1396                         profile.putProfileSetting('nozzle_size', '0.5' if self.version == 2 else '0.35')
1397                         profile.putMachineSetting('extruder_amount', '1')
1398                         profile.putMachineSetting('toolhead', 'Single Extruder V%d' % self.version)
1399                         profile.putMachineSetting('toolhead_shortname', '')
1400                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_SingleV%d' % version)
1401                 elif self.flexy.GetValue():
1402                         profile.putProfileSetting('nozzle_size', '0.6')
1403                         profile.putMachineSetting('extruder_amount', '1')
1404                         profile.putMachineSetting('toolhead', 'Flexystruder V%d' % self.version)
1405                         profile.putMachineSetting('toolhead_shortname', 'Flexystruder v%d' % self.version)
1406                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexystruderV%d' % version)
1407                 elif self.dually.GetValue():
1408                         profile.putProfileSetting('nozzle_size', '0.5')
1409                         profile.putMachineSetting('extruder_amount', '2')
1410                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1411                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1412                         profile.putMachineSetting('toolhead', 'Dual Extruder V%d' % self.version)
1413                         profile.putMachineSetting('toolhead_shortname', 'Dual v%d' % self.version)
1414                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_DualV%d' % version)
1415                 elif self.flexydually.GetValue():
1416                         profile.putProfileSetting('nozzle_size', '0.6')
1417                         profile.putMachineSetting('extruder_amount', '2')
1418                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1419                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1420                         profile.putMachineSetting('toolhead', 'FlexyDually V%d' % self.version)
1421                         profile.putMachineSetting('toolhead_shortname', 'FlexyDually v%d' % self.version)
1422                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexyDuallyV%d' % version)
1423
1424 class LulzbotHotendSelectPage(InfoPage):
1425         def __init__(self, parent, allowBack = True):
1426                 super(LulzbotHotendSelectPage, self).__init__(parent, _("LulzBot Tool Head Hot end Selection"))
1427
1428                 self.allowBack = allowBack
1429                 self.panel = self.AddPanel()
1430                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1431                 self.v1 = self.AddImageButton(self.panel, 0, 0, _('v1 (Budaschnozzle)'),
1432                                                                                         'Lulzbot_Toolhead_v1.jpg', image_size,
1433                                                                                         style=ImageButton.IB_GROUP)
1434                 self.v2 = self.AddImageButton(self.panel, 0, 1, _('v2 (LulzBot Hexagon)'),
1435                                                                                         'Lulzbot_Toolhead_v2.jpg', image_size)
1436                 self.v1.SetValue(True)
1437
1438         def AllowBack(self):
1439                 return self.allowBack
1440
1441         def StoreData(self):
1442                 self.GetParent().lulzbotTazToolheadPage.SetVersion(1 if self.v1.GetValue() else 2)
1443
1444 class LulzbotTaz5NozzleSelectPage(InfoPage):
1445         url2='http://lulzbot.com/printer-identification'
1446
1447         def __init__(self, parent):
1448                 super(LulzbotTaz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ Single v2 Nozzle Selection"))
1449
1450                 self.AddText(_('Please select your LulzBot Hexagon Hot End\'s nozzle diameter:'))
1451                 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1452                 self.Nozzle35Radio.SetValue(True)
1453                 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1454                 self.AddText(_(' '))
1455                 self.AddSeperator()
1456
1457                 self.AddText(_('If you are not sure which nozzle diameter you have,'))
1458                 self.AddText(_('please check this webpage: '))
1459                 button = self.AddButton(LulzbotTaz5NozzleSelectPage.url2)
1460                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1461
1462         def OnUrlClick(self, e):
1463                 webbrowser.open(LulzbotTaz5NozzleSelectPage.url2)
1464
1465         def StoreData(self):
1466                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1467                         taz_version = 4
1468                 else:
1469                         taz_version = 5
1470                 if self.Nozzle35Radio.GetValue():
1471                         profile.putProfileSetting('nozzle_size', '0.35')
1472                         profile.putMachineSetting('toolhead', 'Single Extruder v2 (0.35mm nozzle)')
1473                         profile.putMachineSetting('toolhead_shortname', '0.35 nozzle')
1474                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_035nozzle' % taz_version)
1475
1476                 else:
1477                         profile.putProfileSetting('nozzle_size', '0.5')
1478                         profile.putMachineSetting('toolhead', 'Single Extruder v2 (0.5mm nozzle)')
1479                         profile.putMachineSetting('toolhead_shortname', '0.5 nozzle')
1480                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_05nozzle' % taz_version)
1481
1482 class LulzbotFirmwareUpdatePage(InfoPage):
1483         def __init__(self, parent):
1484                 super(LulzbotFirmwareUpdatePage, self).__init__(parent, _("LulzBot Firmware Update"))
1485
1486                 self.AddText(_("Your LulzBot printer\'s firmware will now be updated.\n" +
1487                 "Note: this will overwrite your existing firmware."))
1488                 self.AddSeperator()
1489                 self.AddText(_("Follow these steps to prevent writing firmware to the wrong device:\n" +
1490                                            "    1) Unplug all USB devices from your computer\n" +
1491                                            "    2) Plug your 3D Printer into the computer with a USB cable\n" +
1492                                            "    3) Turn on your 3D Printer\n" +
1493                                            "    4) Click \"Flash the firmware\""))
1494                 self.AddHiddenSeperator()
1495                 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Flash the firmware'), _('Skip upgrade'))
1496                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
1497                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
1498
1499         def AllowNext(self):
1500                 return version.isDevVersion()
1501
1502         def OnUpgradeClick(self, e):
1503                 if firmwareInstall.InstallFirmware():
1504                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1505                         self.GetParent().ShowPage(self.GetNext())
1506
1507         def OnSkipClick(self, e):
1508                 dlg = wx.MessageDialog(self,
1509                         _("CAUTION: Updating firmware is necessary when changing the\n" \
1510                           "tool head on your LulzBot desktop 3D Printer." \
1511                           "\n\n" +
1512                           "Are you sure you want to skip the firmware update?"),
1513                         _('Skip firmware update?'),
1514                         wx.YES_NO | wx.ICON_EXCLAMATION)
1515                 skip = dlg.ShowModal() == wx.ID_YES
1516                 dlg.Destroy()
1517                 if skip:
1518                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1519                         self.GetParent().ShowPage(self.GetNext())
1520
1521 class LulzbotChangeToolheadWizard(wx.wizard.Wizard):
1522         def __init__(self):
1523                 super(LulzbotChangeToolheadWizard, self).__init__(None, -1, _("Change LulzBot Tool Head Wizard"))
1524
1525                 self._nozzle_size = profile.getProfileSettingFloat('nozzle_size')
1526                 self._machine_name = profile.getMachineSetting('machine_name')
1527                 self._machine_type = profile.getMachineSetting('machine_type')
1528                 self._extruder_amount = int(profile.getMachineSettingFloat('extruder_amount'))
1529
1530                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1531                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1532                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1533
1534                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1535                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1536                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self, False)
1537                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1538                 self.lulzbotTaz6ToolheadPage = LulzbotTaz6ToolheadSelectPage(self, False)
1539                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self, False)
1540                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1541                 self.lulzbotTazBedSelectPage = LulzbotTazBedSelectPage(self)
1542                 self.lulzbotTazSelectPage = LulzbotTazSelectPage(self)
1543
1544                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotReadyPage)
1545                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1546
1547                 if profile.getMachineSetting('machine_type').startswith('lulzbot_mini'):
1548                         self.RunWizard(self.lulzbotMiniToolheadPage)
1549                 elif profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_5') or \
1550                      profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1551                         self.RunWizard(self.lulzbotTazHotendPage)
1552                 elif profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_6'):
1553                         self.RunWizard(self.lulzbotTaz6ToolheadPage)
1554                 self.Destroy()
1555
1556         def OnPageChanging(self, e):
1557                 e.GetPage().StoreData()
1558
1559         def OnPageChanged(self, e):
1560                 if e.GetPage().AllowNext():
1561                         self.FindWindowById(wx.ID_FORWARD).Enable()
1562                 else:
1563                         self.FindWindowById(wx.ID_FORWARD).Disable()
1564                 if e.GetPage().AllowBack():
1565                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1566                 else:
1567                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1568                 if hasattr(e.GetPage(), 'OnPageShown'):
1569                         e.GetPage().OnPageShown()
1570
1571         def OnCancel(self, e):
1572                 profile.putProfileSetting('nozzle_size', self._nozzle_size)
1573                 profile.putMachineSetting('machine_name', self._machine_name)
1574                 profile.putMachineSetting('machine_type', self._machine_type)
1575                 profile.putMachineSetting('extruder_amount', self._extruder_amount)
1576
1577 class ConfigWizard(wx.wizard.Wizard):
1578         def __init__(self, addNew = False):
1579                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1580
1581                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1582                 if addNew:
1583                         profile.setActiveMachine(profile.getMachineCount())
1584
1585                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1586                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1587                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1588
1589                 self.machineSelectPage = MachineSelectPage(self)
1590                 self.ultimakerSelectParts = SelectParts(self)
1591                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1592                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1593                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1594                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1595                 self.bedLevelPage = bedLevelWizardMain(self)
1596                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1597                 self.printrbotSelectType = PrintrbotPage(self)
1598                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1599                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1600                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1601
1602                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1603                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1604                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1605                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self)
1606                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1607                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self)
1608                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1609                 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1610                 self.lulzbotTazBedSelectPage = LulzbotTazBedSelectPage(self)
1611                 self.lulzbotTazSelectPage = LulzbotTazSelectPage(self)
1612                 self.lulzbotTaz6ToolheadPage = LulzbotTaz6ToolheadSelectPage(self)
1613
1614                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotMiniToolheadPage)
1615                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotReadyPage)
1616                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1617                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1618                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1619                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1620                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1621                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1622                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1623
1624                 self.RunWizard(self.lulzbotMachineSelectPage)
1625                 self.Destroy()
1626
1627         def OnPageChanging(self, e):
1628                 e.GetPage().StoreData()
1629
1630         def OnPageChanged(self, e):
1631                 if e.GetPage().AllowNext():
1632                         self.FindWindowById(wx.ID_FORWARD).Enable()
1633                 else:
1634                         self.FindWindowById(wx.ID_FORWARD).Disable()
1635                 if e.GetPage().AllowBack():
1636                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1637                 else:
1638                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1639                 if hasattr(e.GetPage(), 'OnPageShown'):
1640                         e.GetPage().OnPageShown()
1641
1642         def OnCancel(self, e):
1643                 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1644                 profile.setActiveMachine(self._old_machine_index)
1645                 profile.removeMachine(new_machine_index)
1646
1647 class bedLevelWizardMain(InfoPage):
1648         def __init__(self, parent):
1649                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1650
1651                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1652                 self.AddSeperator()
1653                 self.AddText(_('It will do the following steps'))
1654                 self.AddText(_('* Move the printer head to each corner'))
1655                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1656                 self.AddText(_('* Print a line around the bed to check if it is level'))
1657                 self.AddSeperator()
1658
1659                 self.connectButton = self.AddButton(_('Connect to printer'))
1660                 self.comm = None
1661
1662                 self.infoBox = self.AddInfoBox()
1663                 self.resumeButton = self.AddButton(_('Resume'))
1664                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1665                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1666                 self.resumeButton.Enable(False)
1667
1668                 self.upButton.Enable(False)
1669                 self.downButton.Enable(False)
1670                 self.upButton2.Enable(False)
1671                 self.downButton2.Enable(False)
1672
1673                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1674                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1675                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1676                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1677                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1678                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1679
1680         def OnConnect(self, e = None):
1681                 if self.comm is not None:
1682                         self.comm.close()
1683                         del self.comm
1684                         self.comm = None
1685                         wx.CallAfter(self.OnConnect)
1686                         return
1687                 self.connectButton.Enable(False)
1688                 self.comm = machineCom.MachineCom(callbackObject=self)
1689                 self.infoBox.SetBusy(_('Connecting to machine.'))
1690                 self._wizardState = 0
1691
1692         def OnBedUp(self, e):
1693                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1694                 self.comm.sendCommand('G92 Z10')
1695                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1696                 self.comm.sendCommand('M400')
1697
1698         def OnBedDown(self, e):
1699                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1700                 self.comm.sendCommand('G92 Z10')
1701                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1702                 self.comm.sendCommand('M400')
1703
1704         def OnBedUp2(self, e):
1705                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1706                 self.comm.sendCommand('G92 Z10')
1707                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1708                 self.comm.sendCommand('M400')
1709
1710         def OnBedDown2(self, e):
1711                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1712                 self.comm.sendCommand('G92 Z10')
1713                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1714                 self.comm.sendCommand('M400')
1715
1716         def AllowNext(self):
1717                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1718                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1719                 return True
1720
1721         def OnResume(self, e):
1722                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1723                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1724                 if self._wizardState == -1:
1725                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1726                         wx.CallAfter(self.upButton.Enable, False)
1727                         wx.CallAfter(self.downButton.Enable, False)
1728                         wx.CallAfter(self.upButton2.Enable, False)
1729                         wx.CallAfter(self.downButton2.Enable, False)
1730                         self.comm.sendCommand('M105')
1731                         self.comm.sendCommand('G28')
1732                         self._wizardState = 1
1733                 elif self._wizardState == 2:
1734                         if profile.getMachineSetting('has_heated_bed') == 'True':
1735                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1736                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1737                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1738                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1739                                 self.comm.sendCommand('M400')
1740                                 self._wizardState = 3
1741                         else:
1742                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1743                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1744                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1745                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1746                                 self.comm.sendCommand('M400')
1747                                 self._wizardState = 3
1748                 elif self._wizardState == 4:
1749                         if profile.getMachineSetting('has_heated_bed') == 'True':
1750                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1751                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1752                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1753                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1754                                 self.comm.sendCommand('M400')
1755                                 self._wizardState = 7
1756                         else:
1757                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1758                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1759                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1760                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1761                                 self.comm.sendCommand('M400')
1762                                 self._wizardState = 5
1763                 elif self._wizardState == 6:
1764                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1765                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1766                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1767                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1768                         self.comm.sendCommand('M400')
1769                         self._wizardState = 7
1770                 elif self._wizardState == 8:
1771                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1772                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1773                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1774                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1775                         self._wizardState = 9
1776                 elif self._wizardState == 10:
1777                         self._wizardState = 11
1778                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1779                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1780                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1781                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1782                         w = profile.getMachineSettingFloat('machine_width') - 10
1783                         d = profile.getMachineSettingFloat('machine_depth')
1784                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1785                         filamentArea = math.pi * filamentRadius * filamentRadius
1786                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1787                         eValue = 0.0
1788
1789                         gcodeList = [
1790                                 'G1 Z2 F%d' % (feedZ),
1791                                 'G92 E0',
1792                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1793                                 'G1 Z0.3 F%d' % (feedZ)]
1794                         eValue += 5.0
1795                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1796
1797                         for i in xrange(0, 3):
1798                                 dist = 5.0 + 0.4 * float(i)
1799                                 eValue += (d - 2.0*dist) * ePerMM
1800                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1801                                 eValue += (w - 2.0*dist) * ePerMM
1802                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1803                                 eValue += (d - 2.0*dist) * ePerMM
1804                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1805                                 eValue += (w - 2.0*dist) * ePerMM
1806                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1807
1808                         gcodeList.append('M400')
1809                         self.comm.printGCode(gcodeList)
1810                 self.resumeButton.Enable(False)
1811
1812         def mcLog(self, message):
1813                 print 'Log:', message
1814
1815         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1816                 if self._wizardState == 1:
1817                         self._wizardState = 2
1818                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1819                         wx.CallAfter(self.resumeButton.Enable, True)
1820                 elif self._wizardState == 3:
1821                         self._wizardState = 4
1822                         if profile.getMachineSetting('has_heated_bed') == 'True':
1823                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1824                         else:
1825                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1826                         wx.CallAfter(self.resumeButton.Enable, True)
1827                 elif self._wizardState == 5:
1828                         self._wizardState = 6
1829                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1830                         wx.CallAfter(self.resumeButton.Enable, True)
1831                 elif self._wizardState == 7:
1832                         self._wizardState = 8
1833                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1834                         wx.CallAfter(self.resumeButton.Enable, True)
1835                 elif self._wizardState == 9:
1836                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1837                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1838                         else:
1839                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1840                                 wx.CallAfter(self.resumeButton.Enable, True)
1841                                 self._wizardState = 10
1842
1843         def mcStateChange(self, state):
1844                 if self.comm is None:
1845                         return
1846                 if self.comm.isOperational():
1847                         if self._wizardState == 0:
1848                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1849                                 wx.CallAfter(self.upButton.Enable, True)
1850                                 wx.CallAfter(self.downButton.Enable, True)
1851                                 wx.CallAfter(self.upButton2.Enable, True)
1852                                 wx.CallAfter(self.downButton2.Enable, True)
1853                                 wx.CallAfter(self.resumeButton.Enable, True)
1854                                 self._wizardState = -1
1855                         elif self._wizardState == 11 and not self.comm.isPrinting():
1856                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1857                                 self.comm.sendCommand('G92 E0')
1858                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1859                                 self.comm.sendCommand('M104 S0')
1860                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1861                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1862                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1863                                 wx.CallAfter(self.connectButton.Enable, True)
1864                                 self._wizardState = 12
1865                 elif self.comm.isError():
1866                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1867
1868         def mcMessage(self, message):
1869                 pass
1870
1871         def mcProgress(self, lineNr):
1872                 pass
1873
1874         def mcZChange(self, newZ):
1875                 pass
1876
1877 class headOffsetCalibrationPage(InfoPage):
1878         def __init__(self, parent):
1879                 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1880
1881                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1882                 self.AddSeperator()
1883
1884                 self.connectButton = self.AddButton(_('Connect to printer'))
1885                 self.comm = None
1886
1887                 self.infoBox = self.AddInfoBox()
1888                 self.textEntry = self.AddTextCtrl('')
1889                 self.textEntry.Enable(False)
1890                 self.resumeButton = self.AddButton(_('Resume'))
1891                 self.resumeButton.Enable(False)
1892
1893                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1894                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1895
1896         def AllowBack(self):
1897                 return True
1898
1899         def OnConnect(self, e = None):
1900                 if self.comm is not None:
1901                         self.comm.close()
1902                         del self.comm
1903                         self.comm = None
1904                         wx.CallAfter(self.OnConnect)
1905                         return
1906                 self.connectButton.Enable(False)
1907                 self.comm = machineCom.MachineCom(callbackObject=self)
1908                 self.infoBox.SetBusy(_('Connecting to machine.'))
1909                 self._wizardState = 0
1910
1911         def OnResume(self, e):
1912                 if self._wizardState == 2:
1913                         self._wizardState = 3
1914                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1915
1916                         w = profile.getMachineSettingFloat('machine_width')
1917                         d = profile.getMachineSettingFloat('machine_depth')
1918
1919                         gcode = gcodeGenerator.gcodeGenerator()
1920                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1921                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1922                         gcode.addCmd('T0')
1923                         gcode.addPrime(15)
1924                         gcode.addCmd('T1')
1925                         gcode.addPrime(15)
1926
1927                         gcode.addCmd('T0')
1928                         gcode.addMove(w/2, 5)
1929                         gcode.addMove(z=0.2)
1930                         gcode.addPrime()
1931                         gcode.addExtrude(w/2, d-5.0)
1932                         gcode.addRetract()
1933                         gcode.addMove(5, d/2)
1934                         gcode.addPrime()
1935                         gcode.addExtrude(w-5.0, d/2)
1936                         gcode.addRetract(15)
1937
1938                         gcode.addCmd('T1')
1939                         gcode.addMove(w/2, 5)
1940                         gcode.addPrime()
1941                         gcode.addExtrude(w/2, d-5.0)
1942                         gcode.addRetract()
1943                         gcode.addMove(5, d/2)
1944                         gcode.addPrime()
1945                         gcode.addExtrude(w-5.0, d/2)
1946                         gcode.addRetract(15)
1947                         gcode.addCmd('T0')
1948
1949                         gcode.addMove(z=25)
1950                         gcode.addMove(0, 0)
1951                         gcode.addCmd('M400')
1952
1953                         self.comm.printGCode(gcode.list())
1954                         self.resumeButton.Enable(False)
1955                 elif self._wizardState == 4:
1956                         try:
1957                                 float(self.textEntry.GetValue())
1958                         except ValueError:
1959                                 return
1960                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1961                         self._wizardState = 5
1962                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1963                         self.textEntry.SetValue('0.0')
1964                         self.textEntry.Enable(True)
1965                 elif self._wizardState == 5:
1966                         try:
1967                                 float(self.textEntry.GetValue())
1968                         except ValueError:
1969                                 return
1970                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1971                         self._wizardState = 6
1972                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1973                         self.textEntry.SetValue('')
1974                         self.textEntry.Enable(False)
1975                         self.resumeButton.Enable(False)
1976
1977                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1978                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1979                         gcode = gcodeGenerator.gcodeGenerator()
1980                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1981                         gcode.setPrintSpeed(25)
1982                         gcode.addHome()
1983                         gcode.addCmd('T0')
1984                         gcode.addMove(50, 40, 0.2)
1985                         gcode.addPrime(15)
1986                         for n in xrange(0, 10):
1987                                 gcode.addExtrude(50 + n * 10, 150)
1988                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1989                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1990                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1991                         gcode.addMove(40, 50)
1992                         for n in xrange(0, 10):
1993                                 gcode.addExtrude(150, 50 + n * 10)
1994                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1995                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1996                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1997                         gcode.addRetract(15)
1998
1999                         gcode.addCmd('T1')
2000                         gcode.addMove(50 - x, 30 - y, 0.2)
2001                         gcode.addPrime(15)
2002                         for n in xrange(0, 10):
2003                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
2004                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
2005                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
2006                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
2007                         gcode.addMove(30 - x, 50 - y, 0.2)
2008                         for n in xrange(0, 10):
2009                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
2010                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
2011                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
2012                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
2013                         gcode.addRetract(15)
2014                         gcode.addMove(z=15)
2015                         gcode.addCmd('M400')
2016                         gcode.addCmd('M104 T0 S0')
2017                         gcode.addCmd('M104 T1 S0')
2018                         self.comm.printGCode(gcode.list())
2019                 elif self._wizardState == 7:
2020                         try:
2021                                 n = int(self.textEntry.GetValue()) - 1
2022                         except:
2023                                 return
2024                         x = profile.getMachineSettingFloat('extruder_offset_x1')
2025                         x += -1.0 + n * 0.1
2026                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
2027                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
2028                         self.textEntry.SetValue('10')
2029                         self._wizardState = 8
2030                 elif self._wizardState == 8:
2031                         try:
2032                                 n = int(self.textEntry.GetValue()) - 1
2033                         except:
2034                                 return
2035                         y = profile.getMachineSettingFloat('extruder_offset_y1')
2036                         y += -1.0 + n * 0.1
2037                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
2038                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
2039                         self.infoBox.SetReadyIndicator()
2040                         self._wizardState = 8
2041                         self.comm.close()
2042                         self.resumeButton.Enable(False)
2043
2044         def mcLog(self, message):
2045                 print 'Log:', message
2046
2047         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
2048                 if self._wizardState == 1:
2049                         if temp[0] >= 210 and temp[1] >= 210:
2050                                 self._wizardState = 2
2051                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
2052                                 wx.CallAfter(self.resumeButton.Enable, True)
2053                                 wx.CallAfter(self.resumeButton.SetFocus)
2054
2055         def mcStateChange(self, state):
2056                 if self.comm is None:
2057                         return
2058                 if self.comm.isOperational():
2059                         if self._wizardState == 0:
2060                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
2061                                 self.comm.sendCommand('M105')
2062                                 self.comm.sendCommand('M104 S220 T0')
2063                                 self.comm.sendCommand('M104 S220 T1')
2064                                 self.comm.sendCommand('G28')
2065                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
2066                                 self._wizardState = 1
2067                         if not self.comm.isPrinting():
2068                                 if self._wizardState == 3:
2069                                         self._wizardState = 4
2070                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
2071                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
2072                                         wx.CallAfter(self.textEntry.Enable, True)
2073                                         wx.CallAfter(self.resumeButton.Enable, True)
2074                                         wx.CallAfter(self.resumeButton.SetFocus)
2075                                 elif self._wizardState == 6:
2076                                         self._wizardState = 7
2077                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
2078                                         wx.CallAfter(self.textEntry.SetValue, '10')
2079                                         wx.CallAfter(self.textEntry.Enable, True)
2080                                         wx.CallAfter(self.resumeButton.Enable, True)
2081                                         wx.CallAfter(self.resumeButton.SetFocus)
2082
2083                 elif self.comm.isError():
2084                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
2085
2086         def mcMessage(self, message):
2087                 pass
2088
2089         def mcProgress(self, lineNr):
2090                 pass
2091
2092         def mcZChange(self, newZ):
2093                 pass
2094
2095 class bedLevelWizard(wx.wizard.Wizard):
2096         def __init__(self):
2097                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
2098
2099                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
2100                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
2101
2102                 self.mainPage = bedLevelWizardMain(self)
2103                 self.headOffsetCalibration = None
2104
2105                 self.RunWizard(self.mainPage)
2106                 self.Destroy()
2107
2108         def OnPageChanging(self, e):
2109                 e.GetPage().StoreData()
2110
2111         def OnPageChanged(self, e):
2112                 if e.GetPage().AllowNext():
2113                         self.FindWindowById(wx.ID_FORWARD).Enable()
2114                 else:
2115                         self.FindWindowById(wx.ID_FORWARD).Disable()
2116                 if e.GetPage().AllowBack():
2117                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2118                 else:
2119                         self.FindWindowById(wx.ID_BACKWARD).Disable()
2120
2121 class headOffsetWizard(wx.wizard.Wizard):
2122         def __init__(self):
2123                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
2124
2125                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
2126                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
2127
2128                 self.mainPage = headOffsetCalibrationPage(self)
2129
2130                 self.RunWizard(self.mainPage)
2131                 self.Destroy()
2132
2133         def OnPageChanging(self, e):
2134                 e.GetPage().StoreData()
2135
2136         def OnPageChanged(self, e):
2137                 if e.GetPage().AllowNext():
2138                         self.FindWindowById(wx.ID_FORWARD).Enable()
2139                 else:
2140                         self.FindWindowById(wx.ID_FORWARD).Disable()
2141                 if e.GetPage().AllowBack():
2142                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2143                 else:
2144                         self.FindWindowById(wx.ID_BACKWARD).Disable()