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