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