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