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