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