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