chiark / gitweb /
Merge pull request #557 from GreatFruitOmsk/i18n
[cura.git] / Cura / gui / configWizard.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
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.resources import getPathForImage
18
19 class InfoBox(wx.Panel):
20         def __init__(self, parent):
21                 super(InfoBox, self).__init__(parent)
22                 self.SetBackgroundColour('#FFFF80')
23
24                 self.sizer = wx.GridBagSizer(5, 5)
25                 self.SetSizer(self.sizer)
26
27                 self.attentionBitmap = wx.Bitmap(getPathForImage('attention.png'))
28                 self.errorBitmap = wx.Bitmap(getPathForImage('error.png'))
29                 self.readyBitmap = wx.Bitmap(getPathForImage('ready.png'))
30                 self.busyBitmap = [
31                         wx.Bitmap(getPathForImage('busy-0.png')),
32                         wx.Bitmap(getPathForImage('busy-1.png')),
33                         wx.Bitmap(getPathForImage('busy-2.png')),
34                         wx.Bitmap(getPathForImage('busy-3.png'))
35                 ]
36
37                 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
38                 self.text = wx.StaticText(self, -1, '')
39                 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
40                 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
41                 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
42                 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
43                 self.sizer.AddGrowableCol(1)
44
45                 self.extraInfoButton.Show(False)
46
47                 self.extraInfoUrl = ''
48                 self.busyState = None
49                 self.timer = wx.Timer(self)
50                 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
51                 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
52                 self.timer.Start(100)
53
54         def SetInfo(self, info):
55                 self.SetBackgroundColour('#FFFF80')
56                 self.text.SetLabel(info)
57                 self.extraInfoButton.Show(False)
58                 self.Refresh()
59
60         def SetError(self, info, extraInfoUrl):
61                 self.extraInfoUrl = extraInfoUrl
62                 self.SetBackgroundColour('#FF8080')
63                 self.text.SetLabel(info)
64                 self.extraInfoButton.Show(True)
65                 self.Layout()
66                 self.SetErrorIndicator()
67                 self.Refresh()
68
69         def SetAttention(self, info):
70                 self.SetBackgroundColour('#FFFF80')
71                 self.text.SetLabel(info)
72                 self.extraInfoButton.Show(False)
73                 self.SetAttentionIndicator()
74                 self.Layout()
75                 self.Refresh()
76
77         def SetBusy(self, info):
78                 self.SetInfo(info)
79                 self.SetBusyIndicator()
80
81         def SetBusyIndicator(self):
82                 self.busyState = 0
83                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
84
85         def doExtraInfo(self, e):
86                 webbrowser.open(self.extraInfoUrl)
87
88         def doBusyUpdate(self, e):
89                 if self.busyState is None:
90                         return
91                 self.busyState += 1
92                 if self.busyState >= len(self.busyBitmap):
93                         self.busyState = 0
94                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
95
96         def SetReadyIndicator(self):
97                 self.busyState = None
98                 self.bitmap.SetBitmap(self.readyBitmap)
99
100         def SetErrorIndicator(self):
101                 self.busyState = None
102                 self.bitmap.SetBitmap(self.errorBitmap)
103
104         def SetAttentionIndicator(self):
105                 self.busyState = None
106                 self.bitmap.SetBitmap(self.attentionBitmap)
107
108
109 class InfoPage(wx.wizard.WizardPageSimple):
110         def __init__(self, parent, title):
111                 wx.wizard.WizardPageSimple.__init__(self, parent)
112
113                 sizer = wx.GridBagSizer(5, 5)
114                 self.sizer = sizer
115                 self.SetSizer(sizer)
116
117                 title = wx.StaticText(self, -1, title)
118                 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
119                 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
120                 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
121                 sizer.AddGrowableCol(1)
122
123                 self.rowNr = 2
124
125         def AddText(self, info):
126                 text = wx.StaticText(self, -1, info)
127                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
128                 self.rowNr += 1
129                 return text
130
131         def AddSeperator(self):
132                 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
133                 self.rowNr += 1
134
135         def AddHiddenSeperator(self):
136                 self.AddText("")
137
138         def AddInfoBox(self):
139                 infoBox = InfoBox(self)
140                 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
141                 self.rowNr += 1
142                 return infoBox
143
144         def AddRadioButton(self, label, style=0):
145                 radio = wx.RadioButton(self, -1, label, style=style)
146                 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
147                 self.rowNr += 1
148                 return radio
149
150         def AddCheckbox(self, label, checked=False):
151                 check = wx.CheckBox(self, -1)
152                 text = wx.StaticText(self, -1, label)
153                 check.SetValue(checked)
154                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
155                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
156                 self.rowNr += 1
157                 return check
158
159         def AddButton(self, label):
160                 button = wx.Button(self, -1, label)
161                 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
162                 self.rowNr += 1
163                 return button
164
165         def AddDualButton(self, label1, label2):
166                 button1 = wx.Button(self, -1, label1)
167                 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
168                 button2 = wx.Button(self, -1, label2)
169                 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
170                 self.rowNr += 1
171                 return button1, button2
172
173         def AddTextCtrl(self, value):
174                 ret = wx.TextCtrl(self, -1, value)
175                 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
176                 self.rowNr += 1
177                 return ret
178
179         def AddLabelTextCtrl(self, info, value):
180                 text = wx.StaticText(self, -1, info)
181                 ret = wx.TextCtrl(self, -1, value)
182                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
183                 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
184                 self.rowNr += 1
185                 return ret
186
187         def AddTextCtrlButton(self, value, buttonText):
188                 text = wx.TextCtrl(self, -1, value)
189                 button = wx.Button(self, -1, buttonText)
190                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
191                 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
192                 self.rowNr += 1
193                 return text, button
194
195         def AddBitmap(self, bitmap):
196                 bitmap = wx.StaticBitmap(self, -1, bitmap)
197                 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
198                 self.rowNr += 1
199                 return bitmap
200
201         def AddCheckmark(self, label, bitmap):
202                 check = wx.StaticBitmap(self, -1, bitmap)
203                 text = wx.StaticText(self, -1, label)
204                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
205                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
206                 self.rowNr += 1
207                 return check
208
209         def AllowNext(self):
210                 return True
211
212         def StoreData(self):
213                 pass
214
215
216 class FirstInfoPage(InfoPage):
217         def __init__(self, parent):
218                 super(FirstInfoPage, self).__init__(parent, _("First time run wizard"))
219                 self.AddText(_("Welcome, and thanks for trying Cura!"))
220                 self.AddSeperator()
221                 self.AddText(_("This wizard will help you with the following steps:"))
222                 self.AddText(_("* Configure Cura for your machine"))
223                 self.AddText(_("* Upgrade your firmware"))
224                 self.AddText(_("* Check if your machine is working safely"))
225                 self.AddText(_("* Level your printer bed"))
226
227                 #self.AddText('* Calibrate your machine')
228                 #self.AddText('* Do your first print')
229
230
231 class RepRapInfoPage(InfoPage):
232         def __init__(self, parent):
233                 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
234                 self.AddText(
235                         _("RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them."))
236                 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
237                 self.AddSeperator()
238                 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
239                 self.AddSeperator()
240                 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
241                 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
242                 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "60")
243                 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
244                 self.heatedBed = self.AddCheckbox(_("Heated bed"))
245                 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
246
247         def StoreData(self):
248                 profile.putPreference('machine_width', self.machineWidth.GetValue())
249                 profile.putPreference('machine_depth', self.machineDepth.GetValue())
250                 profile.putPreference('machine_height', self.machineHeight.GetValue())
251                 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
252                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
253                 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
254                 profile.putPreference('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
255                 profile.putPreference('extruder_head_size_min_x', '0')
256                 profile.putPreference('extruder_head_size_min_y', '0')
257                 profile.putPreference('extruder_head_size_max_x', '0')
258                 profile.putPreference('extruder_head_size_max_y', '0')
259                 profile.putPreference('extruder_head_size_height', '0')
260
261
262 class MachineSelectPage(InfoPage):
263         def __init__(self, parent):
264                 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
265                 self.AddText(_("What kind of machine do you have:"))
266
267                 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
268                 self.UltimakerRadio.SetValue(True)
269                 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
270                 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap)"))
271                 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
272                 self.AddSeperator()
273                 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
274                 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
275                 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
276                 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
277                 self.SubmitUserStats.SetValue(True)
278
279         def OnUltimakerSelect(self, e):
280                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
281
282         def OnOtherSelect(self, e):
283                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
284
285         def StoreData(self):
286                 if self.UltimakerRadio.GetValue():
287                         profile.putPreference('machine_width', '205')
288                         profile.putPreference('machine_depth', '205')
289                         profile.putPreference('machine_height', '200')
290                         profile.putPreference('machine_type', 'ultimaker')
291                         profile.putPreference('machine_center_is_zero', 'False')
292                         profile.putProfileSetting('nozzle_size', '0.4')
293                         profile.putPreference('extruder_head_size_min_x', '75.0')
294                         profile.putPreference('extruder_head_size_min_y', '18.0')
295                         profile.putPreference('extruder_head_size_max_x', '18.0')
296                         profile.putPreference('extruder_head_size_max_y', '35.0')
297                         profile.putPreference('extruder_head_size_height', '60.0')
298                 else:
299                         profile.putPreference('machine_width', '80')
300                         profile.putPreference('machine_depth', '80')
301                         profile.putPreference('machine_height', '60')
302                         profile.putPreference('machine_type', 'reprap')
303                         profile.putPreference('startMode', 'Normal')
304                         profile.putProfileSetting('nozzle_size', '0.5')
305                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
306                 if self.SubmitUserStats.GetValue():
307                         profile.putPreference('submit_slice_information', 'True')
308                 else:
309                         profile.putPreference('submit_slice_information', 'False')
310
311 class SelectParts(InfoPage):
312         def __init__(self, parent):
313                 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
314                 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."))
315                 self.AddSeperator()
316                 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
317                 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
318                 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
319                 self.AddSeperator()
320                 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."))
321                 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
322                 self.springExtruder.SetValue(True)
323
324         def StoreData(self):
325                 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
326                 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
327                 if self.dualExtrusion.GetValue():
328                         profile.putPreference('extruder_amount', '2')
329                         profile.putPreference('machine_depth', '195')
330                 else:
331                         profile.putPreference('extruder_amount', '1')
332                 if profile.getPreference('ultimaker_extruder_upgrade') == 'True':
333                         profile.putProfileSetting('retraction_enable', 'True')
334                 else:
335                         profile.putProfileSetting('retraction_enable', 'False')
336
337
338 class FirmwareUpgradePage(InfoPage):
339         def __init__(self, parent):
340                 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
341                 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."))
342                 self.AddHiddenSeperator()
343                 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
344                 self.AddHiddenSeperator()
345                 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."))
346                 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
347                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
348                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
349                 self.AddHiddenSeperator()
350                 self.AddText(_("Do not upgrade to this firmware if:"))
351                 self.AddText(_("* You have an older machine based on ATMega1280"))
352                 self.AddText(_("* Have other changes in the firmware"))
353 #               button = self.AddButton('Goto this page for a custom firmware')
354 #               button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
355
356         def AllowNext(self):
357                 return False
358
359         def OnUpgradeClick(self, e):
360                 if firmwareInstall.InstallFirmware():
361                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
362
363         def OnSkipClick(self, e):
364                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
365                 self.GetParent().ShowPage(self.GetNext())
366
367         def OnUrlClick(self, e):
368                 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
369
370
371 class UltimakerCheckupPage(InfoPage):
372         def __init__(self, parent):
373                 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
374
375                 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
376                 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
377                 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
378                 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
379                 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
380                 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
381                 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
382                 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
383                 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
384                 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
385
386                 self.AddText(
387                         _("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."))
388                 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
389                 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
390                 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
391                 self.AddSeperator()
392                 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
393                 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
394                 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
395                 self.AddSeperator()
396                 self.infoBox = self.AddInfoBox()
397                 self.machineState = self.AddText("")
398                 self.temperatureLabel = self.AddText("")
399                 self.errorLogButton = self.AddButton(_("Show error log"))
400                 self.errorLogButton.Show(False)
401                 self.AddSeperator()
402                 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
403                 self.comm = None
404                 self.xMinStop = False
405                 self.xMaxStop = False
406                 self.yMinStop = False
407                 self.yMaxStop = False
408                 self.zMinStop = False
409                 self.zMaxStop = False
410
411                 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
412
413         def __del__(self):
414                 if self.comm is not None:
415                         self.comm.close()
416
417         def AllowNext(self):
418                 self.endstopBitmap.Show(False)
419                 return False
420
421         def OnSkipClick(self, e):
422                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
423                 self.GetParent().ShowPage(self.GetNext())
424
425         def OnCheckClick(self, e=None):
426                 self.errorLogButton.Show(False)
427                 if self.comm is not None:
428                         self.comm.close()
429                         del self.comm
430                         self.comm = None
431                         wx.CallAfter(self.OnCheckClick)
432                         return
433                 self.infoBox.SetBusy(_("Connecting to machine."))
434                 self.commState.SetBitmap(self.unknownBitmap)
435                 self.tempState.SetBitmap(self.unknownBitmap)
436                 self.stopState.SetBitmap(self.unknownBitmap)
437                 self.checkupState = 0
438                 self.checkExtruderNr = 0
439                 self.comm = machineCom.MachineCom(callbackObject=self)
440
441         def OnErrorLog(self, e):
442                 printWindow.LogWindow('\n'.join(self.comm.getLog()))
443
444         def mcLog(self, message):
445                 pass
446
447         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
448                 if not self.comm.isOperational():
449                         return
450                 if self.checkupState == 0:
451                         self.tempCheckTimeout = 20
452                         if temp[self.checkExtruderNr] > 70:
453                                 self.checkupState = 1
454                                 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
455                                 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
456                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
457                         else:
458                                 self.startTemp = temp[self.checkExtruderNr]
459                                 self.checkupState = 2
460                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
461                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
462                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
463                 elif self.checkupState == 1:
464                         if temp < 60:
465                                 self.startTemp = temp[self.checkExtruderNr]
466                                 self.checkupState = 2
467                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
468                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
469                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
470                 elif self.checkupState == 2:
471                         #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
472                         if temp[self.checkExtruderNr] > self.startTemp + 40:
473                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
474                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
475                                 if self.checkExtruderNr < int(profile.getPreference('extruder_amount')):
476                                         self.checkExtruderNr = 0
477                                         self.checkupState = 3
478                                         wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
479                                         wx.CallAfter(self.endstopBitmap.Show, True)
480                                         wx.CallAfter(self.Layout)
481                                         self.comm.sendCommand('M119')
482                                         wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
483                                 else:
484                                         self.checkupState = 0
485                                         self.checkExtruderNr += 1
486                         else:
487                                 self.tempCheckTimeout -= 1
488                                 if self.tempCheckTimeout < 1:
489                                         self.checkupState = -1
490                                         wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
491                                         wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
492                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
493                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
494                 elif self.checkupState >= 3 and self.checkupState < 10:
495                         self.comm.sendCommand('M119')
496                 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
497
498         def mcStateChange(self, state):
499                 if self.comm is None:
500                         return
501                 if self.comm.isOperational():
502                         wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
503                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
504                 elif self.comm.isError():
505                         wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
506                         wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
507                         wx.CallAfter(self.endstopBitmap.Show, False)
508                         wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
509                         wx.CallAfter(self.errorLogButton.Show, True)
510                         wx.CallAfter(self.Layout)
511                 else:
512                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
513
514         def mcMessage(self, message):
515                 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
516                         for data in message.split(' '):
517                                 if ':' in data:
518                                         tag, value = data.split(':', 1)
519                                         if tag == 'x_min':
520                                                 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
521                                         if tag == 'x_max':
522                                                 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
523                                         if tag == 'y_min':
524                                                 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
525                                         if tag == 'y_max':
526                                                 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
527                                         if tag == 'z_min':
528                                                 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
529                                         if tag == 'z_max':
530                                                 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
531                         if ':' in message:
532                                 tag, value = map(str.strip, message.split(':', 1))
533                                 if tag == 'x_min':
534                                         self.xMinStop = (value == 'H' or value == 'TRIGGERED')
535                                 if tag == 'x_max':
536                                         self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
537                                 if tag == 'y_min':
538                                         self.yMinStop = (value == 'H' or value == 'TRIGGERED')
539                                 if tag == 'y_max':
540                                         self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
541                                 if tag == 'z_min':
542                                         self.zMinStop = (value == 'H' or value == 'TRIGGERED')
543                                 if tag == 'z_max':
544                                         self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
545                         if 'z_max' in message:
546                                 self.comm.sendCommand('M119')
547
548                         if self.checkupState == 3:
549                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
550                                         self.checkupState = 4
551                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
552                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
553                         elif self.checkupState == 4:
554                                 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
555                                         self.checkupState = 5
556                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
557                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
558                         elif self.checkupState == 5:
559                                 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
560                                         self.checkupState = 6
561                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
562                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
563                         elif self.checkupState == 6:
564                                 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
565                                         self.checkupState = 7
566                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
567                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
568                         elif self.checkupState == 7:
569                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
570                                         self.checkupState = 8
571                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
572                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
573                         elif self.checkupState == 8:
574                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
575                                         self.checkupState = 9
576                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
577                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
578                         elif self.checkupState == 9:
579                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
580                                         self.checkupState = 10
581                                         self.comm.close()
582                                         wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
583                                         wx.CallAfter(self.infoBox.SetReadyIndicator)
584                                         wx.CallAfter(self.endstopBitmap.Show, False)
585                                         wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
586                                         wx.CallAfter(self.OnSkipClick, None)
587
588         def mcProgress(self, lineNr):
589                 pass
590
591         def mcZChange(self, newZ):
592                 pass
593
594
595 class UltimakerCalibrationPage(InfoPage):
596         def __init__(self, parent):
597                 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
598
599                 self.AddText("Your Ultimaker requires some calibration.")
600                 self.AddText("This calibration is needed for a proper extrusion amount.")
601                 self.AddSeperator()
602                 self.AddText("The following values are needed:")
603                 self.AddText("* Diameter of filament")
604                 self.AddText("* Number of steps per mm of filament extrusion")
605                 self.AddSeperator()
606                 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
607                 self.AddSeperator()
608                 self.AddText("First we need the diameter of your filament:")
609                 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
610                 self.AddText(
611                         "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.")
612                 self.AddText("Note: This value can be changed later at any time.")
613
614         def StoreData(self):
615                 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
616
617
618 class UltimakerCalibrateStepsPerEPage(InfoPage):
619         def __init__(self, parent):
620                 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
621
622                 #if profile.getPreference('steps_per_e') == '0':
623                 #       profile.putPreference('steps_per_e', '865.888')
624
625                 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
626                 self.AddText(_("First remove any filament from your machine."))
627                 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
628                 self.AddText(_("We'll push the filament 100mm"))
629                 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
630                 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
631                 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
632                 self.AddText(_("This results in the following steps per E:"))
633                 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
634                 self.AddText(_("You can repeat these steps to get better calibration."))
635                 self.AddSeperator()
636                 self.AddText(
637                         _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
638                 self.heatButton = self.AddButton(_("Heatup for filament removal"))
639
640                 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
641                 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
642                 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
643
644         def OnSaveLengthClick(self, e):
645                 currentEValue = float(self.stepsPerEInput.GetValue())
646                 realExtrudeLength = float(self.lengthInput.GetValue())
647                 newEValue = currentEValue * 100 / realExtrudeLength
648                 self.stepsPerEInput.SetValue(str(newEValue))
649                 self.lengthInput.SetValue("100")
650
651         def OnExtrudeClick(self, e):
652                 threading.Thread(target=self.OnExtrudeRun).start()
653
654         def OnExtrudeRun(self):
655                 self.heatButton.Enable(False)
656                 self.extrudeButton.Enable(False)
657                 currentEValue = float(self.stepsPerEInput.GetValue())
658                 self.comm = machineCom.MachineCom()
659                 if not self.comm.isOpen():
660                         wx.MessageBox(
661                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
662                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
663                         self.heatButton.Enable(True)
664                         self.extrudeButton.Enable(True)
665                         return
666                 while True:
667                         line = self.comm.readline()
668                         if line == '':
669                                 return
670                         if 'start' in line:
671                                 break
672                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
673                 time.sleep(3)
674
675                 self.sendGCommand('M302') #Disable cold extrusion protection
676                 self.sendGCommand("M92 E%f" % (currentEValue))
677                 self.sendGCommand("G92 E0")
678                 self.sendGCommand("G1 E100 F600")
679                 time.sleep(15)
680                 self.comm.close()
681                 self.extrudeButton.Enable()
682                 self.heatButton.Enable()
683
684         def OnHeatClick(self, e):
685                 threading.Thread(target=self.OnHeatRun).start()
686
687         def OnHeatRun(self):
688                 self.heatButton.Enable(False)
689                 self.extrudeButton.Enable(False)
690                 self.comm = machineCom.MachineCom()
691                 if not self.comm.isOpen():
692                         wx.MessageBox(
693                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
694                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
695                         self.heatButton.Enable(True)
696                         self.extrudeButton.Enable(True)
697                         return
698                 while True:
699                         line = self.comm.readline()
700                         if line == '':
701                                 self.heatButton.Enable(True)
702                                 self.extrudeButton.Enable(True)
703                                 return
704                         if 'start' in line:
705                                 break
706                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
707                 time.sleep(3)
708
709                 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
710                 wx.MessageBox(
711                         'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
712                         'Machine heatup', wx.OK | wx.ICON_INFORMATION)
713                 self.sendGCommand('M104 S0')
714                 time.sleep(1)
715                 self.comm.close()
716                 self.heatButton.Enable(True)
717                 self.extrudeButton.Enable(True)
718
719         def sendGCommand(self, cmd):
720                 self.comm.sendCommand(cmd) #Disable cold extrusion protection
721                 while True:
722                         line = self.comm.readline()
723                         if line == '':
724                                 return
725                         if line.startswith('ok'):
726                                 break
727
728         def StoreData(self):
729                 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
730
731
732 class configWizard(wx.wizard.Wizard):
733         def __init__(self):
734                 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
735
736                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
737                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
738
739                 self.firstInfoPage = FirstInfoPage(self)
740                 self.machineSelectPage = MachineSelectPage(self)
741                 self.ultimakerSelectParts = SelectParts(self)
742                 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
743                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
744                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
745                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
746                 self.bedLevelPage = bedLevelWizardMain(self)
747                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
748                 self.repRapInfoPage = RepRapInfoPage(self)
749
750                 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
751                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
752                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
753                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
754                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
755                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
756
757                 self.FitToPage(self.firstInfoPage)
758                 self.GetPageAreaSizer().Add(self.firstInfoPage)
759
760                 self.RunWizard(self.firstInfoPage)
761                 self.Destroy()
762
763         def OnPageChanging(self, e):
764                 e.GetPage().StoreData()
765
766         def OnPageChanged(self, e):
767                 if e.GetPage().AllowNext():
768                         self.FindWindowById(wx.ID_FORWARD).Enable()
769                 else:
770                         self.FindWindowById(wx.ID_FORWARD).Disable()
771                 self.FindWindowById(wx.ID_BACKWARD).Disable()
772
773 class bedLevelWizardMain(InfoPage):
774         def __init__(self, parent):
775                 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
776
777                 self.AddText('This wizard will help you in leveling your printer bed')
778                 self.AddSeperator()
779                 self.AddText('It will do the following steps')
780                 self.AddText('* Move the printer head to each corner')
781                 self.AddText('  and let you adjust the height of the bed to the nozzle')
782                 self.AddText('* Print a line around the bed to check if it is level')
783                 self.AddSeperator()
784
785                 self.connectButton = self.AddButton('Connect to printer')
786                 self.comm = None
787
788                 self.infoBox = self.AddInfoBox()
789                 self.resumeButton = self.AddButton('Resume')
790                 self.resumeButton.Enable(False)
791
792                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
793                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
794
795         def OnConnect(self, e = None):
796                 if self.comm is not None:
797                         self.comm.close()
798                         del self.comm
799                         self.comm = None
800                         wx.CallAfter(self.OnConnect)
801                         return
802                 self.connectButton.Enable(False)
803                 self.comm = machineCom.MachineCom(callbackObject=self)
804                 self.infoBox.SetBusy('Connecting to machine.')
805                 self._wizardState = 0
806
807         def AllowNext(self):
808                 if self.GetParent().headOffsetCalibration is not None and int(profile.getPreference('extruder_amount')) > 1:
809                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
810                 return True
811
812         def OnResume(self, e):
813                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
814                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
815                 if self._wizardState == 2:
816                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
817                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
818                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
819                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
820                         self.comm.sendCommand('M400')
821                         self._wizardState = 3
822                 elif self._wizardState == 4:
823                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
824                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
825                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width') - 5.0, profile.getPreferenceFloat('machine_depth') - 25, feedTravel))
826                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
827                         self.comm.sendCommand('M400')
828                         self._wizardState = 5
829                 elif self._wizardState == 6:
830                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
831                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
832                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width') - 5.0, 20, feedTravel))
833                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
834                         self.comm.sendCommand('M400')
835                         self._wizardState = 7
836                 elif self._wizardState == 8:
837                         wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
838                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
839                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
840                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
841                         self._wizardState = 9
842                 elif self._wizardState == 10:
843                         self._wizardState = 11
844                         wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
845                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
846                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
847                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
848                         w = profile.getPreferenceFloat('machine_width')
849                         d = profile.getPreferenceFloat('machine_depth')
850                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
851                         filamentArea = math.pi * filamentRadius * filamentRadius
852                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
853                         eValue = 0.0
854
855                         gcodeList = [
856                                 'G1 Z2 F%d' % (feedZ),
857                                 'G92 E0',
858                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
859                                 'G1 Z0.3 F%d' % (feedZ)]
860                         eValue += 5.0
861                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
862
863                         for i in xrange(0, 3):
864                                 dist = 5.0 + 0.4 * float(i)
865                                 eValue += (d - 2.0*dist) * ePerMM
866                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
867                                 eValue += (w - 2.0*dist) * ePerMM
868                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
869                                 eValue += (d - 2.0*dist) * ePerMM
870                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
871                                 eValue += (w - 2.0*dist) * ePerMM
872                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
873
874                         gcodeList.append('M400')
875                         self.comm.printGCode(gcodeList)
876                 self.resumeButton.Enable(False)
877
878         def mcLog(self, message):
879                 print 'Log:', message
880
881         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
882                 if self._wizardState == 1:
883                         self._wizardState = 2
884                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
885                         wx.CallAfter(self.resumeButton.Enable, True)
886                 elif self._wizardState == 3:
887                         self._wizardState = 4
888                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
889                         wx.CallAfter(self.resumeButton.Enable, True)
890                 elif self._wizardState == 5:
891                         self._wizardState = 6
892                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
893                         wx.CallAfter(self.resumeButton.Enable, True)
894                 elif self._wizardState == 7:
895                         self._wizardState = 8
896                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
897                         wx.CallAfter(self.resumeButton.Enable, True)
898                 elif self._wizardState == 9:
899                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
900                                 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
901                         else:
902                                 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
903                                 wx.CallAfter(self.resumeButton.Enable, True)
904                                 self._wizardState = 10
905
906         def mcStateChange(self, state):
907                 if self.comm is None:
908                         return
909                 if self.comm.isOperational():
910                         if self._wizardState == 0:
911                                 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
912                                 self.comm.sendCommand('M105')
913                                 self.comm.sendCommand('G28')
914                                 self._wizardState = 1
915                         elif self._wizardState == 11 and not self.comm.isPrinting():
916                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
917                                 self.comm.sendCommand('G92 E0')
918                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
919                                 self.comm.sendCommand('M104 S0')
920                                 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
921                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
922                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
923                                 wx.CallAfter(self.connectButton.Enable, True)
924                                 self._wizardState = 12
925                 elif self.comm.isError():
926                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
927
928         def mcMessage(self, message):
929                 pass
930
931         def mcProgress(self, lineNr):
932                 pass
933
934         def mcZChange(self, newZ):
935                 pass
936
937 class headOffsetCalibrationPage(InfoPage):
938         def __init__(self, parent):
939                 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
940
941                 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
942                 self.AddSeperator()
943
944                 self.connectButton = self.AddButton('Connect to printer')
945                 self.comm = None
946
947                 self.infoBox = self.AddInfoBox()
948                 self.textEntry = self.AddTextCtrl('')
949                 self.textEntry.Enable(False)
950                 self.resumeButton = self.AddButton('Resume')
951                 self.resumeButton.Enable(False)
952
953                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
954                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
955
956         def OnConnect(self, e = None):
957                 if self.comm is not None:
958                         self.comm.close()
959                         del self.comm
960                         self.comm = None
961                         wx.CallAfter(self.OnConnect)
962                         return
963                 self.connectButton.Enable(False)
964                 self.comm = machineCom.MachineCom(callbackObject=self)
965                 self.infoBox.SetBusy('Connecting to machine.')
966                 self._wizardState = 0
967
968         def OnResume(self, e):
969                 if self._wizardState == 2:
970                         self._wizardState = 3
971                         wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
972
973                         w = profile.getPreferenceFloat('machine_width')
974                         d = profile.getPreferenceFloat('machine_depth')
975
976                         gcode = gcodeGenerator.gcodeGenerator()
977                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
978                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
979                         gcode.addCmd('T0')
980                         gcode.addPrime(15)
981                         gcode.addCmd('T1')
982                         gcode.addPrime(15)
983
984                         gcode.addCmd('T0')
985                         gcode.addMove(w/2, 5)
986                         gcode.addMove(z=0.2)
987                         gcode.addPrime()
988                         gcode.addExtrude(w/2, d-5.0)
989                         gcode.addRetract()
990                         gcode.addMove(5, d/2)
991                         gcode.addPrime()
992                         gcode.addExtrude(w-5.0, d/2)
993                         gcode.addRetract(15)
994
995                         gcode.addCmd('T1')
996                         gcode.addMove(w/2, 5)
997                         gcode.addPrime()
998                         gcode.addExtrude(w/2, d-5.0)
999                         gcode.addRetract()
1000                         gcode.addMove(5, d/2)
1001                         gcode.addPrime()
1002                         gcode.addExtrude(w-5.0, d/2)
1003                         gcode.addRetract(15)
1004                         gcode.addCmd('T0')
1005
1006                         gcode.addMove(z=25)
1007                         gcode.addMove(0, 0)
1008                         gcode.addCmd('M400')
1009
1010                         self.comm.printGCode(gcode.list())
1011                         self.resumeButton.Enable(False)
1012                 elif self._wizardState == 4:
1013                         try:
1014                                 float(self.textEntry.GetValue())
1015                         except ValueError:
1016                                 return
1017                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1018                         self._wizardState = 5
1019                         self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1020                         self.textEntry.SetValue('0.0')
1021                         self.textEntry.Enable(True)
1022                 elif self._wizardState == 5:
1023                         try:
1024                                 float(self.textEntry.GetValue())
1025                         except ValueError:
1026                                 return
1027                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1028                         self._wizardState = 6
1029                         self.infoBox.SetBusy('Printing the fine calibration lines.')
1030                         self.textEntry.SetValue('')
1031                         self.textEntry.Enable(False)
1032                         self.resumeButton.Enable(False)
1033
1034                         x = profile.getPreferenceFloat('extruder_offset_x1')
1035                         y = profile.getPreferenceFloat('extruder_offset_y1')
1036                         gcode = gcodeGenerator.gcodeGenerator()
1037                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1038                         gcode.setPrintSpeed(25)
1039                         gcode.addHome()
1040                         gcode.addCmd('T0')
1041                         gcode.addMove(50, 40, 0.2)
1042                         gcode.addPrime(15)
1043                         for n in xrange(0, 10):
1044                                 gcode.addExtrude(50 + n * 10, 150)
1045                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1046                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1047                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1048                         gcode.addMove(40, 50)
1049                         for n in xrange(0, 10):
1050                                 gcode.addExtrude(150, 50 + n * 10)
1051                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1052                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1053                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1054                         gcode.addRetract(15)
1055
1056                         gcode.addCmd('T1')
1057                         gcode.addMove(50 - x, 30 - y, 0.2)
1058                         gcode.addPrime(15)
1059                         for n in xrange(0, 10):
1060                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1061                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1062                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1063                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1064                         gcode.addMove(30 - x, 50 - y, 0.2)
1065                         for n in xrange(0, 10):
1066                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1067                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1068                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1069                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1070                         gcode.addRetract(15)
1071                         gcode.addMove(z=15)
1072                         gcode.addCmd('M400')
1073                         gcode.addCmd('M104 T0 S0')
1074                         gcode.addCmd('M104 T1 S0')
1075                         self.comm.printGCode(gcode.list())
1076                 elif self._wizardState == 7:
1077                         try:
1078                                 n = int(self.textEntry.GetValue()) - 1
1079                         except:
1080                                 return
1081                         x = profile.getPreferenceFloat('extruder_offset_x1')
1082                         x += -1.0 + n * 0.1
1083                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1084                         self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1085                         self.textEntry.SetValue('10')
1086                         self._wizardState = 8
1087                 elif self._wizardState == 8:
1088                         try:
1089                                 n = int(self.textEntry.GetValue()) - 1
1090                         except:
1091                                 return
1092                         y = profile.getPreferenceFloat('extruder_offset_y1')
1093                         y += -1.0 + n * 0.1
1094                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1095                         self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1')))
1096                         self.infoBox.SetReadyIndicator()
1097                         self._wizardState = 8
1098                         self.comm.close()
1099                         self.resumeButton.Enable(False)
1100
1101         def mcLog(self, message):
1102                 print 'Log:', message
1103
1104         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1105                 if self._wizardState == 1:
1106                         if temp[0] >= 210 and temp[1] >= 210:
1107                                 self._wizardState = 2
1108                                 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1109                                 wx.CallAfter(self.resumeButton.Enable, True)
1110                                 wx.CallAfter(self.resumeButton.SetFocus)
1111
1112         def mcStateChange(self, state):
1113                 if self.comm is None:
1114                         return
1115                 if self.comm.isOperational():
1116                         if self._wizardState == 0:
1117                                 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1118                                 self.comm.sendCommand('M105')
1119                                 self.comm.sendCommand('M104 S220 T0')
1120                                 self.comm.sendCommand('M104 S220 T1')
1121                                 self.comm.sendCommand('G28')
1122                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1123                                 self._wizardState = 1
1124                         if not self.comm.isPrinting():
1125                                 if self._wizardState == 3:
1126                                         self._wizardState = 4
1127                                         wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1128                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1129                                         wx.CallAfter(self.textEntry.Enable, True)
1130                                         wx.CallAfter(self.resumeButton.Enable, True)
1131                                         wx.CallAfter(self.resumeButton.SetFocus)
1132                                 elif self._wizardState == 6:
1133                                         self._wizardState = 7
1134                                         wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1135                                         wx.CallAfter(self.textEntry.SetValue, '10')
1136                                         wx.CallAfter(self.textEntry.Enable, True)
1137                                         wx.CallAfter(self.resumeButton.Enable, True)
1138                                         wx.CallAfter(self.resumeButton.SetFocus)
1139
1140                 elif self.comm.isError():
1141                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1142
1143         def mcMessage(self, message):
1144                 pass
1145
1146         def mcProgress(self, lineNr):
1147                 pass
1148
1149         def mcZChange(self, newZ):
1150                 pass
1151
1152 class bedLevelWizard(wx.wizard.Wizard):
1153         def __init__(self):
1154                 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1155
1156                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1157                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1158
1159                 self.mainPage = bedLevelWizardMain(self)
1160                 self.headOffsetCalibration = None
1161
1162                 self.FitToPage(self.mainPage)
1163                 self.GetPageAreaSizer().Add(self.mainPage)
1164
1165                 self.RunWizard(self.mainPage)
1166                 self.Destroy()
1167
1168         def OnPageChanging(self, e):
1169                 e.GetPage().StoreData()
1170
1171         def OnPageChanged(self, e):
1172                 if e.GetPage().AllowNext():
1173                         self.FindWindowById(wx.ID_FORWARD).Enable()
1174                 else:
1175                         self.FindWindowById(wx.ID_FORWARD).Disable()
1176                 self.FindWindowById(wx.ID_BACKWARD).Disable()
1177
1178 class headOffsetWizard(wx.wizard.Wizard):
1179         def __init__(self):
1180                 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1181
1182                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1183                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1184
1185                 self.mainPage = headOffsetCalibrationPage(self)
1186
1187                 self.FitToPage(self.mainPage)
1188                 self.GetPageAreaSizer().Add(self.mainPage)
1189
1190                 self.RunWizard(self.mainPage)
1191                 self.Destroy()
1192
1193         def OnPageChanging(self, e):
1194                 e.GetPage().StoreData()
1195
1196         def OnPageChanged(self, e):
1197                 if e.GetPage().AllowNext():
1198                         self.FindWindowById(wx.ID_FORWARD).Enable()
1199                 else:
1200                         self.FindWindowById(wx.ID_FORWARD).Disable()
1201                 self.FindWindowById(wx.ID_BACKWARD).Disable()