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