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