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