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