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