chiark / gitweb /
Cleanup
[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                         elif self.LulzbotTaz5.GetValue():
1147                                 # Nozzle diameter will be set in the toolhead selection page
1148                                 # We set the machine_type here so later pages can differenciate between TAZ 4 and 5
1149                                 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1150                                 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5')
1151                                 profile.putMachineSetting('machine_width', '290')
1152                                 profile.putMachineSetting('machine_depth', '275')
1153                                 profile.putMachineSetting('machine_height', '250')
1154                                 profile.putMachineSetting('serial_baud', '115200')
1155                         else:
1156                                 # Nozzle diameter and machine type will be set in the toolhead selection page
1157                                 profile.putMachineSetting('machine_name', 'LulzBot Mini')
1158                                 profile.putMachineSetting('machine_width', '155')
1159                                 profile.putMachineSetting('machine_depth', '155')
1160                                 profile.putMachineSetting('machine_height', '163')
1161                                 profile.putMachineSetting('serial_baud', '115200')
1162                                 profile.putMachineSetting('extruder_head_size_min_x', '40')
1163                                 profile.putMachineSetting('extruder_head_size_max_x', '75')
1164                                 profile.putMachineSetting('extruder_head_size_min_y', '25')
1165                                 profile.putMachineSetting('extruder_head_size_max_y', '55')
1166                                 profile.putMachineSetting('extruder_head_size_height', '17')
1167
1168                         profile.putMachineSetting('machine_center_is_zero', 'False')
1169                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
1170                         profile.putMachineSetting('has_heated_bed', 'True')
1171                         profile.putMachineSetting('extruder_head_size_min_x', '0.0')
1172                         profile.putMachineSetting('extruder_head_size_min_y', '0.0')
1173                         profile.putMachineSetting('extruder_head_size_max_x', '0.0')
1174                         profile.putMachineSetting('extruder_head_size_max_y', '0.0')
1175                         profile.putMachineSetting('extruder_head_size_height', '0.0')
1176                         profile.putProfileSetting('retraction_enable', 'True')
1177                         profile.putPreference('startMode', 'Simple')
1178                         profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
1179                         profile.checkAndUpdateMachineName()
1180
1181 class LulzbotReadyPage(InfoPage):
1182         def __init__(self, parent):
1183                 super(LulzbotReadyPage, self).__init__(parent, _("LulzBot TAZ/Mini"))
1184                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1185                 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
1186                 self.AddSeperator()
1187                 self.AddText(_('For more information about using Cura with your LulzBot'))
1188                 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
1189                 self.AddSeperator()
1190
1191 class LulzbotToolheadSelectPage(InfoPage):
1192         def __init__(self, parent, title):
1193                 super(LulzbotToolheadSelectPage, self).__init__(parent, title)
1194                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1195                 self.AddText(_('Picking the correct Tool Head is important.'))
1196                 txt = self.AddText(_('WARNING: Printing with the incorrect firmware can damage your LulzBot printer!'))
1197                 txt.SetForegroundColour(wx.RED)
1198                 self.AddSeperator()
1199
1200 class LulzbotMiniToolheadSelectPage(LulzbotToolheadSelectPage):
1201         def __init__(self, parent, allowBack = True):
1202                 super(LulzbotMiniToolheadSelectPage, self).__init__(parent, _("LulzBot Mini Tool Head Selection"))
1203
1204                 self.allowBack = allowBack
1205                 self.panel = self.AddPanel()
1206                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1207                 self.standard = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v2'),
1208                                                                                         'Lulzbot_Toolhead_Mini_Standard.jpg', image_size,
1209                                                                                         style=ImageButton.IB_GROUP)
1210                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v2'),
1211                                                                                         'Lulzbot_Toolhead_Mini_Flexystruder.jpg', image_size)
1212                 self.standard.SetValue(True)
1213
1214         def AllowBack(self):
1215                 return self.allowBack
1216
1217         def StoreData(self):
1218                 if self.standard.GetValue():
1219                         profile.putProfileSetting('nozzle_size', '0.5')
1220                         profile.putMachineSetting('extruder_amount', '1')
1221                         profile.putMachineSetting('toolhead', 'Single Extruder V2')
1222                         profile.putMachineSetting('toolhead_shortname', '')
1223                         profile.putMachineSetting('machine_type', 'lulzbot_mini')
1224                 else:
1225                         profile.putProfileSetting('nozzle_size', '0.6')
1226                         profile.putMachineSetting('extruder_amount', '1')
1227                         profile.putMachineSetting('toolhead', 'Flexystruder V2')
1228                         profile.putMachineSetting('toolhead_shortname', 'Flexystruder')
1229                         profile.putMachineSetting('machine_type', 'lulzbot_mini_flexystruder')
1230
1231
1232 class LulzbotTazToolheadSelectPage(LulzbotToolheadSelectPage):
1233         def __init__(self, parent):
1234                 super(LulzbotTazToolheadSelectPage, self).__init__(parent, _("LulzBot TAZ Tool Head Selection"))
1235
1236                 self.panel = self.AddPanel()
1237                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1238                 self.single = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v1'),
1239                                                                                         'Lulzbot_Toolhead_TAZ_Single_v1.jpg', image_size,
1240                                                                                         style=ImageButton.IB_GROUP)
1241                 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v1'),
1242                                                                                         'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg', image_size)
1243                 self.dually = self.AddImageButton(self.panel, 1, 0, _('Dual Extruder v1'),
1244                                                                                         'Lulzbot_Toolhead_TAZ_Dual_Extruder_v1.jpg', image_size)
1245                 self.flexydually = self.AddImageButton(self.panel, 1, 1, _('FlexyDually v1'),
1246                                                                                         'Lulzbot_Toolhead_TAZ_FlexyDually_v1.jpg', image_size)
1247                 self.SetVersion(1)
1248                 self.single.SetValue(True)
1249
1250         def SetVersion(self, version):
1251                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1252                 self.single.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Single_v%d.jpg' % version, image_size))
1253                 self.single.SetLabel(_('Single Extruder v%d' % version))
1254                 self.flexy.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Flexystruder_v%d.jpg' % version, image_size))
1255                 self.flexy.SetLabel(_('Flexystruder v%d' % version))
1256                 self.dually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Dual_Extruder_v%d.jpg' % version, image_size))
1257                 self.dually.SetLabel(_('Dual Extruder v%d' % version))
1258                 self.flexydually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_FlexyDually_v%d.jpg' % version, image_size))
1259                 self.flexydually.SetLabel(_('FlexyDually v%d' % version))
1260                 self.version = version
1261                 if version == 1:
1262                         self.single.OnSelected(None)
1263                         self.flexy.OnSelected(None)
1264                         self.dually.OnSelected(None)
1265                         self.flexydually.OnSelected(None)
1266                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1267                 elif version == 2:
1268                         self.single.OnSelected(self.OnSingleV2)
1269                         self.flexy.OnSelected(self.OnNonSingle)
1270                         self.dually.OnSelected(self.OnNonSingle)
1271                         self.flexydually.OnSelected(self.OnNonSingle)
1272                         if self.single.GetValue():
1273                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1274                                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage, self.GetParent().lulzbotFirmwarePage)
1275                         else:
1276                                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1277
1278         def OnSingleV2(self):
1279                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1280                 wx.wizard.WizardPageSimple.Chain(self.GetParent().lulzbotTaz5NozzleSelectPage, self.GetParent().lulzbotFirmwarePage)
1281
1282         def OnNonSingle(self):
1283                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotFirmwarePage)
1284
1285         def StoreData(self):
1286                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1287                         taz_version = 4
1288                 else:
1289                         taz_version = 5
1290                 version = (taz_version, self.version)
1291                 if self.single.GetValue():
1292                         profile.putProfileSetting('nozzle_size', '0.5' if self.version == 2 else '0.35')
1293                         profile.putMachineSetting('extruder_amount', '1')
1294                         profile.putMachineSetting('toolhead', 'Single Extruder V%d' % self.version)
1295                         profile.putMachineSetting('toolhead_shortname', '')
1296                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_SingleV%d' % version)
1297                 elif self.flexy.GetValue():
1298                         profile.putProfileSetting('nozzle_size', '0.6')
1299                         profile.putMachineSetting('extruder_amount', '1')
1300                         profile.putMachineSetting('toolhead', 'Flexystruder V%d' % self.version)
1301                         profile.putMachineSetting('toolhead_shortname', 'Flexystruder v%d' % self.version)
1302                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexystruderV%d' % version)
1303                 elif self.dually.GetValue():
1304                         profile.putProfileSetting('nozzle_size', '0.5')
1305                         profile.putMachineSetting('extruder_amount', '2')
1306                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1307                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1308                         profile.putMachineSetting('toolhead', 'Dual Extruder V%d' % self.version)
1309                         profile.putMachineSetting('toolhead_shortname', 'Dually v%d' % self.version)
1310                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_DuallyV%d' % version)
1311                 elif self.flexydually.GetValue():
1312                         profile.putProfileSetting('nozzle_size', '0.6')
1313                         profile.putMachineSetting('extruder_amount', '2')
1314                         profile.putMachineSetting('extruder_offset_x1', '0.0')
1315                         profile.putMachineSetting('extruder_offset_y1', '-50.0' if self.version == 2 else '-52.00')
1316                         profile.putMachineSetting('toolhead', 'FlexyDually V%d' % self.version)
1317                         profile.putMachineSetting('toolhead_shortname', 'FlexyDually v%d' % self.version)
1318                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_FlexyDuallyV%d' % version)
1319
1320
1321 class LulzbotHotendSelectPage(LulzbotToolheadSelectPage):
1322         def __init__(self, parent, allowBack = True):
1323                 super(LulzbotHotendSelectPage, self).__init__(parent, _("LulzBot Tool Head Hotend Selection"))
1324
1325                 self.allowBack = allowBack
1326                 self.panel = self.AddPanel()
1327                 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1328                 self.v1 = self.AddImageButton(self.panel, 0, 0, _('v1 (Budaschnozzle)'),
1329                                                                                         'Lulzbot_Toolhead_v1.jpg', image_size,
1330                                                                                         style=ImageButton.IB_GROUP)
1331                 self.v2 = self.AddImageButton(self.panel, 0, 1, _('v2 (Hexagon)'),
1332                                                                                         'Lulzbot_Toolhead_v2.jpg', image_size)
1333                 self.v1.SetValue(True)
1334
1335         def AllowBack(self):
1336                 return self.allowBack
1337
1338         def StoreData(self):
1339                 self.GetParent().lulzbotTazToolheadPage.SetVersion(1 if self.v1.GetValue() else 2)
1340
1341 class LulzbotTaz5NozzleSelectPage(LulzbotToolheadSelectPage):
1342         url2='http://lulzbot.com/printer-identification'
1343
1344         def __init__(self, parent):
1345                 super(LulzbotTaz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ Single V2 Nozzle Selection"))
1346
1347                 self.AddText(_('Please select your Hexagon hotend\'s nozzle diameter:'))
1348                 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1349                 self.Nozzle35Radio.SetValue(True)
1350                 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1351                 self.AddText(_(' '))
1352                 self.AddSeperator()
1353
1354                 self.AddText(_('If you are not sure which nozzle diameter you have'))
1355                 self.AddText(_('please check this webpage: '))
1356                 button = self.AddButton(LulzbotTaz5NozzleSelectPage.url2)
1357                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1358
1359         def OnUrlClick(self, e):
1360                 webbrowser.open(LulzbotTaz5NozzleSelectPage.url2)
1361
1362         def StoreData(self):
1363                 if profile.getMachineSetting('machine_type').startswith('lulzbot_TAZ_4'):
1364                         taz_version = 4
1365                 else:
1366                         taz_version = 5
1367                 if self.Nozzle35Radio.GetValue():
1368                         profile.putProfileSetting('nozzle_size', '0.35')
1369                         profile.putMachineSetting('toolhead', 'Single Extruder V2 (0.35mm nozzle)')
1370                         profile.putMachineSetting('toolhead_shortname', '0.35 nozzle')
1371                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_035nozzle' % taz_version)
1372
1373                 else:
1374                         profile.putProfileSetting('nozzle_size', '0.5')
1375                         profile.putMachineSetting('toolhead', 'Single Extruder V2 (0.5mm nozzle)')
1376                         profile.putMachineSetting('toolhead_shortname', '0.5 nozzle')
1377                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_%d_05nozzle' % taz_version)
1378
1379 class LulzbotFirmwareUpdatePage(InfoPage):
1380         def __init__(self, parent):
1381                 super(LulzbotFirmwareUpdatePage, self).__init__(parent, _("LulzBot Firmware Update"))
1382
1383                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1384
1385                 self.AddText(_('Your Lulzbot printer\'s firmware will now be updated'))
1386                 self.AddSeperator()
1387                 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."))
1388                 self.AddHiddenSeperator()
1389                 self.AddText(_("The firmware shipping with new Lulzbot printers works, but upgrades may be available\nwhich improve the functionality of your printer.\nIf you changed your tool head, you will also need to flash\n new firmware or you risk damaging your printer."))
1390                 self.AddHiddenSeperator()
1391                 self.AddText(_("To avoid any possible confusion, make sure to disconnect all printers from your PC\nexpect the printer that you are currently configuring."))
1392                 self.AddHiddenSeperator()
1393                 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Flash the firmware'), _('Skip upgrade'))
1394                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
1395                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
1396
1397         def AllowNext(self):
1398                 return version.isDevVersion()
1399
1400         def OnUpgradeClick(self, e):
1401                 if firmwareInstall.InstallFirmware():
1402                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1403
1404         def OnSkipClick(self, e):
1405                 dlg = wx.MessageDialog(self, _("Are you sure you want to skip the firmware upgrade?\n" +
1406                                                                            "It could damage your printer if you changed your tool head and have not updated thefirmware!"),
1407                                                         _('Skip firmware upgrade?'), wx.YES_NO | wx.ICON_EXCLAMATION)
1408                 skip = dlg.ShowModal() == wx.ID_YES
1409                 dlg.Destroy()
1410                 if skip:
1411                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
1412                         self.GetParent().ShowPage(self.GetNext())
1413
1414 class LulzbotChangeToolheadWizard(wx.wizard.Wizard):
1415         def __init__(self):
1416                 super(LulzbotChangeToolheadWizard, self).__init__(None, -1, _("Change Lulzbot Tool Head Wizard"))
1417
1418                 self._nozzle_size = profile.getProfileSettingFloat('nozzle_size')
1419                 self._machine_name = profile.getMachineSetting('machine_name')
1420                 self._machine_type = profile.getMachineSetting('machine_type')
1421                 self._extruder_amount = int(profile.getMachineSettingFloat('extruder_amount'))
1422
1423                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1424                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1425                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1426
1427                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1428                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1429                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self, False)
1430                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1431                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self, False)
1432                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1433
1434                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotFirmwarePage)
1435                 wx.wizard.WizardPageSimple.Chain(self.lulzbotFirmwarePage, self.lulzbotReadyPage)
1436                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1437
1438                 if profile.getMachineSetting('machine_type').startswith('lulzbot_mini'):
1439                         self.RunWizard(self.lulzbotMiniToolheadPage)
1440                 else:
1441                         self.RunWizard(self.lulzbotTazHotendPage)
1442                 self.Destroy()
1443
1444         def OnPageChanging(self, e):
1445                 e.GetPage().StoreData()
1446
1447         def OnPageChanged(self, e):
1448                 if e.GetPage().AllowNext():
1449                         self.FindWindowById(wx.ID_FORWARD).Enable()
1450                 else:
1451                         self.FindWindowById(wx.ID_FORWARD).Disable()
1452                 if e.GetPage().AllowBack():
1453                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1454                 else:
1455                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1456
1457         def OnCancel(self, e):
1458                 profile.putProfileSetting('nozzle_size', self._nozzle_size)
1459                 profile.putMachineSetting('machine_name', self._machine_name)
1460                 profile.putMachineSetting('machine_type', self._machine_type)
1461                 profile.putMachineSetting('extruder_amount', self._extruder_amount)
1462
1463 class ConfigWizard(wx.wizard.Wizard):
1464         def __init__(self, addNew = False):
1465                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1466
1467                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1468                 if addNew:
1469                         profile.setActiveMachine(profile.getMachineCount())
1470
1471                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1472                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1473                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1474
1475                 self.machineSelectPage = MachineSelectPage(self)
1476                 self.ultimakerSelectParts = SelectParts(self)
1477                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1478                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1479                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1480                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1481                 self.bedLevelPage = bedLevelWizardMain(self)
1482                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1483                 self.printrbotSelectType = PrintrbotPage(self)
1484                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1485                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1486                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1487
1488                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1489                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1490                 self.lulzbotFirmwarePage = LulzbotFirmwareUpdatePage(self)
1491                 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self)
1492                 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1493                 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self)
1494                 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1495                 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1496
1497                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotMiniToolheadPage)
1498                 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotFirmwarePage)
1499                 wx.wizard.WizardPageSimple.Chain(self.lulzbotFirmwarePage, self.lulzbotReadyPage)
1500                 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1501                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1502                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1503                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1504                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1505                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1506                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1507
1508                 self.RunWizard(self.lulzbotMachineSelectPage)
1509                 self.Destroy()
1510
1511         def OnPageChanging(self, e):
1512                 e.GetPage().StoreData()
1513
1514         def OnPageChanged(self, e):
1515                 if e.GetPage().AllowNext():
1516                         self.FindWindowById(wx.ID_FORWARD).Enable()
1517                 else:
1518                         self.FindWindowById(wx.ID_FORWARD).Disable()
1519                 if e.GetPage().AllowBack():
1520                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1521                 else:
1522                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1523
1524         def OnCancel(self, e):
1525                 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1526                 profile.setActiveMachine(self._old_machine_index)
1527                 profile.removeMachine(new_machine_index)
1528
1529 class bedLevelWizardMain(InfoPage):
1530         def __init__(self, parent):
1531                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1532
1533                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1534                 self.AddSeperator()
1535                 self.AddText(_('It will do the following steps'))
1536                 self.AddText(_('* Move the printer head to each corner'))
1537                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1538                 self.AddText(_('* Print a line around the bed to check if it is level'))
1539                 self.AddSeperator()
1540
1541                 self.connectButton = self.AddButton(_('Connect to printer'))
1542                 self.comm = None
1543
1544                 self.infoBox = self.AddInfoBox()
1545                 self.resumeButton = self.AddButton(_('Resume'))
1546                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1547                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1548                 self.resumeButton.Enable(False)
1549
1550                 self.upButton.Enable(False)
1551                 self.downButton.Enable(False)
1552                 self.upButton2.Enable(False)
1553                 self.downButton2.Enable(False)
1554
1555                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1556                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1557                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1558                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1559                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1560                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1561
1562         def OnConnect(self, e = None):
1563                 if self.comm is not None:
1564                         self.comm.close()
1565                         del self.comm
1566                         self.comm = None
1567                         wx.CallAfter(self.OnConnect)
1568                         return
1569                 self.connectButton.Enable(False)
1570                 self.comm = machineCom.MachineCom(callbackObject=self)
1571                 self.infoBox.SetBusy(_('Connecting to machine.'))
1572                 self._wizardState = 0
1573
1574         def OnBedUp(self, e):
1575                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1576                 self.comm.sendCommand('G92 Z10')
1577                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1578                 self.comm.sendCommand('M400')
1579
1580         def OnBedDown(self, e):
1581                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1582                 self.comm.sendCommand('G92 Z10')
1583                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1584                 self.comm.sendCommand('M400')
1585
1586         def OnBedUp2(self, e):
1587                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1588                 self.comm.sendCommand('G92 Z10')
1589                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1590                 self.comm.sendCommand('M400')
1591
1592         def OnBedDown2(self, e):
1593                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1594                 self.comm.sendCommand('G92 Z10')
1595                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1596                 self.comm.sendCommand('M400')
1597
1598         def AllowNext(self):
1599                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1600                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1601                 return True
1602
1603         def OnResume(self, e):
1604                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1605                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1606                 if self._wizardState == -1:
1607                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1608                         wx.CallAfter(self.upButton.Enable, False)
1609                         wx.CallAfter(self.downButton.Enable, False)
1610                         wx.CallAfter(self.upButton2.Enable, False)
1611                         wx.CallAfter(self.downButton2.Enable, False)
1612                         self.comm.sendCommand('M105')
1613                         self.comm.sendCommand('G28')
1614                         self._wizardState = 1
1615                 elif self._wizardState == 2:
1616                         if profile.getMachineSetting('has_heated_bed') == 'True':
1617                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1618                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1619                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1620                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1621                                 self.comm.sendCommand('M400')
1622                                 self._wizardState = 3
1623                         else:
1624                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1625                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1626                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1627                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1628                                 self.comm.sendCommand('M400')
1629                                 self._wizardState = 3
1630                 elif self._wizardState == 4:
1631                         if profile.getMachineSetting('has_heated_bed') == 'True':
1632                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1633                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1634                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1635                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1636                                 self.comm.sendCommand('M400')
1637                                 self._wizardState = 7
1638                         else:
1639                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1640                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1641                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1642                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1643                                 self.comm.sendCommand('M400')
1644                                 self._wizardState = 5
1645                 elif self._wizardState == 6:
1646                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1647                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1648                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1649                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1650                         self.comm.sendCommand('M400')
1651                         self._wizardState = 7
1652                 elif self._wizardState == 8:
1653                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1654                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1655                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1656                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1657                         self._wizardState = 9
1658                 elif self._wizardState == 10:
1659                         self._wizardState = 11
1660                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1661                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1662                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1663                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1664                         w = profile.getMachineSettingFloat('machine_width') - 10
1665                         d = profile.getMachineSettingFloat('machine_depth')
1666                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1667                         filamentArea = math.pi * filamentRadius * filamentRadius
1668                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1669                         eValue = 0.0
1670
1671                         gcodeList = [
1672                                 'G1 Z2 F%d' % (feedZ),
1673                                 'G92 E0',
1674                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1675                                 'G1 Z0.3 F%d' % (feedZ)]
1676                         eValue += 5.0
1677                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1678
1679                         for i in xrange(0, 3):
1680                                 dist = 5.0 + 0.4 * float(i)
1681                                 eValue += (d - 2.0*dist) * ePerMM
1682                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1683                                 eValue += (w - 2.0*dist) * ePerMM
1684                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1685                                 eValue += (d - 2.0*dist) * ePerMM
1686                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1687                                 eValue += (w - 2.0*dist) * ePerMM
1688                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1689
1690                         gcodeList.append('M400')
1691                         self.comm.printGCode(gcodeList)
1692                 self.resumeButton.Enable(False)
1693
1694         def mcLog(self, message):
1695                 print 'Log:', message
1696
1697         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1698                 if self._wizardState == 1:
1699                         self._wizardState = 2
1700                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1701                         wx.CallAfter(self.resumeButton.Enable, True)
1702                 elif self._wizardState == 3:
1703                         self._wizardState = 4
1704                         if profile.getMachineSetting('has_heated_bed') == 'True':
1705                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1706                         else:
1707                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1708                         wx.CallAfter(self.resumeButton.Enable, True)
1709                 elif self._wizardState == 5:
1710                         self._wizardState = 6
1711                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1712                         wx.CallAfter(self.resumeButton.Enable, True)
1713                 elif self._wizardState == 7:
1714                         self._wizardState = 8
1715                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1716                         wx.CallAfter(self.resumeButton.Enable, True)
1717                 elif self._wizardState == 9:
1718                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1719                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1720                         else:
1721                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1722                                 wx.CallAfter(self.resumeButton.Enable, True)
1723                                 self._wizardState = 10
1724
1725         def mcStateChange(self, state):
1726                 if self.comm is None:
1727                         return
1728                 if self.comm.isOperational():
1729                         if self._wizardState == 0:
1730                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1731                                 wx.CallAfter(self.upButton.Enable, True)
1732                                 wx.CallAfter(self.downButton.Enable, True)
1733                                 wx.CallAfter(self.upButton2.Enable, True)
1734                                 wx.CallAfter(self.downButton2.Enable, True)
1735                                 wx.CallAfter(self.resumeButton.Enable, True)
1736                                 self._wizardState = -1
1737                         elif self._wizardState == 11 and not self.comm.isPrinting():
1738                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1739                                 self.comm.sendCommand('G92 E0')
1740                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1741                                 self.comm.sendCommand('M104 S0')
1742                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1743                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1744                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1745                                 wx.CallAfter(self.connectButton.Enable, True)
1746                                 self._wizardState = 12
1747                 elif self.comm.isError():
1748                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1749
1750         def mcMessage(self, message):
1751                 pass
1752
1753         def mcProgress(self, lineNr):
1754                 pass
1755
1756         def mcZChange(self, newZ):
1757                 pass
1758
1759 class headOffsetCalibrationPage(InfoPage):
1760         def __init__(self, parent):
1761                 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1762
1763                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1764                 self.AddSeperator()
1765
1766                 self.connectButton = self.AddButton(_('Connect to printer'))
1767                 self.comm = None
1768
1769                 self.infoBox = self.AddInfoBox()
1770                 self.textEntry = self.AddTextCtrl('')
1771                 self.textEntry.Enable(False)
1772                 self.resumeButton = self.AddButton(_('Resume'))
1773                 self.resumeButton.Enable(False)
1774
1775                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1776                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1777
1778         def AllowBack(self):
1779                 return True
1780
1781         def OnConnect(self, e = None):
1782                 if self.comm is not None:
1783                         self.comm.close()
1784                         del self.comm
1785                         self.comm = None
1786                         wx.CallAfter(self.OnConnect)
1787                         return
1788                 self.connectButton.Enable(False)
1789                 self.comm = machineCom.MachineCom(callbackObject=self)
1790                 self.infoBox.SetBusy(_('Connecting to machine.'))
1791                 self._wizardState = 0
1792
1793         def OnResume(self, e):
1794                 if self._wizardState == 2:
1795                         self._wizardState = 3
1796                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1797
1798                         w = profile.getMachineSettingFloat('machine_width')
1799                         d = profile.getMachineSettingFloat('machine_depth')
1800
1801                         gcode = gcodeGenerator.gcodeGenerator()
1802                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1803                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1804                         gcode.addCmd('T0')
1805                         gcode.addPrime(15)
1806                         gcode.addCmd('T1')
1807                         gcode.addPrime(15)
1808
1809                         gcode.addCmd('T0')
1810                         gcode.addMove(w/2, 5)
1811                         gcode.addMove(z=0.2)
1812                         gcode.addPrime()
1813                         gcode.addExtrude(w/2, d-5.0)
1814                         gcode.addRetract()
1815                         gcode.addMove(5, d/2)
1816                         gcode.addPrime()
1817                         gcode.addExtrude(w-5.0, d/2)
1818                         gcode.addRetract(15)
1819
1820                         gcode.addCmd('T1')
1821                         gcode.addMove(w/2, 5)
1822                         gcode.addPrime()
1823                         gcode.addExtrude(w/2, d-5.0)
1824                         gcode.addRetract()
1825                         gcode.addMove(5, d/2)
1826                         gcode.addPrime()
1827                         gcode.addExtrude(w-5.0, d/2)
1828                         gcode.addRetract(15)
1829                         gcode.addCmd('T0')
1830
1831                         gcode.addMove(z=25)
1832                         gcode.addMove(0, 0)
1833                         gcode.addCmd('M400')
1834
1835                         self.comm.printGCode(gcode.list())
1836                         self.resumeButton.Enable(False)
1837                 elif self._wizardState == 4:
1838                         try:
1839                                 float(self.textEntry.GetValue())
1840                         except ValueError:
1841                                 return
1842                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1843                         self._wizardState = 5
1844                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1845                         self.textEntry.SetValue('0.0')
1846                         self.textEntry.Enable(True)
1847                 elif self._wizardState == 5:
1848                         try:
1849                                 float(self.textEntry.GetValue())
1850                         except ValueError:
1851                                 return
1852                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1853                         self._wizardState = 6
1854                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1855                         self.textEntry.SetValue('')
1856                         self.textEntry.Enable(False)
1857                         self.resumeButton.Enable(False)
1858
1859                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1860                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1861                         gcode = gcodeGenerator.gcodeGenerator()
1862                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1863                         gcode.setPrintSpeed(25)
1864                         gcode.addHome()
1865                         gcode.addCmd('T0')
1866                         gcode.addMove(50, 40, 0.2)
1867                         gcode.addPrime(15)
1868                         for n in xrange(0, 10):
1869                                 gcode.addExtrude(50 + n * 10, 150)
1870                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1871                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1872                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1873                         gcode.addMove(40, 50)
1874                         for n in xrange(0, 10):
1875                                 gcode.addExtrude(150, 50 + n * 10)
1876                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1877                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1878                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1879                         gcode.addRetract(15)
1880
1881                         gcode.addCmd('T1')
1882                         gcode.addMove(50 - x, 30 - y, 0.2)
1883                         gcode.addPrime(15)
1884                         for n in xrange(0, 10):
1885                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1886                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1887                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1888                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1889                         gcode.addMove(30 - x, 50 - y, 0.2)
1890                         for n in xrange(0, 10):
1891                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1892                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1893                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1894                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1895                         gcode.addRetract(15)
1896                         gcode.addMove(z=15)
1897                         gcode.addCmd('M400')
1898                         gcode.addCmd('M104 T0 S0')
1899                         gcode.addCmd('M104 T1 S0')
1900                         self.comm.printGCode(gcode.list())
1901                 elif self._wizardState == 7:
1902                         try:
1903                                 n = int(self.textEntry.GetValue()) - 1
1904                         except:
1905                                 return
1906                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1907                         x += -1.0 + n * 0.1
1908                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1909                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1910                         self.textEntry.SetValue('10')
1911                         self._wizardState = 8
1912                 elif self._wizardState == 8:
1913                         try:
1914                                 n = int(self.textEntry.GetValue()) - 1
1915                         except:
1916                                 return
1917                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1918                         y += -1.0 + n * 0.1
1919                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1920                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1921                         self.infoBox.SetReadyIndicator()
1922                         self._wizardState = 8
1923                         self.comm.close()
1924                         self.resumeButton.Enable(False)
1925
1926         def mcLog(self, message):
1927                 print 'Log:', message
1928
1929         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1930                 if self._wizardState == 1:
1931                         if temp[0] >= 210 and temp[1] >= 210:
1932                                 self._wizardState = 2
1933                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1934                                 wx.CallAfter(self.resumeButton.Enable, True)
1935                                 wx.CallAfter(self.resumeButton.SetFocus)
1936
1937         def mcStateChange(self, state):
1938                 if self.comm is None:
1939                         return
1940                 if self.comm.isOperational():
1941                         if self._wizardState == 0:
1942                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1943                                 self.comm.sendCommand('M105')
1944                                 self.comm.sendCommand('M104 S220 T0')
1945                                 self.comm.sendCommand('M104 S220 T1')
1946                                 self.comm.sendCommand('G28')
1947                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1948                                 self._wizardState = 1
1949                         if not self.comm.isPrinting():
1950                                 if self._wizardState == 3:
1951                                         self._wizardState = 4
1952                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1953                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1954                                         wx.CallAfter(self.textEntry.Enable, True)
1955                                         wx.CallAfter(self.resumeButton.Enable, True)
1956                                         wx.CallAfter(self.resumeButton.SetFocus)
1957                                 elif self._wizardState == 6:
1958                                         self._wizardState = 7
1959                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1960                                         wx.CallAfter(self.textEntry.SetValue, '10')
1961                                         wx.CallAfter(self.textEntry.Enable, True)
1962                                         wx.CallAfter(self.resumeButton.Enable, True)
1963                                         wx.CallAfter(self.resumeButton.SetFocus)
1964
1965                 elif self.comm.isError():
1966                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1967
1968         def mcMessage(self, message):
1969                 pass
1970
1971         def mcProgress(self, lineNr):
1972                 pass
1973
1974         def mcZChange(self, newZ):
1975                 pass
1976
1977 class bedLevelWizard(wx.wizard.Wizard):
1978         def __init__(self):
1979                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1980
1981                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1982                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1983
1984                 self.mainPage = bedLevelWizardMain(self)
1985                 self.headOffsetCalibration = None
1986
1987                 self.RunWizard(self.mainPage)
1988                 self.Destroy()
1989
1990         def OnPageChanging(self, e):
1991                 e.GetPage().StoreData()
1992
1993         def OnPageChanged(self, e):
1994                 if e.GetPage().AllowNext():
1995                         self.FindWindowById(wx.ID_FORWARD).Enable()
1996                 else:
1997                         self.FindWindowById(wx.ID_FORWARD).Disable()
1998                 if e.GetPage().AllowBack():
1999                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2000                 else:
2001                         self.FindWindowById(wx.ID_BACKWARD).Disable()
2002
2003 class headOffsetWizard(wx.wizard.Wizard):
2004         def __init__(self):
2005                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
2006
2007                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
2008                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
2009
2010                 self.mainPage = headOffsetCalibrationPage(self)
2011
2012                 self.RunWizard(self.mainPage)
2013                 self.Destroy()
2014
2015         def OnPageChanging(self, e):
2016                 e.GetPage().StoreData()
2017
2018         def OnPageChanged(self, e):
2019                 if e.GetPage().AllowNext():
2020                         self.FindWindowById(wx.ID_FORWARD).Enable()
2021                 else:
2022                         self.FindWindowById(wx.ID_FORWARD).Disable()
2023                 if e.GetPage().AllowBack():
2024                         self.FindWindowById(wx.ID_BACKWARD).Enable()
2025                 else:
2026                         self.FindWindowById(wx.ID_BACKWARD).Disable()