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