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