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