chiark / gitweb /
08afffaebc24c2e30e7184e62e50fe34b1b51d73
[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().lulzbotFirmwarePage)
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, _('Single Extruder v2'),
1213                                                                                         'Lulzbot_Toolhead_Mini_Standard.jpg', image_size,
1214                                                                                         style=ImageButton.IB_GROUP)
1215                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v2'),
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
1283         def OnSingleV2(self):
1284                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1285                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage, self.GetParent().lulzbotFirmwarePage)
1286
1287         def OnNonSingle(self):
1288                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1289
1290         def StoreData(self):
1291                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1292                         taz_version = 4
1293                 else:
1294                         taz_version = 5
1295                 version = (taz_version, self.version)
1296                 if self.single.GetValue():
1297                         profile.putProfileSetting('nozzle_size', '0.5' if self.version == 2 else '0.35')
1298                         profile.putMachineSetting('extruder_amount', '1')
1299                         profile.putMachineSetting('toolhead', 'Single Extruder V%d' % self.version)
1300                         profile.putMachineSetting('toolhead_shortname', '')
1301                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_SingleV%d' % version)
1302                 elif self.flexy.GetValue():
1303                         profile.putProfileSetting('nozzle_size', '0.6')
1304                         profile.putMachineSetting('extruder_amount', '1')
1305                         profile.putMachineSetting('toolhead', 'Flexystruder V%d' % self.version)
1306                         profile.putMachineSetting('toolhead_shortname', 'Flexystruder v%d' % self.version)
1307                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexystruderV%d' % version)
1308                 elif self.dually.GetValue():
1309                         profile.putProfileSetting('nozzle_size', '0.5')
1310                         profile.putMachineSetting('extruder_amount', '2')
1311                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1312                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1313                         profile.putMachineSetting('toolhead', 'Dual Extruder V%d' % self.version)
1314                         profile.putMachineSetting('toolhead_shortname', 'Dual v%d' % self.version)
1315                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_DualV%d' % version)
1316                 elif self.flexydually.GetValue():
1317                         profile.putProfileSetting('nozzle_size', '0.6')
1318                         profile.putMachineSetting('extruder_amount', '2')
1319                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1320                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1321                         profile.putMachineSetting('toolhead', 'FlexyDually V%d' % self.version)
1322                         profile.putMachineSetting('toolhead_shortname', 'FlexyDually v%d' % self.version)
1323                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexyDuallyV%d' % version)
1324
1325
1326 class LulzbotHotendSelectPage(LulzbotToolheadSelectPage):
1327         def __init__(self, parent, allowBack = True):
1328                 super(LulzbotHotendSelectPage, self).__init__(parent, _("LulzBot Tool Head Hotend Selection"))
1329
1330                 self.allowBack = allowBack
1331                 self.panel = self.AddPanel()
1332                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1333                 self.v1 = self.AddImageButton(self.panel, 0, 0, _('v1 (Budaschnozzle)'),
1334                                                                                         'Lulzbot_Toolhead_v1.jpg', image_size,
1335                                                                                         style=ImageButton.IB_GROUP)
1336                 self.v2 = self.AddImageButton(self.panel, 0, 1, _('v2 (Hexagon)'),
1337                                                                                         'Lulzbot_Toolhead_v2.jpg', image_size)
1338                 self.v1.SetValue(True)
1339
1340         def AllowBack(self):
1341                 return self.allowBack
1342
1343         def StoreData(self):
1344                 self.GetParent().lulzbotTazToolheadPage.SetVersion(1 if self.v1.GetValue() else 2)
1345
1346 class LulzbotTaz5NozzleSelectPage(LulzbotToolheadSelectPage):
1347         url2='http://lulzbot.com/printer-identification'
1348
1349         def __init__(self, parent):
1350                 super(LulzbotTaz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ Single V2 Nozzle Selection"))
1351
1352                 self.AddText(_('Please select your Hexagon hotend\'s nozzle diameter:'))
1353                 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1354                 self.Nozzle35Radio.SetValue(True)
1355                 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1356                 self.AddText(_(' '))
1357                 self.AddSeperator()
1358
1359                 self.AddText(_('If you are not sure which nozzle diameter you have'))
1360                 self.AddText(_('please check this webpage: '))
1361                 button = self.AddButton(LulzbotTaz5NozzleSelectPage.url2)
1362                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1363
1364         def OnUrlClick(self, e):
1365                 webbrowser.open(LulzbotTaz5NozzleSelectPage.url2)
1366
1367         def StoreData(self):
1368                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1369                         taz_version = 4
1370                 else:
1371                         taz_version = 5
1372                 if self.Nozzle35Radio.GetValue():
1373                         profile.putProfileSetting('nozzle_size', '0.35')
1374                         profile.putMachineSetting('toolhead', 'Single Extruder V2 (0.35mm nozzle)')
1375                         profile.putMachineSetting('toolhead_shortname', '0.35 nozzle')
1376                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_035nozzle' % taz_version)
1377
1378                 else:
1379                         profile.putProfileSetting('nozzle_size', '0.5')
1380                         profile.putMachineSetting('toolhead', 'Single Extruder V2 (0.5mm nozzle)')
1381                         profile.putMachineSetting('toolhead_shortname', '0.5 nozzle')
1382                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_05nozzle' % taz_version)
1383
1384 class LulzbotFirmwareUpdatePage(InfoPage):
1385         def __init__(self, parent):
1386                 super(LulzbotFirmwareUpdatePage, self).__init__(parent, _("LulzBot Firmware Update"))
1387
1388                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1389
1390                 self.AddText(_('Your LulzBot printer\'s firmware will now be updated.'))
1391                 self.AddSeperator()
1392                 self.AddText(_("Firmware is the \"brain\" inside your 3D printer.\n" +
1393                                            "It controls motors/heaters, regulates temperature\n" +
1394                                            "and ultimately makes your printer work."))
1395                 self.AddHiddenSeperator()
1396                 self.AddText(_("When changing tool heads it is neccesary to update the firmware.\n" +
1397                                            "Please connect (with a USB cable) the printer you are currently configuring and power it on.\n" +
1398                                            "Disconnecting all other 3D printers helps reduce confusion."))
1399                 self.AddHiddenSeperator()
1400                 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Flash the firmware'), _('Skip upgrade'))
1401                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
1402                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
1403
1404         def AllowNext(self):
1405                 return version.isDevVersion()
1406
1407         def OnUpgradeClick(self, e):
1408                 if firmwareInstall.InstallFirmware():
1409                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1410
1411         def OnSkipClick(self, e):
1412                 dlg = wx.MessageDialog(self,
1413                                 _("WARNING: You risk serious damage to your printer by not updating the firmware. This is not a recommended course of action!\n\n" + 
1414                                   "Are you sure you want to skip the firmware upgrade?"),
1415                                 _('Skip firmware upgrade?'),
1416                                 wx.YES_NO | wx.ICON_EXCLAMATION)
1417                 skip = dlg.ShowModal() == wx.ID_YES
1418                 dlg.Destroy()
1419                 if skip:
1420                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1421                         self.GetParent().ShowPage(self.GetNext())
1422
1423 class LulzbotChangeToolheadWizard(wx.wizard.Wizard):
1424         def __init__(self):
1425                 super(LulzbotChangeToolheadWizard, self).__init__(None, -1, _("Change Lulzbot Tool Head Wizard"))
1426
1427                 self._nozzle_size = profile.getProfileSettingFloat('nozzle_size')
1428                 self._machine_name = profile.getMachineSetting('machine_name')
1429                 self._machine_type = profile.getMachineSetting('machine_type')
1430                 self._extruder_amount = int(profile.getMachineSettingFloat('extruder_amount'))
1431
1432                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1433                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1434                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1435
1436                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1437                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1438                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self, False)
1439                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1440                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self, False)
1441                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1442
1443                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotFirmwarePage)
1444                 wx.wizard.WizardPageSimple.Chain(self.lulzbotFirmwarePage, self.lulzbotReadyPage)
1445                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1446
1447                 if profile.getMachineSetting('machine_type').startswith('lulzbot_mini'):
1448                         self.RunWizard(self.lulzbotMiniToolheadPage)
1449                 else:
1450                         self.RunWizard(self.lulzbotTazHotendPage)
1451                 self.Destroy()
1452
1453         def OnPageChanging(self, e):
1454                 e.GetPage().StoreData()
1455
1456         def OnPageChanged(self, e):
1457                 if e.GetPage().AllowNext():
1458                         self.FindWindowById(wx.ID_FORWARD).Enable()
1459                 else:
1460                         self.FindWindowById(wx.ID_FORWARD).Disable()
1461                 if e.GetPage().AllowBack():
1462                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1463                 else:
1464                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1465
1466         def OnCancel(self, e):
1467                 profile.putProfileSetting('nozzle_size', self._nozzle_size)
1468                 profile.putMachineSetting('machine_name', self._machine_name)
1469                 profile.putMachineSetting('machine_type', self._machine_type)
1470                 profile.putMachineSetting('extruder_amount', self._extruder_amount)
1471
1472 class ConfigWizard(wx.wizard.Wizard):
1473         def __init__(self, addNew = False):
1474                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1475
1476                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1477                 if addNew:
1478                         profile.setActiveMachine(profile.getMachineCount())
1479
1480                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1481                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1482                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1483
1484                 self.machineSelectPage = MachineSelectPage(self)
1485                 self.ultimakerSelectParts = SelectParts(self)
1486                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1487                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1488                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1489                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1490                 self.bedLevelPage = bedLevelWizardMain(self)
1491                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1492                 self.printrbotSelectType = PrintrbotPage(self)
1493                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1494                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1495                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1496
1497                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1498                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1499                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1500                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self)
1501                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1502                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self)
1503                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1504                 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1505
1506                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotMiniToolheadPage)
1507                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotFirmwarePage)
1508                 wx.wizard.WizardPageSimple.Chain(self.lulzbotFirmwarePage, self.lulzbotReadyPage)
1509                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1510                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1511                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1512                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1513                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1514                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1515                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1516
1517                 self.RunWizard(self.lulzbotMachineSelectPage)
1518                 self.Destroy()
1519
1520         def OnPageChanging(self, e):
1521                 e.GetPage().StoreData()
1522
1523         def OnPageChanged(self, e):
1524                 if e.GetPage().AllowNext():
1525                         self.FindWindowById(wx.ID_FORWARD).Enable()
1526                 else:
1527                         self.FindWindowById(wx.ID_FORWARD).Disable()
1528                 if e.GetPage().AllowBack():
1529                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1530                 else:
1531                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1532
1533         def OnCancel(self, e):
1534                 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1535                 profile.setActiveMachine(self._old_machine_index)
1536                 profile.removeMachine(new_machine_index)
1537
1538 class bedLevelWizardMain(InfoPage):
1539         def __init__(self, parent):
1540                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1541
1542                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1543                 self.AddSeperator()
1544                 self.AddText(_('It will do the following steps'))
1545                 self.AddText(_('* Move the printer head to each corner'))
1546                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1547                 self.AddText(_('* Print a line around the bed to check if it is level'))
1548                 self.AddSeperator()
1549
1550                 self.connectButton = self.AddButton(_('Connect to printer'))
1551                 self.comm = None
1552
1553                 self.infoBox = self.AddInfoBox()
1554                 self.resumeButton = self.AddButton(_('Resume'))
1555                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1556                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1557                 self.resumeButton.Enable(False)
1558
1559                 self.upButton.Enable(False)
1560                 self.downButton.Enable(False)
1561                 self.upButton2.Enable(False)
1562                 self.downButton2.Enable(False)
1563
1564                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1565                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1566                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1567                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1568                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1569                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1570
1571         def OnConnect(self, e = None):
1572                 if self.comm is not None:
1573                         self.comm.close()
1574                         del self.comm
1575                         self.comm = None
1576                         wx.CallAfter(self.OnConnect)
1577                         return
1578                 self.connectButton.Enable(False)
1579                 self.comm = machineCom.MachineCom(callbackObject=self)
1580                 self.infoBox.SetBusy(_('Connecting to machine.'))
1581                 self._wizardState = 0
1582
1583         def OnBedUp(self, e):
1584                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1585                 self.comm.sendCommand('G92 Z10')
1586                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1587                 self.comm.sendCommand('M400')
1588
1589         def OnBedDown(self, e):
1590                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1591                 self.comm.sendCommand('G92 Z10')
1592                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1593                 self.comm.sendCommand('M400')
1594
1595         def OnBedUp2(self, e):
1596                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1597                 self.comm.sendCommand('G92 Z10')
1598                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1599                 self.comm.sendCommand('M400')
1600
1601         def OnBedDown2(self, e):
1602                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1603                 self.comm.sendCommand('G92 Z10')
1604                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1605                 self.comm.sendCommand('M400')
1606
1607         def AllowNext(self):
1608                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1609                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1610                 return True
1611
1612         def OnResume(self, e):
1613                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1614                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1615                 if self._wizardState == -1:
1616                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1617                         wx.CallAfter(self.upButton.Enable, False)
1618                         wx.CallAfter(self.downButton.Enable, False)
1619                         wx.CallAfter(self.upButton2.Enable, False)
1620                         wx.CallAfter(self.downButton2.Enable, False)
1621                         self.comm.sendCommand('M105')
1622                         self.comm.sendCommand('G28')
1623                         self._wizardState = 1
1624                 elif self._wizardState == 2:
1625                         if profile.getMachineSetting('has_heated_bed') == 'True':
1626                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1627                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1628                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1629                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1630                                 self.comm.sendCommand('M400')
1631                                 self._wizardState = 3
1632                         else:
1633                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1634                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1635                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1636                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1637                                 self.comm.sendCommand('M400')
1638                                 self._wizardState = 3
1639                 elif self._wizardState == 4:
1640                         if profile.getMachineSetting('has_heated_bed') == 'True':
1641                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1642                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1643                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1644                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1645                                 self.comm.sendCommand('M400')
1646                                 self._wizardState = 7
1647                         else:
1648                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1649                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1650                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1651                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1652                                 self.comm.sendCommand('M400')
1653                                 self._wizardState = 5
1654                 elif self._wizardState == 6:
1655                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1656                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1657                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1658                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1659                         self.comm.sendCommand('M400')
1660                         self._wizardState = 7
1661                 elif self._wizardState == 8:
1662                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1663                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1664                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1665                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1666                         self._wizardState = 9
1667                 elif self._wizardState == 10:
1668                         self._wizardState = 11
1669                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1670                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1671                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1672                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1673                         w = profile.getMachineSettingFloat('machine_width') - 10
1674                         d = profile.getMachineSettingFloat('machine_depth')
1675                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1676                         filamentArea = math.pi * filamentRadius * filamentRadius
1677                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1678                         eValue = 0.0
1679
1680                         gcodeList = [
1681                                 'G1 Z2 F%d' % (feedZ),
1682                                 'G92 E0',
1683                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1684                                 'G1 Z0.3 F%d' % (feedZ)]
1685                         eValue += 5.0
1686                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1687
1688                         for i in xrange(0, 3):
1689                                 dist = 5.0 + 0.4 * float(i)
1690                                 eValue += (d - 2.0*dist) * ePerMM
1691                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1692                                 eValue += (w - 2.0*dist) * ePerMM
1693                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1694                                 eValue += (d - 2.0*dist) * ePerMM
1695                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1696                                 eValue += (w - 2.0*dist) * ePerMM
1697                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1698
1699                         gcodeList.append('M400')
1700                         self.comm.printGCode(gcodeList)
1701                 self.resumeButton.Enable(False)
1702
1703         def mcLog(self, message):
1704                 print 'Log:', message
1705
1706         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1707                 if self._wizardState == 1:
1708                         self._wizardState = 2
1709                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1710                         wx.CallAfter(self.resumeButton.Enable, True)
1711                 elif self._wizardState == 3:
1712                         self._wizardState = 4
1713                         if profile.getMachineSetting('has_heated_bed') == 'True':
1714                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1715                         else:
1716                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1717                         wx.CallAfter(self.resumeButton.Enable, True)
1718                 elif self._wizardState == 5:
1719                         self._wizardState = 6
1720                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1721                         wx.CallAfter(self.resumeButton.Enable, True)
1722                 elif self._wizardState == 7:
1723                         self._wizardState = 8
1724                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1725                         wx.CallAfter(self.resumeButton.Enable, True)
1726                 elif self._wizardState == 9:
1727                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1728                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1729                         else:
1730                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1731                                 wx.CallAfter(self.resumeButton.Enable, True)
1732                                 self._wizardState = 10
1733
1734         def mcStateChange(self, state):
1735                 if self.comm is None:
1736                         return
1737                 if self.comm.isOperational():
1738                         if self._wizardState == 0:
1739                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1740                                 wx.CallAfter(self.upButton.Enable, True)
1741                                 wx.CallAfter(self.downButton.Enable, True)
1742                                 wx.CallAfter(self.upButton2.Enable, True)
1743                                 wx.CallAfter(self.downButton2.Enable, True)
1744                                 wx.CallAfter(self.resumeButton.Enable, True)
1745                                 self._wizardState = -1
1746                         elif self._wizardState == 11 and not self.comm.isPrinting():
1747                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1748                                 self.comm.sendCommand('G92 E0')
1749                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1750                                 self.comm.sendCommand('M104 S0')
1751                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1752                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1753                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1754                                 wx.CallAfter(self.connectButton.Enable, True)
1755                                 self._wizardState = 12
1756                 elif self.comm.isError():
1757                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1758
1759         def mcMessage(self, message):
1760                 pass
1761
1762         def mcProgress(self, lineNr):
1763                 pass
1764
1765         def mcZChange(self, newZ):
1766                 pass
1767
1768 class headOffsetCalibrationPage(InfoPage):
1769         def __init__(self, parent):
1770                 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1771
1772                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1773                 self.AddSeperator()
1774
1775                 self.connectButton = self.AddButton(_('Connect to printer'))
1776                 self.comm = None
1777
1778                 self.infoBox = self.AddInfoBox()
1779                 self.textEntry = self.AddTextCtrl('')
1780                 self.textEntry.Enable(False)
1781                 self.resumeButton = self.AddButton(_('Resume'))
1782                 self.resumeButton.Enable(False)
1783
1784                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1785                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1786
1787         def AllowBack(self):
1788                 return True
1789
1790         def OnConnect(self, e = None):
1791                 if self.comm is not None:
1792                         self.comm.close()
1793                         del self.comm
1794                         self.comm = None
1795                         wx.CallAfter(self.OnConnect)
1796                         return
1797                 self.connectButton.Enable(False)
1798                 self.comm = machineCom.MachineCom(callbackObject=self)
1799                 self.infoBox.SetBusy(_('Connecting to machine.'))
1800                 self._wizardState = 0
1801
1802         def OnResume(self, e):
1803                 if self._wizardState == 2:
1804                         self._wizardState = 3
1805                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1806
1807                         w = profile.getMachineSettingFloat('machine_width')
1808                         d = profile.getMachineSettingFloat('machine_depth')
1809
1810                         gcode = gcodeGenerator.gcodeGenerator()
1811                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1812                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1813                         gcode.addCmd('T0')
1814                         gcode.addPrime(15)
1815                         gcode.addCmd('T1')
1816                         gcode.addPrime(15)
1817
1818                         gcode.addCmd('T0')
1819                         gcode.addMove(w/2, 5)
1820                         gcode.addMove(z=0.2)
1821                         gcode.addPrime()
1822                         gcode.addExtrude(w/2, d-5.0)
1823                         gcode.addRetract()
1824                         gcode.addMove(5, d/2)
1825                         gcode.addPrime()
1826                         gcode.addExtrude(w-5.0, d/2)
1827                         gcode.addRetract(15)
1828
1829                         gcode.addCmd('T1')
1830                         gcode.addMove(w/2, 5)
1831                         gcode.addPrime()
1832                         gcode.addExtrude(w/2, d-5.0)
1833                         gcode.addRetract()
1834                         gcode.addMove(5, d/2)
1835                         gcode.addPrime()
1836                         gcode.addExtrude(w-5.0, d/2)
1837                         gcode.addRetract(15)
1838                         gcode.addCmd('T0')
1839
1840                         gcode.addMove(z=25)
1841                         gcode.addMove(0, 0)
1842                         gcode.addCmd('M400')
1843
1844                         self.comm.printGCode(gcode.list())
1845                         self.resumeButton.Enable(False)
1846                 elif self._wizardState == 4:
1847                         try:
1848                                 float(self.textEntry.GetValue())
1849                         except ValueError:
1850                                 return
1851                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1852                         self._wizardState = 5
1853                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1854                         self.textEntry.SetValue('0.0')
1855                         self.textEntry.Enable(True)
1856                 elif self._wizardState == 5:
1857                         try:
1858                                 float(self.textEntry.GetValue())
1859                         except ValueError:
1860                                 return
1861                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1862                         self._wizardState = 6
1863                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1864                         self.textEntry.SetValue('')
1865                         self.textEntry.Enable(False)
1866                         self.resumeButton.Enable(False)
1867
1868                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1869                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1870                         gcode = gcodeGenerator.gcodeGenerator()
1871                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1872                         gcode.setPrintSpeed(25)
1873                         gcode.addHome()
1874                         gcode.addCmd('T0')
1875                         gcode.addMove(50, 40, 0.2)
1876                         gcode.addPrime(15)
1877                         for n in xrange(0, 10):
1878                                 gcode.addExtrude(50 + n * 10, 150)
1879                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1880                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1881                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1882                         gcode.addMove(40, 50)
1883                         for n in xrange(0, 10):
1884                                 gcode.addExtrude(150, 50 + n * 10)
1885                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1886                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1887                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1888                         gcode.addRetract(15)
1889
1890                         gcode.addCmd('T1')
1891                         gcode.addMove(50 - x, 30 - y, 0.2)
1892                         gcode.addPrime(15)
1893                         for n in xrange(0, 10):
1894                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1895                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1896                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1897                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1898                         gcode.addMove(30 - x, 50 - y, 0.2)
1899                         for n in xrange(0, 10):
1900                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1901                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1902                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1903                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1904                         gcode.addRetract(15)
1905                         gcode.addMove(z=15)
1906                         gcode.addCmd('M400')
1907                         gcode.addCmd('M104 T0 S0')
1908                         gcode.addCmd('M104 T1 S0')
1909                         self.comm.printGCode(gcode.list())
1910                 elif self._wizardState == 7:
1911                         try:
1912                                 n = int(self.textEntry.GetValue()) - 1
1913                         except:
1914                                 return
1915                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1916                         x += -1.0 + n * 0.1
1917                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1918                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1919                         self.textEntry.SetValue('10')
1920                         self._wizardState = 8
1921                 elif self._wizardState == 8:
1922                         try:
1923                                 n = int(self.textEntry.GetValue()) - 1
1924                         except:
1925                                 return
1926                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1927                         y += -1.0 + n * 0.1
1928                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1929                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1930                         self.infoBox.SetReadyIndicator()
1931                         self._wizardState = 8
1932                         self.comm.close()
1933                         self.resumeButton.Enable(False)
1934
1935         def mcLog(self, message):
1936                 print 'Log:', message
1937
1938         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1939                 if self._wizardState == 1:
1940                         if temp[0] >= 210 and temp[1] >= 210:
1941                                 self._wizardState = 2
1942                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1943                                 wx.CallAfter(self.resumeButton.Enable, True)
1944                                 wx.CallAfter(self.resumeButton.SetFocus)
1945
1946         def mcStateChange(self, state):
1947                 if self.comm is None:
1948                         return
1949                 if self.comm.isOperational():
1950                         if self._wizardState == 0:
1951                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1952                                 self.comm.sendCommand('M105')
1953                                 self.comm.sendCommand('M104 S220 T0')
1954                                 self.comm.sendCommand('M104 S220 T1')
1955                                 self.comm.sendCommand('G28')
1956                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1957                                 self._wizardState = 1
1958                         if not self.comm.isPrinting():
1959                                 if self._wizardState == 3:
1960                                         self._wizardState = 4
1961                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1962                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1963                                         wx.CallAfter(self.textEntry.Enable, True)
1964                                         wx.CallAfter(self.resumeButton.Enable, True)
1965                                         wx.CallAfter(self.resumeButton.SetFocus)
1966                                 elif self._wizardState == 6:
1967                                         self._wizardState = 7
1968                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1969                                         wx.CallAfter(self.textEntry.SetValue, '10')
1970                                         wx.CallAfter(self.textEntry.Enable, True)
1971                                         wx.CallAfter(self.resumeButton.Enable, True)
1972                                         wx.CallAfter(self.resumeButton.SetFocus)
1973
1974                 elif self.comm.isError():
1975                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1976
1977         def mcMessage(self, message):
1978                 pass
1979
1980         def mcProgress(self, lineNr):
1981                 pass
1982
1983         def mcZChange(self, newZ):
1984                 pass
1985
1986 class bedLevelWizard(wx.wizard.Wizard):
1987         def __init__(self):
1988                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1989
1990                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1991                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1992
1993                 self.mainPage = bedLevelWizardMain(self)
1994                 self.headOffsetCalibration = None
1995
1996                 self.RunWizard(self.mainPage)
1997                 self.Destroy()
1998
1999         def OnPageChanging(self, e):
2000                 e.GetPage().StoreData()
2001
2002         def OnPageChanged(self, e):
2003                 if e.GetPage().AllowNext():
2004                         self.FindWindowById(wx.ID_FORWARD).Enable()
2005                 else:
2006                         self.FindWindowById(wx.ID_FORWARD).Disable()
2007                 if e.GetPage().AllowBack():
2008                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2009                 else:
2010                         self.FindWindowById(wx.ID_BACKWARD).Disable()
2011
2012 class headOffsetWizard(wx.wizard.Wizard):
2013         def __init__(self):
2014                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
2015
2016                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
2017                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
2018
2019                 self.mainPage = headOffsetCalibrationPage(self)
2020
2021                 self.RunWizard(self.mainPage)
2022                 self.Destroy()
2023
2024         def OnPageChanging(self, e):
2025                 e.GetPage().StoreData()
2026
2027         def OnPageChanged(self, e):
2028                 if e.GetPage().AllowNext():
2029                         self.FindWindowById(wx.ID_FORWARD).Enable()
2030                 else:
2031                         self.FindWindowById(wx.ID_FORWARD).Disable()
2032                 if e.GetPage().AllowBack():
2033                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2034                 else:
2035                         self.FindWindowById(wx.ID_BACKWARD).Disable()