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