chiark / gitweb /
Fix back/next issue and ImageButton paddings
[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, _("Non-LulzBot 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                                 profile.putMachineSetting('machine_width', '290')
1137                                 profile.putMachineSetting('machine_depth', '275')
1138                                 profile.putMachineSetting('machine_height', '250')
1139                                 profile.putProfileSetting('nozzle_size', '0.35')
1140                                 profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1141                                 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4')
1142                                 profile.putMachineSetting('serial_baud', '115200')
1143                         elif self.LulzbotTaz5.GetValue():
1144                                 profile.putProfileSetting('nozzle_size', '0.35')
1145                                 profile.putMachineSetting('machine_name', 'LulzBot TAZ 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                                 profile.putMachineSetting('machine_width', '155')
1153                                 profile.putMachineSetting('machine_depth', '155')
1154                                 profile.putMachineSetting('machine_height', '163')
1155                                 profile.putProfileSetting('nozzle_size', '0.5')
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.putMachineSetting('machine_name', 'LulzBot Mini')
1221                         profile.putMachineSetting('machine_type', 'lulzbot_mini')
1222                 else:
1223                         profile.putMachineSetting('machine_name', 'LulzBot Mini (Flexy)')
1224                         profile.putMachineSetting('machine_type', 'lulzbot_mini_flexy')
1225
1226
1227 class LulzbotTazToolheadSelectPage(LulzbotToolheadSelectPage):
1228         def __init__(self, parent):
1229                 super(LulzbotTazToolheadSelectPage, self).__init__(parent, _("LulzBot TAZ Toolhead Selection"))
1230
1231                 self.panel = self.AddPanel()
1232                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1233                 self.standard = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v1'),
1234                                                                                         'Lulzbot_Toolhead_TAZ_Single_v1.jpg', image_size,
1235                                                                                         style=ImageButton.IB_GROUP)
1236                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v1'),
1237                                                                                         'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg', image_size)
1238                 self.dually = self.AddImageButton(self.panel, 1, 0, _('Dual Extruder v1'),
1239                                                                                         'Lulzbot_Toolhead_TAZ_Dually_v1.jpg', image_size)
1240                 self.flexydually = self.AddImageButton(self.panel, 1, 1, _('FlexyDually v1'),
1241                                                                                         'Lulzbot_Toolhead_TAZ_FlexyDually_v1.jpg', image_size)
1242                 self.SetVersion(1)
1243                 self.standard.SetValue(True)
1244
1245         def SetVersion(self, version):
1246                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1247                 self.standard.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Single_v%d.jpg' % version, image_size))
1248                 self.standard.SetLabel(_('Single Extruder v%d' % version))
1249                 self.flexy.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Flexystruder_v%d.jpg' % version, image_size))
1250                 self.flexy.SetLabel(_('Flexystruder v%d' % version))
1251                 self.dually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Dually_v%d.jpg' % version, image_size))
1252                 self.dually.SetLabel(_('Dual Extruder v%d' % version))
1253                 self.flexydually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_FlexyDually_v%d.jpg' % version, image_size))
1254                 self.flexydually.SetLabel(_('FlexyDually v%d' % version))
1255                 if version == 1:
1256                         self.standard.OnSelected(None)
1257                         self.flexy.OnSelected(None)
1258                         self.dually.OnSelected(None)
1259                         self.flexydually.OnSelected(None)
1260                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1261                 elif version == 2:
1262                         self.standard.OnSelected(self.OnStandardV2)
1263                         self.flexy.OnSelected(self.OnNonStandard)
1264                         self.dually.OnSelected(self.OnNonStandard)
1265                         self.flexydually.OnSelected(self.OnNonStandard)
1266                         if self.standard.GetValue():
1267                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1268                         else:
1269                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1270
1271         def OnStandardV2(self):
1272                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1273
1274         def OnNonStandard(self):
1275                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1276
1277
1278         def StoreData(self):
1279                 pass
1280
1281
1282 class LulzbotHotendSelectPage(LulzbotToolheadSelectPage):
1283         def __init__(self, parent):
1284                 super(LulzbotHotendSelectPage, self).__init__(parent, _("LulzBot Toolhead Hotend Selection"))
1285
1286                 self.panel = self.AddPanel()
1287                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1288                 self.v1 = self.AddImageButton(self.panel, 0, 0, _('v1 (Budaschnozzle Hotends)'),
1289                                                                                         'Lulzbot_Toolhead_v1.jpg', image_size,
1290                                                                                         style=ImageButton.IB_GROUP)
1291                 self.v2 = self.AddImageButton(self.panel, 0, 1, _('v2 (Hexagon Hotends)'),
1292                                                                                         'Lulzbot_Toolhead_v2.jpg', image_size)
1293                 self.v1.SetValue(True)
1294
1295         def StoreData(self):
1296                 self.GetParent().lulzbotTazToolheadPage.SetVersion(1 if self.v1.GetValue() else 2)
1297
1298
1299 class LulzbotTaz5NozzleSelectPage(LulzbotToolheadSelectPage):
1300         url2='http://lulzbot.com/printer-identification'
1301
1302         def __init__(self, parent):
1303                 super(LulzbotTaz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ Standard V2 Nozzle Selection"))
1304
1305                 self.AddText(_('Please select your Hexagon hotend\'s nozzle size:'))
1306                 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1307                 self.Nozzle35Radio.SetValue(True)
1308                 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1309                 self.AddText(_(' '))
1310                 self.AddSeperator()
1311
1312                 self.AddText(_('If you are not sure which nozzle size you have'))
1313                 self.AddText(_('please check this webpage: '))
1314                 button = self.AddButton(LulzbotTaz5NozzleSelectPage.url2)
1315                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1316
1317         def OnUrlClick(self, e):
1318                 webbrowser.open(LulzbotTaz5NozzleSelectPage.url2)
1319
1320         def StoreData(self):
1321                 if self.Nozzle35Radio.GetValue():
1322                         profile.putProfileSetting('nozzle_size', '0.35')
1323                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.35 nozzle)')
1324                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_035nozzle')
1325
1326                 else:
1327                         profile.putProfileSetting('nozzle_size', '0.5')
1328                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.5 nozzle)')
1329                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_05nozzle')
1330
1331 class ConfigWizard(wx.wizard.Wizard):
1332         def __init__(self, addNew = False):
1333                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1334
1335                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1336                 if addNew:
1337                         profile.setActiveMachine(profile.getMachineCount())
1338
1339                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1340                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1341                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1342
1343                 self.machineSelectPage = MachineSelectPage(self)
1344                 self.ultimakerSelectParts = SelectParts(self)
1345                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1346                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1347                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1348                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1349                 self.bedLevelPage = bedLevelWizardMain(self)
1350                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1351                 self.printrbotSelectType = PrintrbotPage(self)
1352                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1353                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1354                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1355
1356                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1357                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1358                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self)
1359                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1360                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self)
1361                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1362                 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1363
1364                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotMiniToolheadPage)
1365                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotReadyPage)
1366                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1367                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1368                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1369                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1370                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1371                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1372                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1373
1374                 self.RunWizard(self.lulzbotMachineSelectPage)
1375                 self.Destroy()
1376
1377         def OnPageChanging(self, e):
1378                 e.GetPage().StoreData()
1379
1380         def OnPageChanged(self, e):
1381                 if e.GetPage().AllowNext():
1382                         self.FindWindowById(wx.ID_FORWARD).Enable()
1383                 else:
1384                         self.FindWindowById(wx.ID_FORWARD).Disable()
1385                 if e.GetPage().AllowBack():
1386                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1387                 else:
1388                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1389
1390         def OnCancel(self, e):
1391                 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1392                 profile.setActiveMachine(self._old_machine_index)
1393                 profile.removeMachine(new_machine_index)
1394
1395 class bedLevelWizardMain(InfoPage):
1396         def __init__(self, parent):
1397                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1398
1399                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1400                 self.AddSeperator()
1401                 self.AddText(_('It will do the following steps'))
1402                 self.AddText(_('* Move the printer head to each corner'))
1403                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1404                 self.AddText(_('* Print a line around the bed to check if it is level'))
1405                 self.AddSeperator()
1406
1407                 self.connectButton = self.AddButton(_('Connect to printer'))
1408                 self.comm = None
1409
1410                 self.infoBox = self.AddInfoBox()
1411                 self.resumeButton = self.AddButton(_('Resume'))
1412                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1413                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1414                 self.resumeButton.Enable(False)
1415
1416                 self.upButton.Enable(False)
1417                 self.downButton.Enable(False)
1418                 self.upButton2.Enable(False)
1419                 self.downButton2.Enable(False)
1420
1421                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1422                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1423                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1424                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1425                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1426                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1427
1428         def OnConnect(self, e = None):
1429                 if self.comm is not None:
1430                         self.comm.close()
1431                         del self.comm
1432                         self.comm = None
1433                         wx.CallAfter(self.OnConnect)
1434                         return
1435                 self.connectButton.Enable(False)
1436                 self.comm = machineCom.MachineCom(callbackObject=self)
1437                 self.infoBox.SetBusy(_('Connecting to machine.'))
1438                 self._wizardState = 0
1439
1440         def OnBedUp(self, e):
1441                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1442                 self.comm.sendCommand('G92 Z10')
1443                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1444                 self.comm.sendCommand('M400')
1445
1446         def OnBedDown(self, e):
1447                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1448                 self.comm.sendCommand('G92 Z10')
1449                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1450                 self.comm.sendCommand('M400')
1451
1452         def OnBedUp2(self, e):
1453                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1454                 self.comm.sendCommand('G92 Z10')
1455                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1456                 self.comm.sendCommand('M400')
1457
1458         def OnBedDown2(self, e):
1459                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1460                 self.comm.sendCommand('G92 Z10')
1461                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1462                 self.comm.sendCommand('M400')
1463
1464         def AllowNext(self):
1465                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1466                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1467                 return True
1468
1469         def OnResume(self, e):
1470                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1471                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1472                 if self._wizardState == -1:
1473                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1474                         wx.CallAfter(self.upButton.Enable, False)
1475                         wx.CallAfter(self.downButton.Enable, False)
1476                         wx.CallAfter(self.upButton2.Enable, False)
1477                         wx.CallAfter(self.downButton2.Enable, False)
1478                         self.comm.sendCommand('M105')
1479                         self.comm.sendCommand('G28')
1480                         self._wizardState = 1
1481                 elif self._wizardState == 2:
1482                         if profile.getMachineSetting('has_heated_bed') == 'True':
1483                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1484                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1485                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1486                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1487                                 self.comm.sendCommand('M400')
1488                                 self._wizardState = 3
1489                         else:
1490                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1491                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1492                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1493                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1494                                 self.comm.sendCommand('M400')
1495                                 self._wizardState = 3
1496                 elif self._wizardState == 4:
1497                         if profile.getMachineSetting('has_heated_bed') == 'True':
1498                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1499                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1500                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1501                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1502                                 self.comm.sendCommand('M400')
1503                                 self._wizardState = 7
1504                         else:
1505                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1506                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1507                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1508                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1509                                 self.comm.sendCommand('M400')
1510                                 self._wizardState = 5
1511                 elif self._wizardState == 6:
1512                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1513                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1514                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1515                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1516                         self.comm.sendCommand('M400')
1517                         self._wizardState = 7
1518                 elif self._wizardState == 8:
1519                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1520                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1521                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1522                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1523                         self._wizardState = 9
1524                 elif self._wizardState == 10:
1525                         self._wizardState = 11
1526                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1527                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1528                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1529                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1530                         w = profile.getMachineSettingFloat('machine_width') - 10
1531                         d = profile.getMachineSettingFloat('machine_depth')
1532                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1533                         filamentArea = math.pi * filamentRadius * filamentRadius
1534                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1535                         eValue = 0.0
1536
1537                         gcodeList = [
1538                                 'G1 Z2 F%d' % (feedZ),
1539                                 'G92 E0',
1540                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1541                                 'G1 Z0.3 F%d' % (feedZ)]
1542                         eValue += 5.0
1543                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1544
1545                         for i in xrange(0, 3):
1546                                 dist = 5.0 + 0.4 * float(i)
1547                                 eValue += (d - 2.0*dist) * ePerMM
1548                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1549                                 eValue += (w - 2.0*dist) * ePerMM
1550                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1551                                 eValue += (d - 2.0*dist) * ePerMM
1552                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1553                                 eValue += (w - 2.0*dist) * ePerMM
1554                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1555
1556                         gcodeList.append('M400')
1557                         self.comm.printGCode(gcodeList)
1558                 self.resumeButton.Enable(False)
1559
1560         def mcLog(self, message):
1561                 print 'Log:', message
1562
1563         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1564                 if self._wizardState == 1:
1565                         self._wizardState = 2
1566                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1567                         wx.CallAfter(self.resumeButton.Enable, True)
1568                 elif self._wizardState == 3:
1569                         self._wizardState = 4
1570                         if profile.getMachineSetting('has_heated_bed') == 'True':
1571                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1572                         else:
1573                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1574                         wx.CallAfter(self.resumeButton.Enable, True)
1575                 elif self._wizardState == 5:
1576                         self._wizardState = 6
1577                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1578                         wx.CallAfter(self.resumeButton.Enable, True)
1579                 elif self._wizardState == 7:
1580                         self._wizardState = 8
1581                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1582                         wx.CallAfter(self.resumeButton.Enable, True)
1583                 elif self._wizardState == 9:
1584                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1585                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1586                         else:
1587                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1588                                 wx.CallAfter(self.resumeButton.Enable, True)
1589                                 self._wizardState = 10
1590
1591         def mcStateChange(self, state):
1592                 if self.comm is None:
1593                         return
1594                 if self.comm.isOperational():
1595                         if self._wizardState == 0:
1596                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1597                                 wx.CallAfter(self.upButton.Enable, True)
1598                                 wx.CallAfter(self.downButton.Enable, True)
1599                                 wx.CallAfter(self.upButton2.Enable, True)
1600                                 wx.CallAfter(self.downButton2.Enable, True)
1601                                 wx.CallAfter(self.resumeButton.Enable, True)
1602                                 self._wizardState = -1
1603                         elif self._wizardState == 11 and not self.comm.isPrinting():
1604                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1605                                 self.comm.sendCommand('G92 E0')
1606                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1607                                 self.comm.sendCommand('M104 S0')
1608                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1609                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1610                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1611                                 wx.CallAfter(self.connectButton.Enable, True)
1612                                 self._wizardState = 12
1613                 elif self.comm.isError():
1614                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1615
1616         def mcMessage(self, message):
1617                 pass
1618
1619         def mcProgress(self, lineNr):
1620                 pass
1621
1622         def mcZChange(self, newZ):
1623                 pass
1624
1625 class headOffsetCalibrationPage(InfoPage):
1626         def __init__(self, parent):
1627                 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1628
1629                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1630                 self.AddSeperator()
1631
1632                 self.connectButton = self.AddButton(_('Connect to printer'))
1633                 self.comm = None
1634
1635                 self.infoBox = self.AddInfoBox()
1636                 self.textEntry = self.AddTextCtrl('')
1637                 self.textEntry.Enable(False)
1638                 self.resumeButton = self.AddButton(_('Resume'))
1639                 self.resumeButton.Enable(False)
1640
1641                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1642                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1643
1644         def AllowBack(self):
1645                 return True
1646
1647         def OnConnect(self, e = None):
1648                 if self.comm is not None:
1649                         self.comm.close()
1650                         del self.comm
1651                         self.comm = None
1652                         wx.CallAfter(self.OnConnect)
1653                         return
1654                 self.connectButton.Enable(False)
1655                 self.comm = machineCom.MachineCom(callbackObject=self)
1656                 self.infoBox.SetBusy(_('Connecting to machine.'))
1657                 self._wizardState = 0
1658
1659         def OnResume(self, e):
1660                 if self._wizardState == 2:
1661                         self._wizardState = 3
1662                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1663
1664                         w = profile.getMachineSettingFloat('machine_width')
1665                         d = profile.getMachineSettingFloat('machine_depth')
1666
1667                         gcode = gcodeGenerator.gcodeGenerator()
1668                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1669                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1670                         gcode.addCmd('T0')
1671                         gcode.addPrime(15)
1672                         gcode.addCmd('T1')
1673                         gcode.addPrime(15)
1674
1675                         gcode.addCmd('T0')
1676                         gcode.addMove(w/2, 5)
1677                         gcode.addMove(z=0.2)
1678                         gcode.addPrime()
1679                         gcode.addExtrude(w/2, d-5.0)
1680                         gcode.addRetract()
1681                         gcode.addMove(5, d/2)
1682                         gcode.addPrime()
1683                         gcode.addExtrude(w-5.0, d/2)
1684                         gcode.addRetract(15)
1685
1686                         gcode.addCmd('T1')
1687                         gcode.addMove(w/2, 5)
1688                         gcode.addPrime()
1689                         gcode.addExtrude(w/2, d-5.0)
1690                         gcode.addRetract()
1691                         gcode.addMove(5, d/2)
1692                         gcode.addPrime()
1693                         gcode.addExtrude(w-5.0, d/2)
1694                         gcode.addRetract(15)
1695                         gcode.addCmd('T0')
1696
1697                         gcode.addMove(z=25)
1698                         gcode.addMove(0, 0)
1699                         gcode.addCmd('M400')
1700
1701                         self.comm.printGCode(gcode.list())
1702                         self.resumeButton.Enable(False)
1703                 elif self._wizardState == 4:
1704                         try:
1705                                 float(self.textEntry.GetValue())
1706                         except ValueError:
1707                                 return
1708                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1709                         self._wizardState = 5
1710                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1711                         self.textEntry.SetValue('0.0')
1712                         self.textEntry.Enable(True)
1713                 elif self._wizardState == 5:
1714                         try:
1715                                 float(self.textEntry.GetValue())
1716                         except ValueError:
1717                                 return
1718                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1719                         self._wizardState = 6
1720                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1721                         self.textEntry.SetValue('')
1722                         self.textEntry.Enable(False)
1723                         self.resumeButton.Enable(False)
1724
1725                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1726                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1727                         gcode = gcodeGenerator.gcodeGenerator()
1728                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1729                         gcode.setPrintSpeed(25)
1730                         gcode.addHome()
1731                         gcode.addCmd('T0')
1732                         gcode.addMove(50, 40, 0.2)
1733                         gcode.addPrime(15)
1734                         for n in xrange(0, 10):
1735                                 gcode.addExtrude(50 + n * 10, 150)
1736                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1737                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1738                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1739                         gcode.addMove(40, 50)
1740                         for n in xrange(0, 10):
1741                                 gcode.addExtrude(150, 50 + n * 10)
1742                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1743                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1744                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1745                         gcode.addRetract(15)
1746
1747                         gcode.addCmd('T1')
1748                         gcode.addMove(50 - x, 30 - y, 0.2)
1749                         gcode.addPrime(15)
1750                         for n in xrange(0, 10):
1751                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1752                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1753                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1754                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1755                         gcode.addMove(30 - x, 50 - y, 0.2)
1756                         for n in xrange(0, 10):
1757                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1758                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1759                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1760                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1761                         gcode.addRetract(15)
1762                         gcode.addMove(z=15)
1763                         gcode.addCmd('M400')
1764                         gcode.addCmd('M104 T0 S0')
1765                         gcode.addCmd('M104 T1 S0')
1766                         self.comm.printGCode(gcode.list())
1767                 elif self._wizardState == 7:
1768                         try:
1769                                 n = int(self.textEntry.GetValue()) - 1
1770                         except:
1771                                 return
1772                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1773                         x += -1.0 + n * 0.1
1774                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1775                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1776                         self.textEntry.SetValue('10')
1777                         self._wizardState = 8
1778                 elif self._wizardState == 8:
1779                         try:
1780                                 n = int(self.textEntry.GetValue()) - 1
1781                         except:
1782                                 return
1783                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1784                         y += -1.0 + n * 0.1
1785                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1786                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1787                         self.infoBox.SetReadyIndicator()
1788                         self._wizardState = 8
1789                         self.comm.close()
1790                         self.resumeButton.Enable(False)
1791
1792         def mcLog(self, message):
1793                 print 'Log:', message
1794
1795         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1796                 if self._wizardState == 1:
1797                         if temp[0] >= 210 and temp[1] >= 210:
1798                                 self._wizardState = 2
1799                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1800                                 wx.CallAfter(self.resumeButton.Enable, True)
1801                                 wx.CallAfter(self.resumeButton.SetFocus)
1802
1803         def mcStateChange(self, state):
1804                 if self.comm is None:
1805                         return
1806                 if self.comm.isOperational():
1807                         if self._wizardState == 0:
1808                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1809                                 self.comm.sendCommand('M105')
1810                                 self.comm.sendCommand('M104 S220 T0')
1811                                 self.comm.sendCommand('M104 S220 T1')
1812                                 self.comm.sendCommand('G28')
1813                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1814                                 self._wizardState = 1
1815                         if not self.comm.isPrinting():
1816                                 if self._wizardState == 3:
1817                                         self._wizardState = 4
1818                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1819                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1820                                         wx.CallAfter(self.textEntry.Enable, True)
1821                                         wx.CallAfter(self.resumeButton.Enable, True)
1822                                         wx.CallAfter(self.resumeButton.SetFocus)
1823                                 elif self._wizardState == 6:
1824                                         self._wizardState = 7
1825                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1826                                         wx.CallAfter(self.textEntry.SetValue, '10')
1827                                         wx.CallAfter(self.textEntry.Enable, True)
1828                                         wx.CallAfter(self.resumeButton.Enable, True)
1829                                         wx.CallAfter(self.resumeButton.SetFocus)
1830
1831                 elif self.comm.isError():
1832                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1833
1834         def mcMessage(self, message):
1835                 pass
1836
1837         def mcProgress(self, lineNr):
1838                 pass
1839
1840         def mcZChange(self, newZ):
1841                 pass
1842
1843 class bedLevelWizard(wx.wizard.Wizard):
1844         def __init__(self):
1845                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1846
1847                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1848                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1849
1850                 self.mainPage = bedLevelWizardMain(self)
1851                 self.headOffsetCalibration = None
1852
1853                 self.RunWizard(self.mainPage)
1854                 self.Destroy()
1855
1856         def OnPageChanging(self, e):
1857                 e.GetPage().StoreData()
1858
1859         def OnPageChanged(self, e):
1860                 if e.GetPage().AllowNext():
1861                         self.FindWindowById(wx.ID_FORWARD).Enable()
1862                 else:
1863                         self.FindWindowById(wx.ID_FORWARD).Disable()
1864                 if e.GetPage().AllowBack():
1865                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1866                 else:
1867                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1868
1869 class headOffsetWizard(wx.wizard.Wizard):
1870         def __init__(self):
1871                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1872
1873                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1874                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1875
1876                 self.mainPage = headOffsetCalibrationPage(self)
1877
1878                 self.RunWizard(self.mainPage)
1879                 self.Destroy()
1880
1881         def OnPageChanging(self, e):
1882                 e.GetPage().StoreData()
1883
1884         def OnPageChanged(self, e):
1885                 if e.GetPage().AllowNext():
1886                         self.FindWindowById(wx.ID_FORWARD).Enable()
1887                 else:
1888                         self.FindWindowById(wx.ID_FORWARD).Disable()
1889                 if e.GetPage().AllowBack():
1890                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1891                 else:
1892                         self.FindWindowById(wx.ID_BACKWARD).Disable()