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