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