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