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