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