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