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