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