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