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