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