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