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