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