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