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