chiark / gitweb /
Lulzbot is using a .35 nozzle
[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
228 class FirstInfoPage(InfoPage):
229         def __init__(self, parent, addNew):
230                 if addNew:
231                         super(FirstInfoPage, self).__init__(parent, _("Add new machine wizard"))
232                 else:
233                         super(FirstInfoPage, self).__init__(parent, _("First time run wizard"))
234                         self.AddText(_("Welcome, and thanks for trying Cura!"))
235                         self.AddSeperator()
236                 self.AddText(_("This wizard will help you in setting up Cura for your machine."))
237                 if not addNew:
238                         self.AddSeperator()
239                         self._language_option = self.AddCombo(_("Select your language:"), map(lambda o: o[1], resources.getLanguageOptions()))
240                 else:
241                         self._language_option = None
242                 # self.AddText(_("This wizard will help you with the following steps:"))
243                 # self.AddText(_("* Configure Cura for your machine"))
244                 # self.AddText(_("* Optionally upgrade your firmware"))
245                 # self.AddText(_("* Optionally check if your machine is working safely"))
246                 # self.AddText(_("* Optionally level your printer bed"))
247
248                 #self.AddText('* Calibrate your machine')
249                 #self.AddText('* Do your first print')
250
251         def AllowBack(self):
252                 return False
253
254         def StoreData(self):
255                 if self._language_option is not None:
256                         profile.putPreference('language', self._language_option.GetValue())
257                         resources.setupLocalization(self._language_option.GetValue())
258
259 class PrintrbotPage(InfoPage):
260         def __init__(self, parent):
261                 self._printer_info = {
262                         # X, Y, Z, Filament Diameter, PrintTemperature, Print Speed, Travel Speed, Retract speed, Retract amount
263                         "Original": (130, 130, 130, 2.95, 208, 40, 70, 30, 1),
264                         "Simple Maker's Edition v1": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
265                         "Simple Maker's Edition v2 (2013 Printrbot Simple)": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
266                         "Simple Maker's Edition v3 (2014 Printrbot Simple)": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
267                         "Simple Maker's Edition v4 (Model 1405)": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
268                         "Simple Metal": (150, 150, 150, 1.75, 208, 40, 70, 30, 1),
269                         "Jr v1": (150, 100, 80, 1.75, 208, 40, 70, 30, 1),
270                         "Jr v2": (150, 150, 150, 1.75, 208, 40, 70, 30, 1),
271                         "LC v2": (150, 150, 150, 1.75, 208, 40, 70, 30, 1),
272                         "Plus v2": (200, 200, 200, 1.75, 208, 40, 70, 30, 1),
273                         "Plus v2.1": (200, 200, 200, 1.75, 208, 40, 70, 30, 1),
274                         "Plus v2.2 (Model 1404/140422)": (250, 250, 250, 1.75, 208, 40, 70, 30, 1),
275                         "Plus v2.3 (Model 140501)": (250, 250, 250, 1.75, 208, 40, 70, 30, 1),
276                         "Plus v2.4 (Model 140507)": (250, 250, 250, 1.75, 208, 40, 70, 30, 1),
277                 }
278
279                 super(PrintrbotPage, self).__init__(parent, "Printrbot Selection")
280                 self.AddText(_("Select which Printrbot machine you have:"))
281                 keys = self._printer_info.keys()
282                 keys.sort()
283                 self._items = []
284                 for name in keys:
285                         item = self.AddRadioButton(name)
286                         item.data = self._printer_info[name]
287                         self._items.append(item)
288
289         def StoreData(self):
290                 profile.putMachineSetting('machine_name', 'Printrbot ???')
291                 for item in self._items:
292                         if item.GetValue():
293                                 data = item.data
294                                 profile.putMachineSetting('machine_name', 'Printrbot ' + item.GetLabel())
295                                 profile.putMachineSetting('machine_width', data[0])
296                                 profile.putMachineSetting('machine_depth', data[1])
297                                 profile.putMachineSetting('machine_height', data[2])
298                                 profile.putProfileSetting('nozzle_size', '0.5')
299                                 profile.putProfileSetting('filament_diameter', data[3])
300                                 profile.putProfileSetting('print_temperature', data[4])
301                                 profile.putProfileSetting('print_speed', data[5])
302                                 profile.putProfileSetting('travel_speed', data[6])
303                                 profile.putProfileSetting('retraction_speed', data[7])
304                                 profile.putProfileSetting('retraction_amount', data[8])
305                                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
306                                 profile.putMachineSetting('has_heated_bed', 'False')
307                                 profile.putMachineSetting('machine_center_is_zero', 'False')
308                                 profile.putMachineSetting('extruder_head_size_min_x', '0')
309                                 profile.putMachineSetting('extruder_head_size_min_y', '0')
310                                 profile.putMachineSetting('extruder_head_size_max_x', '0')
311                                 profile.putMachineSetting('extruder_head_size_max_y', '0')
312                                 profile.putMachineSetting('extruder_head_size_height', '0')
313
314 class OtherMachineSelectPage(InfoPage):
315         def __init__(self, parent):
316                 super(OtherMachineSelectPage, self).__init__(parent, "Other machine information")
317                 self.AddText(_("The following pre-defined machine profiles are available"))
318                 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."))
319                 self.options = []
320                 machines = resources.getDefaultMachineProfiles()
321                 machines.sort()
322                 for filename in machines:
323                         name = os.path.splitext(os.path.basename(filename))[0]
324                         item = self.AddRadioButton(name)
325                         item.filename = filename
326                         item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
327                         self.options.append(item)
328                 self.AddSeperator()
329                 item = self.AddRadioButton('Custom...')
330                 item.SetValue(True)
331                 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
332
333         def OnProfileSelect(self, e):
334                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
335
336         def OnOtherSelect(self, e):
337                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
338
339         def StoreData(self):
340                 for option in self.options:
341                         if option.GetValue():
342                                 profile.loadProfile(option.filename)
343                                 profile.loadMachineSettings(option.filename)
344
345 class OtherMachineInfoPage(InfoPage):
346         def __init__(self, parent):
347                 super(OtherMachineInfoPage, self).__init__(parent, "Cura Ready!")
348                 self.AddText(_("Cura is now ready to be used!"))
349
350 class CustomRepRapInfoPage(InfoPage):
351         def __init__(self, parent):
352                 super(CustomRepRapInfoPage, self).__init__(parent, "Custom RepRap information")
353                 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
354                 self.AddText(_("Be sure to review the default profile before running it on your machine."))
355                 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
356                 self.AddSeperator()
357                 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
358                 self.AddSeperator()
359                 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
360                 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
361                 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
362                 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "55")
363                 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
364                 self.heatedBed = self.AddCheckbox(_("Heated bed"))
365                 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
366
367         def StoreData(self):
368                 profile.putMachineSetting('machine_name', self.machineName.GetValue())
369                 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
370                 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
371                 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
372                 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
373                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
374                 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
375                 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
376                 profile.putMachineSetting('extruder_head_size_min_x', '0')
377                 profile.putMachineSetting('extruder_head_size_min_y', '0')
378                 profile.putMachineSetting('extruder_head_size_max_x', '0')
379                 profile.putMachineSetting('extruder_head_size_max_y', '0')
380                 profile.putMachineSetting('extruder_head_size_height', '0')
381                 profile.checkAndUpdateMachineName()
382
383 class MachineSelectPage(InfoPage):
384         def __init__(self, parent):
385                 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
386                 self.AddText(_("What kind of machine do you have:"))
387
388                 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2", style=wx.RB_GROUP)
389                 self.Ultimaker2Radio.SetValue(True)
390                 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
391                 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
392                 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
393                 self.PrintrbotRadio = self.AddRadioButton("Printrbot")
394                 self.PrintrbotRadio.Bind(wx.EVT_RADIOBUTTON, self.OnPrintrbotSelect)
395                 self.LulzbotTazRadio = self.AddRadioButton("Lulzbot TAZ")
396                 self.LulzbotTazRadio.Bind(wx.EVT_RADIOBUTTON, self.OnLulzbotSelect)
397                 self.LulzbotMiniRadio = self.AddRadioButton("Lulzbot Mini")
398                 self.LulzbotMiniRadio.Bind(wx.EVT_RADIOBUTTON, self.OnLulzbotSelect)
399                 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot, Witbox)"))
400                 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
401                 self.AddSeperator()
402                 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
403                 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
404                 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
405                 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
406                 self.SubmitUserStats.SetValue(True)
407
408         def OnUltimaker2Select(self, e):
409                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
410
411         def OnUltimakerSelect(self, e):
412                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
413
414         def OnPrintrbotSelect(self, e):
415                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().printrbotSelectType)
416
417         def OnLulzbotSelect(self, e):
418                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
419
420         def OnOtherSelect(self, e):
421                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
422
423         def AllowNext(self):
424                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
425                 return True
426
427         def StoreData(self):
428                 profile.putProfileSetting('retraction_enable', 'True')
429                 if self.Ultimaker2Radio.GetValue():
430                         profile.putMachineSetting('machine_width', '230')
431                         profile.putMachineSetting('machine_depth', '225')
432                         profile.putMachineSetting('machine_height', '205')
433                         profile.putMachineSetting('machine_name', 'ultimaker2')
434                         profile.putMachineSetting('machine_type', 'ultimaker2')
435                         profile.putMachineSetting('machine_center_is_zero', 'False')
436                         profile.putMachineSetting('has_heated_bed', 'True')
437                         profile.putMachineSetting('gcode_flavor', 'UltiGCode')
438                         profile.putMachineSetting('extruder_head_size_min_x', '40.0')
439                         profile.putMachineSetting('extruder_head_size_min_y', '10.0')
440                         profile.putMachineSetting('extruder_head_size_max_x', '60.0')
441                         profile.putMachineSetting('extruder_head_size_max_y', '30.0')
442                         profile.putMachineSetting('extruder_head_size_height', '55.0')
443                         profile.putProfileSetting('nozzle_size', '0.4')
444                         profile.putProfileSetting('fan_full_height', '5.0')
445                         profile.putMachineSetting('extruder_offset_x1', '18.0')
446                         profile.putMachineSetting('extruder_offset_y1', '0.0')
447                 elif self.UltimakerRadio.GetValue():
448                         profile.putMachineSetting('machine_width', '205')
449                         profile.putMachineSetting('machine_depth', '205')
450                         profile.putMachineSetting('machine_height', '200')
451                         profile.putMachineSetting('machine_name', 'ultimaker original')
452                         profile.putMachineSetting('machine_type', 'ultimaker')
453                         profile.putMachineSetting('machine_center_is_zero', 'False')
454                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
455                         profile.putProfileSetting('nozzle_size', '0.4')
456                         profile.putMachineSetting('extruder_head_size_min_x', '75.0')
457                         profile.putMachineSetting('extruder_head_size_min_y', '18.0')
458                         profile.putMachineSetting('extruder_head_size_max_x', '18.0')
459                         profile.putMachineSetting('extruder_head_size_max_y', '35.0')
460                         profile.putMachineSetting('extruder_head_size_height', '55.0')
461                 elif self.LulzbotTazRadio.GetValue() or self.LulzbotMiniRadio.GetValue():
462                         if self.LulzbotTazRadio.GetValue():
463                                 profile.putMachineSetting('machine_width', '298')
464                                 profile.putMachineSetting('machine_depth', '275')
465                                 profile.putMachineSetting('machine_height', '250')
466                                 profile.putMachineSetting('machine_name', 'Lulzbot TAZ')
467                         else:
468                                 profile.putMachineSetting('machine_width', '160')
469                                 profile.putMachineSetting('machine_depth', '160')
470                                 profile.putMachineSetting('machine_height', '160')
471                                 profile.putMachineSetting('machine_name', 'Lulzbot Mini')
472                         profile.putMachineSetting('machine_type', 'Aleph Objects')
473                         profile.putMachineSetting('machine_center_is_zero', 'False')
474                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
475                         profile.putMachineSetting('has_heated_bed', 'True')
476                         profile.putProfileSetting('nozzle_size', '0.4')
477                         profile.putMachineSetting('extruder_head_size_min_x', '0.0')
478                         profile.putMachineSetting('extruder_head_size_min_y', '0.0')
479                         profile.putMachineSetting('extruder_head_size_max_x', '0.0')
480                         profile.putMachineSetting('extruder_head_size_max_y', '0.0')
481                         profile.putMachineSetting('extruder_head_size_height', '0.0')
482                 else:
483                         profile.putMachineSetting('machine_width', '80')
484                         profile.putMachineSetting('machine_depth', '80')
485                         profile.putMachineSetting('machine_height', '60')
486                         profile.putMachineSetting('machine_name', 'reprap')
487                         profile.putMachineSetting('machine_type', 'reprap')
488                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
489                         profile.putPreference('startMode', 'Normal')
490                         profile.putProfileSetting('nozzle_size', '0.5')
491                 profile.checkAndUpdateMachineName()
492                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
493                 if self.SubmitUserStats.GetValue():
494                         profile.putPreference('submit_slice_information', 'True')
495                 else:
496                         profile.putPreference('submit_slice_information', 'False')
497
498
499 class SelectParts(InfoPage):
500         def __init__(self, parent):
501                 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
502                 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."))
503                 self.AddSeperator()
504                 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
505                 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
506                 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
507                 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
508                 self.AddSeperator()
509                 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."))
510                 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
511                 self.springExtruder.SetValue(True)
512
513         def StoreData(self):
514                 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
515                 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
516                         profile.putMachineSetting('has_heated_bed', 'True')
517                 else:
518                         profile.putMachineSetting('has_heated_bed', 'False')
519                 if self.dualExtrusion.GetValue():
520                         profile.putMachineSetting('extruder_amount', '2')
521                         profile.putMachineSetting('machine_depth', '195')
522                 else:
523                         profile.putMachineSetting('extruder_amount', '1')
524                 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
525                         profile.putProfileSetting('retraction_enable', 'True')
526                 else:
527                         profile.putProfileSetting('retraction_enable', 'False')
528
529
530 class UltimakerFirmwareUpgradePage(InfoPage):
531         def __init__(self, parent):
532                 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
533                 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."))
534                 self.AddHiddenSeperator()
535                 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
536                 self.AddHiddenSeperator()
537                 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."))
538                 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
539                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
540                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
541                 self.AddHiddenSeperator()
542                 self.AddText(_("Do not upgrade to this firmware if:"))
543                 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
544                 self.AddText(_("* Have other changes in the firmware"))
545 #               button = self.AddButton('Goto this page for a custom firmware')
546 #               button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
547
548         def AllowNext(self):
549                 return False
550
551         def OnUpgradeClick(self, e):
552                 if firmwareInstall.InstallFirmware():
553                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
554
555         def OnSkipClick(self, e):
556                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
557                 self.GetParent().ShowPage(self.GetNext())
558
559         def OnUrlClick(self, e):
560                 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
561
562 class UltimakerCheckupPage(InfoPage):
563         def __init__(self, parent):
564                 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
565
566                 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
567                 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
568                 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
569                 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
570                 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
571                 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
572                 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
573                 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
574                 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
575                 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
576
577                 self.AddText(
578                         _("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."))
579                 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
580                 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
581                 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
582                 self.AddSeperator()
583                 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
584                 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
585                 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
586                 self.AddSeperator()
587                 self.infoBox = self.AddInfoBox()
588                 self.machineState = self.AddText("")
589                 self.temperatureLabel = self.AddText("")
590                 self.errorLogButton = self.AddButton(_("Show error log"))
591                 self.errorLogButton.Show(False)
592                 self.AddSeperator()
593                 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
594                 self.comm = None
595                 self.xMinStop = False
596                 self.xMaxStop = False
597                 self.yMinStop = False
598                 self.yMaxStop = False
599                 self.zMinStop = False
600                 self.zMaxStop = False
601
602                 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
603
604         def __del__(self):
605                 if self.comm is not None:
606                         self.comm.close()
607
608         def AllowNext(self):
609                 self.endstopBitmap.Show(False)
610                 return False
611
612         def OnSkipClick(self, e):
613                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
614                 self.GetParent().ShowPage(self.GetNext())
615
616         def OnCheckClick(self, e=None):
617                 self.errorLogButton.Show(False)
618                 if self.comm is not None:
619                         self.comm.close()
620                         del self.comm
621                         self.comm = None
622                         wx.CallAfter(self.OnCheckClick)
623                         return
624                 self.infoBox.SetBusy(_("Connecting to machine."))
625                 self.commState.SetBitmap(self.unknownBitmap)
626                 self.tempState.SetBitmap(self.unknownBitmap)
627                 self.stopState.SetBitmap(self.unknownBitmap)
628                 self.checkupState = 0
629                 self.checkExtruderNr = 0
630                 self.comm = machineCom.MachineCom(callbackObject=self)
631
632         def OnErrorLog(self, e):
633                 printWindow.LogWindow('\n'.join(self.comm.getLog()))
634
635         def mcLog(self, message):
636                 pass
637
638         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
639                 if not self.comm.isOperational():
640                         return
641                 if self.checkupState == 0:
642                         self.tempCheckTimeout = 20
643                         if temp[self.checkExtruderNr] > 70:
644                                 self.checkupState = 1
645                                 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
646                                 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
647                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
648                         else:
649                                 self.startTemp = temp[self.checkExtruderNr]
650                                 self.checkupState = 2
651                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
652                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
653                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
654                 elif self.checkupState == 1:
655                         if temp < 60:
656                                 self.startTemp = temp[self.checkExtruderNr]
657                                 self.checkupState = 2
658                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
659                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
660                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
661                 elif self.checkupState == 2:
662                         #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
663                         if temp[self.checkExtruderNr] > self.startTemp + 40:
664                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
665                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
666                                 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
667                                         self.checkExtruderNr = 0
668                                         self.checkupState = 3
669                                         wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
670                                         wx.CallAfter(self.endstopBitmap.Show, True)
671                                         wx.CallAfter(self.Layout)
672                                         self.comm.sendCommand('M119')
673                                         wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
674                                 else:
675                                         self.checkupState = 0
676                                         self.checkExtruderNr += 1
677                         else:
678                                 self.tempCheckTimeout -= 1
679                                 if self.tempCheckTimeout < 1:
680                                         self.checkupState = -1
681                                         wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
682                                         wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
683                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
684                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
685                 elif self.checkupState >= 3 and self.checkupState < 10:
686                         self.comm.sendCommand('M119')
687                 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
688
689         def mcStateChange(self, state):
690                 if self.comm is None:
691                         return
692                 if self.comm.isOperational():
693                         wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
694                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
695                 elif self.comm.isError():
696                         wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
697                         wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
698                         wx.CallAfter(self.endstopBitmap.Show, False)
699                         wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
700                         wx.CallAfter(self.errorLogButton.Show, True)
701                         wx.CallAfter(self.Layout)
702                 else:
703                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
704
705         def mcMessage(self, message):
706                 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
707                         for data in message.split(' '):
708                                 if ':' in data:
709                                         tag, value = data.split(':', 1)
710                                         if tag == 'x_min':
711                                                 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
712                                         if tag == 'x_max':
713                                                 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
714                                         if tag == 'y_min':
715                                                 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
716                                         if tag == 'y_max':
717                                                 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
718                                         if tag == 'z_min':
719                                                 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
720                                         if tag == 'z_max':
721                                                 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
722                         if ':' in message:
723                                 tag, value = map(str.strip, message.split(':', 1))
724                                 if tag == 'x_min':
725                                         self.xMinStop = (value == 'H' or value == 'TRIGGERED')
726                                 if tag == 'x_max':
727                                         self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
728                                 if tag == 'y_min':
729                                         self.yMinStop = (value == 'H' or value == 'TRIGGERED')
730                                 if tag == 'y_max':
731                                         self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
732                                 if tag == 'z_min':
733                                         self.zMinStop = (value == 'H' or value == 'TRIGGERED')
734                                 if tag == 'z_max':
735                                         self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
736                         if 'z_max' in message:
737                                 self.comm.sendCommand('M119')
738
739                         if self.checkupState == 3:
740                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
741                                         self.checkupState = 4
742                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
743                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
744                         elif self.checkupState == 4:
745                                 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
746                                         self.checkupState = 5
747                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
748                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
749                         elif self.checkupState == 5:
750                                 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
751                                         self.checkupState = 6
752                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
753                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
754                         elif self.checkupState == 6:
755                                 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
756                                         self.checkupState = 7
757                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
758                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
759                         elif self.checkupState == 7:
760                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
761                                         self.checkupState = 8
762                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
763                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
764                         elif self.checkupState == 8:
765                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
766                                         self.checkupState = 9
767                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
768                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
769                         elif self.checkupState == 9:
770                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
771                                         self.checkupState = 10
772                                         self.comm.close()
773                                         wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
774                                         wx.CallAfter(self.infoBox.SetReadyIndicator)
775                                         wx.CallAfter(self.endstopBitmap.Show, False)
776                                         wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
777                                         wx.CallAfter(self.OnSkipClick, None)
778
779         def mcProgress(self, lineNr):
780                 pass
781
782         def mcZChange(self, newZ):
783                 pass
784
785
786 class UltimakerCalibrationPage(InfoPage):
787         def __init__(self, parent):
788                 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
789
790                 self.AddText("Your Ultimaker requires some calibration.")
791                 self.AddText("This calibration is needed for a proper extrusion amount.")
792                 self.AddSeperator()
793                 self.AddText("The following values are needed:")
794                 self.AddText("* Diameter of filament")
795                 self.AddText("* Number of steps per mm of filament extrusion")
796                 self.AddSeperator()
797                 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
798                 self.AddSeperator()
799                 self.AddText("First we need the diameter of your filament:")
800                 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
801                 self.AddText(
802                         "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.")
803                 self.AddText("Note: This value can be changed later at any time.")
804
805         def StoreData(self):
806                 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
807
808
809 class UltimakerCalibrateStepsPerEPage(InfoPage):
810         def __init__(self, parent):
811                 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
812
813                 #if profile.getMachineSetting('steps_per_e') == '0':
814                 #       profile.putMachineSetting('steps_per_e', '865.888')
815
816                 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
817                 self.AddText(_("First remove any filament from your machine."))
818                 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
819                 self.AddText(_("We'll push the filament 100mm"))
820                 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
821                 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
822                 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
823                 self.AddText(_("This results in the following steps per E:"))
824                 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
825                 self.AddText(_("You can repeat these steps to get better calibration."))
826                 self.AddSeperator()
827                 self.AddText(
828                         _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
829                 self.heatButton = self.AddButton(_("Heatup for filament removal"))
830
831                 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
832                 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
833                 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
834
835         def OnSaveLengthClick(self, e):
836                 currentEValue = float(self.stepsPerEInput.GetValue())
837                 realExtrudeLength = float(self.lengthInput.GetValue())
838                 newEValue = currentEValue * 100 / realExtrudeLength
839                 self.stepsPerEInput.SetValue(str(newEValue))
840                 self.lengthInput.SetValue("100")
841
842         def OnExtrudeClick(self, e):
843                 t = threading.Thread(target=self.OnExtrudeRun)
844                 t.daemon = True
845                 t.start()
846
847         def OnExtrudeRun(self):
848                 self.heatButton.Enable(False)
849                 self.extrudeButton.Enable(False)
850                 currentEValue = float(self.stepsPerEInput.GetValue())
851                 self.comm = machineCom.MachineCom()
852                 if not self.comm.isOpen():
853                         wx.MessageBox(
854                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
855                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
856                         self.heatButton.Enable(True)
857                         self.extrudeButton.Enable(True)
858                         return
859                 while True:
860                         line = self.comm.readline()
861                         if line == '':
862                                 return
863                         if 'start' in line:
864                                 break
865                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
866                 time.sleep(3)
867
868                 self.sendGCommand('M302') #Disable cold extrusion protection
869                 self.sendGCommand("M92 E%f" % (currentEValue))
870                 self.sendGCommand("G92 E0")
871                 self.sendGCommand("G1 E100 F600")
872                 time.sleep(15)
873                 self.comm.close()
874                 self.extrudeButton.Enable()
875                 self.heatButton.Enable()
876
877         def OnHeatClick(self, e):
878                 t = threading.Thread(target=self.OnHeatRun)
879                 t.daemon = True
880                 t.start()
881
882         def OnHeatRun(self):
883                 self.heatButton.Enable(False)
884                 self.extrudeButton.Enable(False)
885                 self.comm = machineCom.MachineCom()
886                 if not self.comm.isOpen():
887                         wx.MessageBox(
888                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
889                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
890                         self.heatButton.Enable(True)
891                         self.extrudeButton.Enable(True)
892                         return
893                 while True:
894                         line = self.comm.readline()
895                         if line == '':
896                                 self.heatButton.Enable(True)
897                                 self.extrudeButton.Enable(True)
898                                 return
899                         if 'start' in line:
900                                 break
901                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
902                 time.sleep(3)
903
904                 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
905                 wx.MessageBox(
906                         'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
907                         'Machine heatup', wx.OK | wx.ICON_INFORMATION)
908                 self.sendGCommand('M104 S0')
909                 time.sleep(1)
910                 self.comm.close()
911                 self.heatButton.Enable(True)
912                 self.extrudeButton.Enable(True)
913
914         def sendGCommand(self, cmd):
915                 self.comm.sendCommand(cmd) #Disable cold extrusion protection
916                 while True:
917                         line = self.comm.readline()
918                         if line == '':
919                                 return
920                         if line.startswith('ok'):
921                                 break
922
923         def StoreData(self):
924                 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
925
926 class Ultimaker2ReadyPage(InfoPage):
927         def __init__(self, parent):
928                 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
929                 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
930                 self.AddText('Cura is now ready to be used with your Ultimaker2.')
931                 self.AddSeperator()
932
933 class LulzbotReadyPage(InfoPage):
934         def __init__(self, parent):
935                 super(LulzbotReadyPage, self).__init__(parent, "Lulzbot TAZ/Mini")
936                 self.AddText('Cura is now ready to be used with your Lulzbot.')
937                 self.AddSeperator()
938
939 class configWizard(wx.wizard.Wizard):
940         def __init__(self, addNew = False):
941                 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
942
943                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
944                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
945
946                 self.firstInfoPage = FirstInfoPage(self, addNew)
947                 self.machineSelectPage = MachineSelectPage(self)
948                 self.ultimakerSelectParts = SelectParts(self)
949                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
950                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
951                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
952                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
953                 self.bedLevelPage = bedLevelWizardMain(self)
954                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
955                 self.printrbotSelectType = PrintrbotPage(self)
956                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
957                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
958                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
959
960                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
961                 self.lulzbotReadyPage = LulzbotReadyPage(self)
962
963                 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
964                 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
965                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
966                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
967                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
968                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
969                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
970                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
971                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
972
973                 self.FitToPage(self.firstInfoPage)
974                 self.GetPageAreaSizer().Add(self.firstInfoPage)
975
976                 self.RunWizard(self.firstInfoPage)
977                 self.Destroy()
978
979         def OnPageChanging(self, e):
980                 e.GetPage().StoreData()
981
982         def OnPageChanged(self, e):
983                 if e.GetPage().AllowNext():
984                         self.FindWindowById(wx.ID_FORWARD).Enable()
985                 else:
986                         self.FindWindowById(wx.ID_FORWARD).Disable()
987                 if e.GetPage().AllowBack():
988                         self.FindWindowById(wx.ID_BACKWARD).Enable()
989                 else:
990                         self.FindWindowById(wx.ID_BACKWARD).Disable()
991
992 class bedLevelWizardMain(InfoPage):
993         def __init__(self, parent):
994                 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
995
996                 self.AddText('This wizard will help you in leveling your printer bed')
997                 self.AddSeperator()
998                 self.AddText('It will do the following steps')
999                 self.AddText('* Move the printer head to each corner')
1000                 self.AddText('  and let you adjust the height of the bed to the nozzle')
1001                 self.AddText('* Print a line around the bed to check if it is level')
1002                 self.AddSeperator()
1003
1004                 self.connectButton = self.AddButton('Connect to printer')
1005                 self.comm = None
1006
1007                 self.infoBox = self.AddInfoBox()
1008                 self.resumeButton = self.AddButton('Resume')
1009                 self.upButton, self.downButton = self.AddDualButton('Up 0.2mm', 'Down 0.2mm')
1010                 self.upButton2, self.downButton2 = self.AddDualButton('Up 10mm', 'Down 10mm')
1011                 self.resumeButton.Enable(False)
1012
1013                 self.upButton.Enable(False)
1014                 self.downButton.Enable(False)
1015                 self.upButton2.Enable(False)
1016                 self.downButton2.Enable(False)
1017
1018                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1019                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1020                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1021                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1022                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1023                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1024
1025         def OnConnect(self, e = None):
1026                 if self.comm is not None:
1027                         self.comm.close()
1028                         del self.comm
1029                         self.comm = None
1030                         wx.CallAfter(self.OnConnect)
1031                         return
1032                 self.connectButton.Enable(False)
1033                 self.comm = machineCom.MachineCom(callbackObject=self)
1034                 self.infoBox.SetBusy('Connecting to machine.')
1035                 self._wizardState = 0
1036
1037         def OnBedUp(self, e):
1038                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1039                 self.comm.sendCommand('G92 Z10')
1040                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1041                 self.comm.sendCommand('M400')
1042
1043         def OnBedDown(self, e):
1044                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1045                 self.comm.sendCommand('G92 Z10')
1046                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1047                 self.comm.sendCommand('M400')
1048
1049         def OnBedUp2(self, e):
1050                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1051                 self.comm.sendCommand('G92 Z10')
1052                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1053                 self.comm.sendCommand('M400')
1054
1055         def OnBedDown2(self, e):
1056                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1057                 self.comm.sendCommand('G92 Z10')
1058                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1059                 self.comm.sendCommand('M400')
1060
1061         def AllowNext(self):
1062                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1063                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1064                 return True
1065
1066         def OnResume(self, e):
1067                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1068                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1069                 if self._wizardState == -1:
1070                         wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
1071                         wx.CallAfter(self.upButton.Enable, False)
1072                         wx.CallAfter(self.downButton.Enable, False)
1073                         wx.CallAfter(self.upButton2.Enable, False)
1074                         wx.CallAfter(self.downButton2.Enable, False)
1075                         self.comm.sendCommand('M105')
1076                         self.comm.sendCommand('G28')
1077                         self._wizardState = 1
1078                 elif self._wizardState == 2:
1079                         if profile.getMachineSetting('has_heated_bed') == 'True':
1080                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back center...')
1081                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1082                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1083                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1084                                 self.comm.sendCommand('M400')
1085                                 self._wizardState = 3
1086                         else:
1087                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
1088                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1089                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1090                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1091                                 self.comm.sendCommand('M400')
1092                                 self._wizardState = 3
1093                 elif self._wizardState == 4:
1094                         if profile.getMachineSetting('has_heated_bed') == 'True':
1095                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
1096                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1097                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1098                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1099                                 self.comm.sendCommand('M400')
1100                                 self._wizardState = 7
1101                         else:
1102                                 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
1103                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1104                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1105                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1106                                 self.comm.sendCommand('M400')
1107                                 self._wizardState = 5
1108                 elif self._wizardState == 6:
1109                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
1110                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1111                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1112                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1113                         self.comm.sendCommand('M400')
1114                         self._wizardState = 7
1115                 elif self._wizardState == 8:
1116                         wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
1117                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1118                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1119                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1120                         self._wizardState = 9
1121                 elif self._wizardState == 10:
1122                         self._wizardState = 11
1123                         wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
1124                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1125                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1126                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1127                         w = profile.getMachineSettingFloat('machine_width') - 10
1128                         d = profile.getMachineSettingFloat('machine_depth')
1129                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1130                         filamentArea = math.pi * filamentRadius * filamentRadius
1131                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1132                         eValue = 0.0
1133
1134                         gcodeList = [
1135                                 'G1 Z2 F%d' % (feedZ),
1136                                 'G92 E0',
1137                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1138                                 'G1 Z0.3 F%d' % (feedZ)]
1139                         eValue += 5.0
1140                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1141
1142                         for i in xrange(0, 3):
1143                                 dist = 5.0 + 0.4 * float(i)
1144                                 eValue += (d - 2.0*dist) * ePerMM
1145                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1146                                 eValue += (w - 2.0*dist) * ePerMM
1147                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1148                                 eValue += (d - 2.0*dist) * ePerMM
1149                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1150                                 eValue += (w - 2.0*dist) * ePerMM
1151                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1152
1153                         gcodeList.append('M400')
1154                         self.comm.printGCode(gcodeList)
1155                 self.resumeButton.Enable(False)
1156
1157         def mcLog(self, message):
1158                 print 'Log:', message
1159
1160         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1161                 if self._wizardState == 1:
1162                         self._wizardState = 2
1163                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
1164                         wx.CallAfter(self.resumeButton.Enable, True)
1165                 elif self._wizardState == 3:
1166                         self._wizardState = 4
1167                         if profile.getMachineSetting('has_heated_bed') == 'True':
1168                                 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.')
1169                         else:
1170                                 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
1171                         wx.CallAfter(self.resumeButton.Enable, True)
1172                 elif self._wizardState == 5:
1173                         self._wizardState = 6
1174                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
1175                         wx.CallAfter(self.resumeButton.Enable, True)
1176                 elif self._wizardState == 7:
1177                         self._wizardState = 8
1178                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
1179                         wx.CallAfter(self.resumeButton.Enable, True)
1180                 elif self._wizardState == 9:
1181                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1182                                 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1183                         else:
1184                                 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
1185                                 wx.CallAfter(self.resumeButton.Enable, True)
1186                                 self._wizardState = 10
1187
1188         def mcStateChange(self, state):
1189                 if self.comm is None:
1190                         return
1191                 if self.comm.isOperational():
1192                         if self._wizardState == 0:
1193                                 wx.CallAfter(self.infoBox.SetAttention, 'Use the up/down buttons to move the bed and adjust your Z endstop.')
1194                                 wx.CallAfter(self.upButton.Enable, True)
1195                                 wx.CallAfter(self.downButton.Enable, True)
1196                                 wx.CallAfter(self.upButton2.Enable, True)
1197                                 wx.CallAfter(self.downButton2.Enable, True)
1198                                 wx.CallAfter(self.resumeButton.Enable, True)
1199                                 self._wizardState = -1
1200                         elif self._wizardState == 11 and not self.comm.isPrinting():
1201                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1202                                 self.comm.sendCommand('G92 E0')
1203                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1204                                 self.comm.sendCommand('M104 S0')
1205                                 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
1206                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1207                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1208                                 wx.CallAfter(self.connectButton.Enable, True)
1209                                 self._wizardState = 12
1210                 elif self.comm.isError():
1211                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1212
1213         def mcMessage(self, message):
1214                 pass
1215
1216         def mcProgress(self, lineNr):
1217                 pass
1218
1219         def mcZChange(self, newZ):
1220                 pass
1221
1222 class headOffsetCalibrationPage(InfoPage):
1223         def __init__(self, parent):
1224                 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1225
1226                 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
1227                 self.AddSeperator()
1228
1229                 self.connectButton = self.AddButton('Connect to printer')
1230                 self.comm = None
1231
1232                 self.infoBox = self.AddInfoBox()
1233                 self.textEntry = self.AddTextCtrl('')
1234                 self.textEntry.Enable(False)
1235                 self.resumeButton = self.AddButton('Resume')
1236                 self.resumeButton.Enable(False)
1237
1238                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1239                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1240
1241         def AllowBack(self):
1242                 return True
1243
1244         def OnConnect(self, e = None):
1245                 if self.comm is not None:
1246                         self.comm.close()
1247                         del self.comm
1248                         self.comm = None
1249                         wx.CallAfter(self.OnConnect)
1250                         return
1251                 self.connectButton.Enable(False)
1252                 self.comm = machineCom.MachineCom(callbackObject=self)
1253                 self.infoBox.SetBusy('Connecting to machine.')
1254                 self._wizardState = 0
1255
1256         def OnResume(self, e):
1257                 if self._wizardState == 2:
1258                         self._wizardState = 3
1259                         wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1260
1261                         w = profile.getMachineSettingFloat('machine_width')
1262                         d = profile.getMachineSettingFloat('machine_depth')
1263
1264                         gcode = gcodeGenerator.gcodeGenerator()
1265                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1266                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1267                         gcode.addCmd('T0')
1268                         gcode.addPrime(15)
1269                         gcode.addCmd('T1')
1270                         gcode.addPrime(15)
1271
1272                         gcode.addCmd('T0')
1273                         gcode.addMove(w/2, 5)
1274                         gcode.addMove(z=0.2)
1275                         gcode.addPrime()
1276                         gcode.addExtrude(w/2, d-5.0)
1277                         gcode.addRetract()
1278                         gcode.addMove(5, d/2)
1279                         gcode.addPrime()
1280                         gcode.addExtrude(w-5.0, d/2)
1281                         gcode.addRetract(15)
1282
1283                         gcode.addCmd('T1')
1284                         gcode.addMove(w/2, 5)
1285                         gcode.addPrime()
1286                         gcode.addExtrude(w/2, d-5.0)
1287                         gcode.addRetract()
1288                         gcode.addMove(5, d/2)
1289                         gcode.addPrime()
1290                         gcode.addExtrude(w-5.0, d/2)
1291                         gcode.addRetract(15)
1292                         gcode.addCmd('T0')
1293
1294                         gcode.addMove(z=25)
1295                         gcode.addMove(0, 0)
1296                         gcode.addCmd('M400')
1297
1298                         self.comm.printGCode(gcode.list())
1299                         self.resumeButton.Enable(False)
1300                 elif self._wizardState == 4:
1301                         try:
1302                                 float(self.textEntry.GetValue())
1303                         except ValueError:
1304                                 return
1305                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1306                         self._wizardState = 5
1307                         self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1308                         self.textEntry.SetValue('0.0')
1309                         self.textEntry.Enable(True)
1310                 elif self._wizardState == 5:
1311                         try:
1312                                 float(self.textEntry.GetValue())
1313                         except ValueError:
1314                                 return
1315                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1316                         self._wizardState = 6
1317                         self.infoBox.SetBusy('Printing the fine calibration lines.')
1318                         self.textEntry.SetValue('')
1319                         self.textEntry.Enable(False)
1320                         self.resumeButton.Enable(False)
1321
1322                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1323                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1324                         gcode = gcodeGenerator.gcodeGenerator()
1325                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1326                         gcode.setPrintSpeed(25)
1327                         gcode.addHome()
1328                         gcode.addCmd('T0')
1329                         gcode.addMove(50, 40, 0.2)
1330                         gcode.addPrime(15)
1331                         for n in xrange(0, 10):
1332                                 gcode.addExtrude(50 + n * 10, 150)
1333                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1334                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1335                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1336                         gcode.addMove(40, 50)
1337                         for n in xrange(0, 10):
1338                                 gcode.addExtrude(150, 50 + n * 10)
1339                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1340                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1341                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1342                         gcode.addRetract(15)
1343
1344                         gcode.addCmd('T1')
1345                         gcode.addMove(50 - x, 30 - y, 0.2)
1346                         gcode.addPrime(15)
1347                         for n in xrange(0, 10):
1348                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1349                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1350                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1351                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1352                         gcode.addMove(30 - x, 50 - y, 0.2)
1353                         for n in xrange(0, 10):
1354                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1355                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1356                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1357                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1358                         gcode.addRetract(15)
1359                         gcode.addMove(z=15)
1360                         gcode.addCmd('M400')
1361                         gcode.addCmd('M104 T0 S0')
1362                         gcode.addCmd('M104 T1 S0')
1363                         self.comm.printGCode(gcode.list())
1364                 elif self._wizardState == 7:
1365                         try:
1366                                 n = int(self.textEntry.GetValue()) - 1
1367                         except:
1368                                 return
1369                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1370                         x += -1.0 + n * 0.1
1371                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1372                         self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1373                         self.textEntry.SetValue('10')
1374                         self._wizardState = 8
1375                 elif self._wizardState == 8:
1376                         try:
1377                                 n = int(self.textEntry.GetValue()) - 1
1378                         except:
1379                                 return
1380                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1381                         y += -1.0 + n * 0.1
1382                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1383                         self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1384                         self.infoBox.SetReadyIndicator()
1385                         self._wizardState = 8
1386                         self.comm.close()
1387                         self.resumeButton.Enable(False)
1388
1389         def mcLog(self, message):
1390                 print 'Log:', message
1391
1392         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1393                 if self._wizardState == 1:
1394                         if temp[0] >= 210 and temp[1] >= 210:
1395                                 self._wizardState = 2
1396                                 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1397                                 wx.CallAfter(self.resumeButton.Enable, True)
1398                                 wx.CallAfter(self.resumeButton.SetFocus)
1399
1400         def mcStateChange(self, state):
1401                 if self.comm is None:
1402                         return
1403                 if self.comm.isOperational():
1404                         if self._wizardState == 0:
1405                                 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1406                                 self.comm.sendCommand('M105')
1407                                 self.comm.sendCommand('M104 S220 T0')
1408                                 self.comm.sendCommand('M104 S220 T1')
1409                                 self.comm.sendCommand('G28')
1410                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1411                                 self._wizardState = 1
1412                         if not self.comm.isPrinting():
1413                                 if self._wizardState == 3:
1414                                         self._wizardState = 4
1415                                         wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1416                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1417                                         wx.CallAfter(self.textEntry.Enable, True)
1418                                         wx.CallAfter(self.resumeButton.Enable, True)
1419                                         wx.CallAfter(self.resumeButton.SetFocus)
1420                                 elif self._wizardState == 6:
1421                                         self._wizardState = 7
1422                                         wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1423                                         wx.CallAfter(self.textEntry.SetValue, '10')
1424                                         wx.CallAfter(self.textEntry.Enable, True)
1425                                         wx.CallAfter(self.resumeButton.Enable, True)
1426                                         wx.CallAfter(self.resumeButton.SetFocus)
1427
1428                 elif self.comm.isError():
1429                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1430
1431         def mcMessage(self, message):
1432                 pass
1433
1434         def mcProgress(self, lineNr):
1435                 pass
1436
1437         def mcZChange(self, newZ):
1438                 pass
1439
1440 class bedLevelWizard(wx.wizard.Wizard):
1441         def __init__(self):
1442                 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1443
1444                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1445                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1446
1447                 self.mainPage = bedLevelWizardMain(self)
1448                 self.headOffsetCalibration = None
1449
1450                 self.FitToPage(self.mainPage)
1451                 self.GetPageAreaSizer().Add(self.mainPage)
1452
1453                 self.RunWizard(self.mainPage)
1454                 self.Destroy()
1455
1456         def OnPageChanging(self, e):
1457                 e.GetPage().StoreData()
1458
1459         def OnPageChanged(self, e):
1460                 if e.GetPage().AllowNext():
1461                         self.FindWindowById(wx.ID_FORWARD).Enable()
1462                 else:
1463                         self.FindWindowById(wx.ID_FORWARD).Disable()
1464                 if e.GetPage().AllowBack():
1465                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1466                 else:
1467                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1468
1469 class headOffsetWizard(wx.wizard.Wizard):
1470         def __init__(self):
1471                 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1472
1473                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1474                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1475
1476                 self.mainPage = headOffsetCalibrationPage(self)
1477
1478                 self.FitToPage(self.mainPage)
1479                 self.GetPageAreaSizer().Add(self.mainPage)
1480
1481                 self.RunWizard(self.mainPage)
1482                 self.Destroy()
1483
1484         def OnPageChanging(self, e):
1485                 e.GetPage().StoreData()
1486
1487         def OnPageChanged(self, e):
1488                 if e.GetPage().AllowNext():
1489                         self.FindWindowById(wx.ID_FORWARD).Enable()
1490                 else:
1491                         self.FindWindowById(wx.ID_FORWARD).Disable()
1492                 if e.GetPage().AllowBack():
1493                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1494                 else:
1495                         self.FindWindowById(wx.ID_BACKWARD).Disable()