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