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