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