chiark / gitweb /
e12bd7af9e760fa71b52247b430a41524baaff63
[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=350
1084         IMAGE_HEIGHT=250
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                 self.LulzbotMini = ImageButton(self.panel, _("LulzBot Mini"), self.GetBitmap('Lulzbot_mini.jpg'), style=ImageButton.IB_GROUP)
1094                 self.LulzbotMini.OnSelected(self.OnLulzbotSelected)
1095                 self.LulzbotMini.SetValue(True)
1096                 self.LulzbotTaz5 = ImageButton(self.panel, _("LulzBot TAZ 5"), self.GetBitmap('Lulzbot_TAZ5.png'))
1097                 self.LulzbotTaz5.OnSelected(self.OnLulzbotSelected)
1098                 self.LulzbotTaz4 = ImageButton(self.panel, _("LulzBot TAZ 4"), self.GetBitmap('Lulzbot_TAZ4.jpg'))
1099                 self.LulzbotTaz4.OnSelected(self.OnLulzbotSelected)
1100                 self.OtherPrinters = ImageButton(self.panel, _("Non-LulzBot Printers"), self.GetBitmap('Other_Printers.jpg'))
1101                 self.OtherPrinters.OnSelected(self.OnOthersSelected)
1102
1103                 self.panel.GetSizer().Add(self.LulzbotMini, pos=(0, 0))
1104                 self.panel.GetSizer().Add(self.LulzbotTaz4, pos=(0, 1))
1105                 self.panel.GetSizer().Add(self.LulzbotTaz5, pos=(1, 0))
1106                 self.panel.GetSizer().Add(self.OtherPrinters, pos=(1, 1))
1107
1108         def GetBitmap(self, filename):
1109                 image = wx.Image(resources.getPathForImage(filename))
1110                 image_scaled = image.Scale(LulzbotMachineSelectPage.IMAGE_WIDTH,
1111                                                                    LulzbotMachineSelectPage.IMAGE_HEIGHT, wx.IMAGE_QUALITY_HIGH)
1112                 return wx.BitmapFromImage(image_scaled)
1113
1114         def OnOthersSelected(self):
1115                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().machineSelectPage)
1116
1117         def OnLulzbotSelected(self):
1118                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotToolheadPage)
1119
1120         def AllowNext(self):
1121                 return True
1122
1123         def AllowBack(self):
1124                 return False
1125
1126         def StoreData(self):
1127                 if self.LulzbotTaz4.GetValue() or self.LulzbotTaz5.GetValue() or self.LulzbotMini.GetValue():
1128                         if self.LulzbotTaz4.GetValue():
1129                                 profile.putMachineSetting('machine_width', '290')
1130                                 profile.putMachineSetting('machine_depth', '275')
1131                                 profile.putMachineSetting('machine_height', '250')
1132                                 profile.putProfileSetting('nozzle_size', '0.35')
1133                                 profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1134                                 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4')
1135                                 profile.putMachineSetting('serial_baud', '115200')
1136                         elif self.LulzbotTaz5.GetValue():
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                                 # Machine type and name are set in the nozzle select page
1142                         else:
1143                                 profile.putMachineSetting('machine_width', '155')
1144                                 profile.putMachineSetting('machine_depth', '155')
1145                                 profile.putMachineSetting('machine_height', '163')
1146                                 profile.putProfileSetting('nozzle_size', '0.5')
1147                                 profile.putMachineSetting('machine_name', 'LulzBot Mini')
1148                                 profile.putMachineSetting('machine_type', 'lulzbot_mini')
1149                                 profile.putMachineSetting('serial_baud', '115200')
1150                                 profile.putMachineSetting('extruder_head_size_min_x', '40')
1151                                 profile.putMachineSetting('extruder_head_size_max_x', '75')
1152                                 profile.putMachineSetting('extruder_head_size_min_y', '25')
1153                                 profile.putMachineSetting('extruder_head_size_max_y', '55')
1154                                 profile.putMachineSetting('extruder_head_size_height', '17')
1155
1156                         profile.putMachineSetting('machine_center_is_zero', 'False')
1157                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
1158                         profile.putMachineSetting('has_heated_bed', 'True')
1159                         profile.putMachineSetting('extruder_head_size_min_x', '0.0')
1160                         profile.putMachineSetting('extruder_head_size_min_y', '0.0')
1161                         profile.putMachineSetting('extruder_head_size_max_x', '0.0')
1162                         profile.putMachineSetting('extruder_head_size_max_y', '0.0')
1163                         profile.putMachineSetting('extruder_head_size_height', '0.0')
1164                         profile.putProfileSetting('retraction_enable', 'True')
1165                         profile.putPreference('startMode', 'Simple')
1166                         profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
1167                         profile.checkAndUpdateMachineName()
1168
1169 class LulzbotReadyPage(InfoPage):
1170         def __init__(self, parent):
1171                 super(LulzbotReadyPage, self).__init__(parent, _("LulzBot TAZ/Mini"))
1172                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1173                 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
1174                 self.AddSeperator()
1175                 self.AddText(_('For more information about using Cura with your LulzBot'))
1176                 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
1177                 self.AddSeperator()
1178
1179 class LulzbotToolheadSelectPage(InfoPage):
1180         url='http://lulzbot.com/toolhead-identification'
1181
1182         def __init__(self, parent):
1183                 super(LulzbotToolheadSelectPage, self).__init__(parent, _("LulzBot Toolhead Selection"))
1184
1185                 self.mini_choices = [_('Standard'), _('Flexystruder')]
1186                 self.taz_choices = [_('Standard v1'),
1187                                            _('Standard v2 0.35 mm nozzle'), _('Standard v2 0.5 mm nozzle'),
1188                                            _('Flexystruder v1'), _('Flexystruder v2'),
1189                                            _('Dually v1'), _('Dually v2'),
1190                                            _('FlexyDually v1'), _('FlexyDually v2')]
1191                 self.description_map = {
1192                                 _('Standard'): _('This is the standard toolhead that comes with the Lulzbot Mini'),
1193                                 _('Flexystruder'): _('This is the Flexystruder for the Lulzbot Mini\nIt is used for printing Flexible materials'),
1194                                 _('Standard v1'): _('This is the standard toolhead that comes with the Lulzbot TAZ 1-2-3 and TAZ 4.\nIt uses the Budaschnozzle for the hotend'),
1195                                 _('Standard v2 0.35 mm nozzle'): _('This is the standard toolhead that comes with the Lulzbot TAZ 5.\nIt uses the Hexagon hotend and a 0.35 mm nozzle'),
1196                                 _('Standard v2 0.5 mm nozzle'): _('This is the standard toolhead that comes with the Lulzbot TAZ 5.\nIt uses the Hexagon hotend and a 0.5 mm nozzle'),
1197                                 _('Flexystruder v1'): _('It\'s the flexy!'),
1198                                 _('Flexystruder v2'): _('It\'s the flexy v2!'),
1199                                 _('Dually v1'): _('It\'s the dualy v1!'),
1200                                 _('Dually v2'): _('It\'s the dual v2!'),
1201                                 _('FlexyDually v1'): _('It\'s the flexy dually v1!'),
1202                                 _('FlexyDually v2'): _('It\'s the flexy dual v2!')
1203                 }
1204                 self.image_map = {
1205                                 _('Standard'): 'Lulzbot_Toolhead_Mini_Standard.jpg',
1206                                 _('Flexystruder'): 'Lulzbot_logo.png',
1207                                 _('Standard v1'): 'Lulzbot_logo.png',
1208                                 _('Standard v2 0.35 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1209                                 _('Standard v2 0.5 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1210                                 _('Flexystruder v1'): 'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg',
1211                                 _('Flexystruder v2'): 'Lulzbot_logo.png',
1212                                 _('Dually v1'): 'Lulzbot_Toolhead_TAZ_Dually_v1.jpg',
1213                                 _('Dually v2'): 'Lulzbot_logo.png',
1214                                 _('FlexyDually v1'): 'Lulzbot_logo.png',
1215                                 _('FlexyDually v2'): 'Lulzbot_logo.png'
1216                 }
1217                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1218                 printer_name = profile.getMachineSetting('machine_type')
1219                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_SHOWN, self.OnPageShown)
1220
1221                 self.AddText(_('Please select your currently installed Tool Head'))
1222                 txt = self.AddText(_('It is important to select the correct Tool head for your printer.\n' +
1223                                                          'Flashing the wrong firmware on your printer can cause damage to your printer and to your toolhead\n' +
1224                                                          'If you are not sure which toolhead you have, please refer to this webpage for more information: '))
1225                 txt.SetForegroundColour(wx.RED)
1226                 button = self.AddButton(self.url)
1227                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1228
1229                 self.AddSeperator()
1230                 #self.combo = self.AddCombo(_('Currently installed Toolhead'), [''])
1231                 #self.combo.SetEditable(False)
1232                 #self.combo.Bind(wx.EVT_COMBOBOX, self.OnToolheadSelected)
1233                 #self.description = self.AddText('\n\n')
1234                 #self.description.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD))
1235                 #self.image = self.AddBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[self.mini_choices[0]])))
1236                 self.panel = self.AddPanel()
1237                 ib1 = ImageButton(self.panel, "Mini", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg')), "Some description", style=ImageButton.IB_GROUP)
1238                 ib2 = ImageButton(self.panel, "TAZ 4 ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Single_v2.jpg')), "Some description 2")
1239                 ib3 = ImageButton(self.panel, "TAZ 5 ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg')))
1240                 ib4 = ImageButton(self.panel, "Others ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Dually_v1.jpg')))
1241                 self.panel.GetSizer().Add(ib1, pos=(0, 0), flag=wx.EXPAND)
1242                 self.panel.GetSizer().Add(ib2, pos=(0, 1), flag=wx.EXPAND)
1243                 self.panel.GetSizer().Add(ib3, pos=(1, 0), flag=wx.EXPAND)
1244                 self.panel.GetSizer().Add(ib4, pos=(1, 1), flag=wx.EXPAND)
1245
1246
1247         def OnPageShown(self, e):
1248                 printer_name = profile.getMachineSetting('machine_type')
1249                 if printer_name == 'lulzbot_mini':
1250                         choices = self.mini_choices
1251                         default = 0
1252                 else:
1253                         choices = self.taz_choices
1254                         if printer_name == 'lulzbot_TAZ_4':
1255                                 default = 0
1256                         elif printer_name == 'lulzbot_TAZ_5':
1257                                 default = 1
1258                         else:
1259                                 default = 2
1260
1261                 self.combo.Clear()
1262                 self.combo.AppendItems(choices)
1263                 self.combo.SetValue(choices[default])
1264                 self.OnToolheadSelected(e)
1265
1266         def OnUrlClick(self, e):
1267                 webbrowser.open(LulzbotToolheadSelectPage.url)
1268
1269         def OnToolheadSelected(self, e):
1270                 toolhead = self.combo.GetValue()
1271                 if self.description_map.has_key(toolhead):
1272                         self.image.SetBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[toolhead])))
1273                         self.description.SetLabel(self.description_map[toolhead])
1274                 else:
1275                         self.image.SetBitmap(wx.NullBitmap)
1276                         self.description.SetLabel('\n\n')
1277                 self.Layout()
1278                 self.Fit()
1279
1280
1281 class Taz5NozzleSelectPage(InfoPage):
1282         url='http://lulzbot.com/printer-identification'
1283
1284         def __init__(self, parent):
1285                 super(Taz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ5"))
1286                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1287
1288                 self.AddText(_(' '))
1289                 self.AddText(_('Please select nozzle size:'))
1290                 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1291                 self.Nozzle35Radio.SetValue(True)
1292                 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1293                 self.AddText(_(' '))
1294                 self.AddSeperator()
1295
1296                 self.AddText(_('If you are not sure which nozzle size you have'))
1297                 self.AddText(_('please check this webpage: '))
1298                 button = self.AddButton(Taz5NozzleSelectPage.url)
1299                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1300
1301         def OnUrlClick(self, e):
1302                 webbrowser.open(Taz5NozzleSelectPage.url)
1303
1304         def StoreData(self):
1305                 if self.Nozzle35Radio.GetValue():
1306                         profile.putProfileSetting('nozzle_size', '0.35')
1307                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.35 nozzle)')
1308                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1309
1310                 else:
1311                         profile.putProfileSetting('nozzle_size', '0.5')
1312                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.5 nozzle)')
1313                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_05nozzle')
1314
1315 class ConfigWizard(wx.wizard.Wizard):
1316         def __init__(self, addNew = False):
1317                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1318
1319                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1320                 if addNew:
1321                         profile.setActiveMachine(profile.getMachineCount())
1322
1323                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1324                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1325                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1326
1327                 self.machineSelectPage = MachineSelectPage(self)
1328                 self.ultimakerSelectParts = SelectParts(self)
1329                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1330                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1331                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1332                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1333                 self.bedLevelPage = bedLevelWizardMain(self)
1334                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1335                 self.printrbotSelectType = PrintrbotPage(self)
1336                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1337                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1338                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1339
1340                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1341                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1342                 self.taz5NozzleSelectPage = Taz5NozzleSelectPage(self)
1343                 self.lulzbotToolheadPage = LulzbotToolheadSelectPage(self)
1344                 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1345
1346                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotToolheadPage)
1347                 wx.wizard.WizardPageSimple.Chain(self.lulzbotToolheadPage, self.lulzbotReadyPage)
1348                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1349                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1350                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1351                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1352                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1353                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1354
1355                 self.RunWizard(self.lulzbotMachineSelectPage)
1356                 self.Destroy()
1357
1358         def OnPageChanging(self, e):
1359                 e.GetPage().StoreData()
1360
1361         def OnPageChanged(self, e):
1362                 if e.GetPage().AllowNext():
1363                         self.FindWindowById(wx.ID_FORWARD).Enable()
1364                 else:
1365                         self.FindWindowById(wx.ID_FORWARD).Disable()
1366                 if e.GetPage().AllowBack():
1367                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1368                 else:
1369                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1370
1371         def OnCancel(self, e):
1372                 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1373                 profile.setActiveMachine(self._old_machine_index)
1374                 profile.removeMachine(new_machine_index)
1375
1376 class bedLevelWizardMain(InfoPage):
1377         def __init__(self, parent):
1378                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1379
1380                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1381                 self.AddSeperator()
1382                 self.AddText(_('It will do the following steps'))
1383                 self.AddText(_('* Move the printer head to each corner'))
1384                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1385                 self.AddText(_('* Print a line around the bed to check if it is level'))
1386                 self.AddSeperator()
1387
1388                 self.connectButton = self.AddButton(_('Connect to printer'))
1389                 self.comm = None
1390
1391                 self.infoBox = self.AddInfoBox()
1392                 self.resumeButton = self.AddButton(_('Resume'))
1393                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1394                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1395                 self.resumeButton.Enable(False)
1396
1397                 self.upButton.Enable(False)
1398                 self.downButton.Enable(False)
1399                 self.upButton2.Enable(False)
1400                 self.downButton2.Enable(False)
1401
1402                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1403                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1404                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1405                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1406                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1407                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1408
1409         def OnConnect(self, e = None):
1410                 if self.comm is not None:
1411                         self.comm.close()
1412                         del self.comm
1413                         self.comm = None
1414                         wx.CallAfter(self.OnConnect)
1415                         return
1416                 self.connectButton.Enable(False)
1417                 self.comm = machineCom.MachineCom(callbackObject=self)
1418                 self.infoBox.SetBusy(_('Connecting to machine.'))
1419                 self._wizardState = 0
1420
1421         def OnBedUp(self, e):
1422                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1423                 self.comm.sendCommand('G92 Z10')
1424                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1425                 self.comm.sendCommand('M400')
1426
1427         def OnBedDown(self, e):
1428                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1429                 self.comm.sendCommand('G92 Z10')
1430                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1431                 self.comm.sendCommand('M400')
1432
1433         def OnBedUp2(self, e):
1434                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1435                 self.comm.sendCommand('G92 Z10')
1436                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1437                 self.comm.sendCommand('M400')
1438
1439         def OnBedDown2(self, e):
1440                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1441                 self.comm.sendCommand('G92 Z10')
1442                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1443                 self.comm.sendCommand('M400')
1444
1445         def AllowNext(self):
1446                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1447                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1448                 return True
1449
1450         def OnResume(self, e):
1451                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1452                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1453                 if self._wizardState == -1:
1454                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1455                         wx.CallAfter(self.upButton.Enable, False)
1456                         wx.CallAfter(self.downButton.Enable, False)
1457                         wx.CallAfter(self.upButton2.Enable, False)
1458                         wx.CallAfter(self.downButton2.Enable, False)
1459                         self.comm.sendCommand('M105')
1460                         self.comm.sendCommand('G28')
1461                         self._wizardState = 1
1462                 elif self._wizardState == 2:
1463                         if profile.getMachineSetting('has_heated_bed') == 'True':
1464                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1465                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1466                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1467                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1468                                 self.comm.sendCommand('M400')
1469                                 self._wizardState = 3
1470                         else:
1471                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1472                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1473                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1474                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1475                                 self.comm.sendCommand('M400')
1476                                 self._wizardState = 3
1477                 elif self._wizardState == 4:
1478                         if profile.getMachineSetting('has_heated_bed') == 'True':
1479                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1480                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1481                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1482                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1483                                 self.comm.sendCommand('M400')
1484                                 self._wizardState = 7
1485                         else:
1486                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1487                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1488                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1489                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1490                                 self.comm.sendCommand('M400')
1491                                 self._wizardState = 5
1492                 elif self._wizardState == 6:
1493                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1494                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1495                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1496                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1497                         self.comm.sendCommand('M400')
1498                         self._wizardState = 7
1499                 elif self._wizardState == 8:
1500                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1501                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1502                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1503                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1504                         self._wizardState = 9
1505                 elif self._wizardState == 10:
1506                         self._wizardState = 11
1507                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1508                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1509                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1510                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1511                         w = profile.getMachineSettingFloat('machine_width') - 10
1512                         d = profile.getMachineSettingFloat('machine_depth')
1513                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1514                         filamentArea = math.pi * filamentRadius * filamentRadius
1515                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1516                         eValue = 0.0
1517
1518                         gcodeList = [
1519                                 'G1 Z2 F%d' % (feedZ),
1520                                 'G92 E0',
1521                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1522                                 'G1 Z0.3 F%d' % (feedZ)]
1523                         eValue += 5.0
1524                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1525
1526                         for i in xrange(0, 3):
1527                                 dist = 5.0 + 0.4 * float(i)
1528                                 eValue += (d - 2.0*dist) * ePerMM
1529                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1530                                 eValue += (w - 2.0*dist) * ePerMM
1531                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1532                                 eValue += (d - 2.0*dist) * ePerMM
1533                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1534                                 eValue += (w - 2.0*dist) * ePerMM
1535                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1536
1537                         gcodeList.append('M400')
1538                         self.comm.printGCode(gcodeList)
1539                 self.resumeButton.Enable(False)
1540
1541         def mcLog(self, message):
1542                 print 'Log:', message
1543
1544         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1545                 if self._wizardState == 1:
1546                         self._wizardState = 2
1547                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1548                         wx.CallAfter(self.resumeButton.Enable, True)
1549                 elif self._wizardState == 3:
1550                         self._wizardState = 4
1551                         if profile.getMachineSetting('has_heated_bed') == 'True':
1552                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1553                         else:
1554                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1555                         wx.CallAfter(self.resumeButton.Enable, True)
1556                 elif self._wizardState == 5:
1557                         self._wizardState = 6
1558                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1559                         wx.CallAfter(self.resumeButton.Enable, True)
1560                 elif self._wizardState == 7:
1561                         self._wizardState = 8
1562                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1563                         wx.CallAfter(self.resumeButton.Enable, True)
1564                 elif self._wizardState == 9:
1565                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1566                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1567                         else:
1568                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1569                                 wx.CallAfter(self.resumeButton.Enable, True)
1570                                 self._wizardState = 10
1571
1572         def mcStateChange(self, state):
1573                 if self.comm is None:
1574                         return
1575                 if self.comm.isOperational():
1576                         if self._wizardState == 0:
1577                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1578                                 wx.CallAfter(self.upButton.Enable, True)
1579                                 wx.CallAfter(self.downButton.Enable, True)
1580                                 wx.CallAfter(self.upButton2.Enable, True)
1581                                 wx.CallAfter(self.downButton2.Enable, True)
1582                                 wx.CallAfter(self.resumeButton.Enable, True)
1583                                 self._wizardState = -1
1584                         elif self._wizardState == 11 and not self.comm.isPrinting():
1585                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1586                                 self.comm.sendCommand('G92 E0')
1587                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1588                                 self.comm.sendCommand('M104 S0')
1589                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1590                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1591                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1592                                 wx.CallAfter(self.connectButton.Enable, True)
1593                                 self._wizardState = 12
1594                 elif self.comm.isError():
1595                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1596
1597         def mcMessage(self, message):
1598                 pass
1599
1600         def mcProgress(self, lineNr):
1601                 pass
1602
1603         def mcZChange(self, newZ):
1604                 pass
1605
1606 class headOffsetCalibrationPage(InfoPage):
1607         def __init__(self, parent):
1608                 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1609
1610                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1611                 self.AddSeperator()
1612
1613                 self.connectButton = self.AddButton(_('Connect to printer'))
1614                 self.comm = None
1615
1616                 self.infoBox = self.AddInfoBox()
1617                 self.textEntry = self.AddTextCtrl('')
1618                 self.textEntry.Enable(False)
1619                 self.resumeButton = self.AddButton(_('Resume'))
1620                 self.resumeButton.Enable(False)
1621
1622                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1623                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1624
1625         def AllowBack(self):
1626                 return True
1627
1628         def OnConnect(self, e = None):
1629                 if self.comm is not None:
1630                         self.comm.close()
1631                         del self.comm
1632                         self.comm = None
1633                         wx.CallAfter(self.OnConnect)
1634                         return
1635                 self.connectButton.Enable(False)
1636                 self.comm = machineCom.MachineCom(callbackObject=self)
1637                 self.infoBox.SetBusy(_('Connecting to machine.'))
1638                 self._wizardState = 0
1639
1640         def OnResume(self, e):
1641                 if self._wizardState == 2:
1642                         self._wizardState = 3
1643                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1644
1645                         w = profile.getMachineSettingFloat('machine_width')
1646                         d = profile.getMachineSettingFloat('machine_depth')
1647
1648                         gcode = gcodeGenerator.gcodeGenerator()
1649                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1650                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1651                         gcode.addCmd('T0')
1652                         gcode.addPrime(15)
1653                         gcode.addCmd('T1')
1654                         gcode.addPrime(15)
1655
1656                         gcode.addCmd('T0')
1657                         gcode.addMove(w/2, 5)
1658                         gcode.addMove(z=0.2)
1659                         gcode.addPrime()
1660                         gcode.addExtrude(w/2, d-5.0)
1661                         gcode.addRetract()
1662                         gcode.addMove(5, d/2)
1663                         gcode.addPrime()
1664                         gcode.addExtrude(w-5.0, d/2)
1665                         gcode.addRetract(15)
1666
1667                         gcode.addCmd('T1')
1668                         gcode.addMove(w/2, 5)
1669                         gcode.addPrime()
1670                         gcode.addExtrude(w/2, d-5.0)
1671                         gcode.addRetract()
1672                         gcode.addMove(5, d/2)
1673                         gcode.addPrime()
1674                         gcode.addExtrude(w-5.0, d/2)
1675                         gcode.addRetract(15)
1676                         gcode.addCmd('T0')
1677
1678                         gcode.addMove(z=25)
1679                         gcode.addMove(0, 0)
1680                         gcode.addCmd('M400')
1681
1682                         self.comm.printGCode(gcode.list())
1683                         self.resumeButton.Enable(False)
1684                 elif self._wizardState == 4:
1685                         try:
1686                                 float(self.textEntry.GetValue())
1687                         except ValueError:
1688                                 return
1689                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1690                         self._wizardState = 5
1691                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1692                         self.textEntry.SetValue('0.0')
1693                         self.textEntry.Enable(True)
1694                 elif self._wizardState == 5:
1695                         try:
1696                                 float(self.textEntry.GetValue())
1697                         except ValueError:
1698                                 return
1699                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1700                         self._wizardState = 6
1701                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1702                         self.textEntry.SetValue('')
1703                         self.textEntry.Enable(False)
1704                         self.resumeButton.Enable(False)
1705
1706                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1707                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1708                         gcode = gcodeGenerator.gcodeGenerator()
1709                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1710                         gcode.setPrintSpeed(25)
1711                         gcode.addHome()
1712                         gcode.addCmd('T0')
1713                         gcode.addMove(50, 40, 0.2)
1714                         gcode.addPrime(15)
1715                         for n in xrange(0, 10):
1716                                 gcode.addExtrude(50 + n * 10, 150)
1717                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1718                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1719                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1720                         gcode.addMove(40, 50)
1721                         for n in xrange(0, 10):
1722                                 gcode.addExtrude(150, 50 + n * 10)
1723                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1724                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1725                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1726                         gcode.addRetract(15)
1727
1728                         gcode.addCmd('T1')
1729                         gcode.addMove(50 - x, 30 - y, 0.2)
1730                         gcode.addPrime(15)
1731                         for n in xrange(0, 10):
1732                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1733                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1734                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1735                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1736                         gcode.addMove(30 - x, 50 - y, 0.2)
1737                         for n in xrange(0, 10):
1738                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1739                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1740                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1741                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1742                         gcode.addRetract(15)
1743                         gcode.addMove(z=15)
1744                         gcode.addCmd('M400')
1745                         gcode.addCmd('M104 T0 S0')
1746                         gcode.addCmd('M104 T1 S0')
1747                         self.comm.printGCode(gcode.list())
1748                 elif self._wizardState == 7:
1749                         try:
1750                                 n = int(self.textEntry.GetValue()) - 1
1751                         except:
1752                                 return
1753                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1754                         x += -1.0 + n * 0.1
1755                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1756                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1757                         self.textEntry.SetValue('10')
1758                         self._wizardState = 8
1759                 elif self._wizardState == 8:
1760                         try:
1761                                 n = int(self.textEntry.GetValue()) - 1
1762                         except:
1763                                 return
1764                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1765                         y += -1.0 + n * 0.1
1766                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1767                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1768                         self.infoBox.SetReadyIndicator()
1769                         self._wizardState = 8
1770                         self.comm.close()
1771                         self.resumeButton.Enable(False)
1772
1773         def mcLog(self, message):
1774                 print 'Log:', message
1775
1776         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1777                 if self._wizardState == 1:
1778                         if temp[0] >= 210 and temp[1] >= 210:
1779                                 self._wizardState = 2
1780                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1781                                 wx.CallAfter(self.resumeButton.Enable, True)
1782                                 wx.CallAfter(self.resumeButton.SetFocus)
1783
1784         def mcStateChange(self, state):
1785                 if self.comm is None:
1786                         return
1787                 if self.comm.isOperational():
1788                         if self._wizardState == 0:
1789                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1790                                 self.comm.sendCommand('M105')
1791                                 self.comm.sendCommand('M104 S220 T0')
1792                                 self.comm.sendCommand('M104 S220 T1')
1793                                 self.comm.sendCommand('G28')
1794                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1795                                 self._wizardState = 1
1796                         if not self.comm.isPrinting():
1797                                 if self._wizardState == 3:
1798                                         self._wizardState = 4
1799                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1800                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1801                                         wx.CallAfter(self.textEntry.Enable, True)
1802                                         wx.CallAfter(self.resumeButton.Enable, True)
1803                                         wx.CallAfter(self.resumeButton.SetFocus)
1804                                 elif self._wizardState == 6:
1805                                         self._wizardState = 7
1806                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1807                                         wx.CallAfter(self.textEntry.SetValue, '10')
1808                                         wx.CallAfter(self.textEntry.Enable, True)
1809                                         wx.CallAfter(self.resumeButton.Enable, True)
1810                                         wx.CallAfter(self.resumeButton.SetFocus)
1811
1812                 elif self.comm.isError():
1813                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1814
1815         def mcMessage(self, message):
1816                 pass
1817
1818         def mcProgress(self, lineNr):
1819                 pass
1820
1821         def mcZChange(self, newZ):
1822                 pass
1823
1824 class bedLevelWizard(wx.wizard.Wizard):
1825         def __init__(self):
1826                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1827
1828                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1829                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1830
1831                 self.mainPage = bedLevelWizardMain(self)
1832                 self.headOffsetCalibration = None
1833
1834                 self.RunWizard(self.mainPage)
1835                 self.Destroy()
1836
1837         def OnPageChanging(self, e):
1838                 e.GetPage().StoreData()
1839
1840         def OnPageChanged(self, e):
1841                 if e.GetPage().AllowNext():
1842                         self.FindWindowById(wx.ID_FORWARD).Enable()
1843                 else:
1844                         self.FindWindowById(wx.ID_FORWARD).Disable()
1845                 if e.GetPage().AllowBack():
1846                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1847                 else:
1848                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1849
1850 class headOffsetWizard(wx.wizard.Wizard):
1851         def __init__(self):
1852                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1853
1854                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1855                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1856
1857                 self.mainPage = headOffsetCalibrationPage(self)
1858
1859                 self.RunWizard(self.mainPage)
1860                 self.Destroy()
1861
1862         def OnPageChanging(self, e):
1863                 e.GetPage().StoreData()
1864
1865         def OnPageChanged(self, e):
1866                 if e.GetPage().AllowNext():
1867                         self.FindWindowById(wx.ID_FORWARD).Enable()
1868                 else:
1869                         self.FindWindowById(wx.ID_FORWARD).Disable()
1870                 if e.GetPage().AllowBack():
1871                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1872                 else:
1873                         self.FindWindowById(wx.ID_BACKWARD).Disable()