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