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