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