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