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