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