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