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