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