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