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