chiark / gitweb /
Resizing lulzbot logo and changing logo for the toolhead page
[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 | wx.ALIGN_CENTER)
225                 self.GetSizer().Add(combo, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
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().lulzbotToolheadPage)
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().lulzbotToolheadPage)
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.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1021                 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
1022                 self.AddSeperator()
1023                 self.AddText(_('For more information about using Cura with your LulzBot'))
1024                 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
1025                 self.AddSeperator()
1026
1027 class LulzbotToolheadSelectPage(InfoPage):
1028         url='http://lulzbot.com/toolhead-identification'
1029
1030         def __init__(self, parent):
1031                 super(LulzbotToolheadSelectPage, self).__init__(parent, _("LulzBot Toolhead Selection"))
1032
1033                 self.mini_choices = [_('Standard'), _('Flexystruder')]
1034                 self.taz_choices = [_('Standard v1'),
1035                                            _('Standard v2 0.35 mm nozzle'), _('Standard v2 0.5 mm nozzle'),
1036                                            _('Flexystruder v1'), _('Flexystruder v2'),
1037                                            _('Dually v1'), _('Dually v2'),
1038                                            _('FlexyDually v1'), _('FlexyDually v2')]
1039                 self.description_map = {
1040                                 _('Standard'): _('This is the standard toolhead that comes with the Lulzbot Mini'),
1041                                 _('Flexystruder'): _('This is the Flexystruder for the Lulzbot Mini\nIt is used for printing Flexible materials'),
1042                                 _('Standard v1'): _('This is the standard toolhead that comes with the Lulzbot TAZ 1-2-3 and TAZ 4.\nIt uses the Budaschnozzle for the hotend'),
1043                                 _('Standard v2 0.35 mm nozzle'): _('This is the standard toolhead that comes with the Lulzbot TAZ 5.\nIt uses the Hexagon hotend and a 0.35 mm nozzle'),
1044                                 _('Standard v2 0.5 mm nozzle'): _('This is the standard toolhead that comes with the Lulzbot TAZ 5.\nIt uses the Hexagon hotend and a 0.5 mm nozzle'),
1045                                 _('Flexystruder v1'): _('It\'s the flexy!'),
1046                                 _('Flexystruder v2'): _('It\'s the flexy v2!'),
1047                                 _('Dually v1'): _('It\'s the dualy v1!'),
1048                                 _('Dually v2'): _('It\'s the dual v2!'),
1049                                 _('FlexyDually v1'): _('It\'s the flexy dually v1!'),
1050                                 _('FlexyDually v2'): _('It\'s the flexy dual v2!')
1051                 }
1052                 self.image_map = {
1053                                 _('Standard'): 'Lulzbot_Toolhead_Mini_Standard.jpg',
1054                                 _('Flexystruder'): 'Lulzbot_logo.png',
1055                                 _('Standard v1'): 'Lulzbot_logo.png',
1056                                 _('Standard v2 0.35 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1057                                 _('Standard v2 0.5 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1058                                 _('Flexystruder v1'): 'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg',
1059                                 _('Flexystruder v2'): 'Lulzbot_logo.png',
1060                                 _('Dually v1'): 'Lulzbot_Toolhead_TAZ_Dually_v1.jpg',
1061                                 _('Dually v2'): 'Lulzbot_logo.png',
1062                                 _('FlexyDually v1'): 'Lulzbot_logo.png',
1063                                 _('FlexyDually v2'): 'Lulzbot_logo.png'
1064                 }
1065                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('LulzBot_logo.png')))
1066                 printer_name = profile.getMachineSetting('machine_type')
1067                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_SHOWN, self.OnPageShown)
1068
1069                 self.AddText(_('Please select your currently installed Tool Head'))
1070                 txt = self.AddText(_('It is important to select the correct Tool head for your printer.\n' +
1071                                                          'Flashing the wrong firmware on your printer can cause damage to your printer and to your toolhead\n' +
1072                                                          'If you are not sure which toolhead you have, please refer to this webpage for more information: '))
1073                 txt.SetForegroundColour(wx.RED)
1074                 button = self.AddButton(self.url)
1075                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1076
1077                 self.AddSeperator()
1078                 self.combo = self.AddCombo(_('Currently installed Toolhead'), [''])
1079                 self.combo.SetEditable(False)
1080                 self.combo.Bind(wx.EVT_COMBOBOX, self.OnToolheadSelected)
1081                 self.description = self.AddText('\n\n')
1082                 self.description.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD))
1083                 self.image = self.AddBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[self.mini_choices[0]])))
1084
1085         def OnPageShown(self, e):
1086                 printer_name = profile.getMachineSetting('machine_type')
1087                 if printer_name == 'lulzbot_mini':
1088                         choices = self.mini_choices
1089                         default = 0
1090                 else:
1091                         choices = self.taz_choices
1092                         if printer_name == 'lulzbot_TAZ_4':
1093                                 default = 0
1094                         elif printer_name == 'lulzbot_TAZ_5':
1095                                 default = 1
1096                         else:
1097                                 default = 2
1098
1099                 self.combo.Clear()
1100                 self.combo.AppendItems(choices)
1101                 self.combo.SetValue(choices[default])
1102                 self.OnToolheadSelected(e)
1103
1104         def OnUrlClick(self, e):
1105                 webbrowser.open(LulzbotToolheadSelectPage.url)
1106
1107         def OnToolheadSelected(self, e):
1108                 toolhead = self.combo.GetValue()
1109                 if self.description_map.has_key(toolhead):
1110                         self.image.SetBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[toolhead])))
1111                         self.description.SetLabel(self.description_map[toolhead])
1112                 else:
1113                         self.image.SetBitmap(wx.NullBitmap)
1114                         self.description.SetLabel('\n\n')
1115                 self.Layout()
1116                 self.Fit()
1117
1118
1119 class Taz5NozzleSelectPage(InfoPage):
1120         url='http://lulzbot.com/printer-identification'
1121
1122         def __init__(self, parent):
1123                 super(Taz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ5"))
1124                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1125
1126                 self.AddText(_(' '))
1127                 self.AddText(_('Please select nozzle size:'))
1128                 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1129                 self.Nozzle35Radio.SetValue(True)
1130                 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1131                 self.AddText(_(' '))
1132                 self.AddSeperator()
1133
1134                 self.AddText(_('If you are not sure which nozzle size you have'))
1135                 self.AddText(_('please check this webpage: '))
1136                 button = self.AddButton(Taz5NozzleSelectPage.url)
1137                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1138
1139         def OnUrlClick(self, e):
1140                 webbrowser.open(Taz5NozzleSelectPage.url)
1141
1142         def StoreData(self):
1143                 if self.Nozzle35Radio.GetValue():
1144                         profile.putProfileSetting('nozzle_size', '0.35')
1145                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.35 nozzle)')
1146                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1147
1148                 else:
1149                         profile.putProfileSetting('nozzle_size', '0.5')
1150                         profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.5 nozzle)')
1151                         profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_05nozzle')
1152
1153 class ConfigWizard(wx.wizard.Wizard):
1154         def __init__(self, addNew = False):
1155                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1156
1157                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1158                 if addNew:
1159                         profile.setActiveMachine(profile.getMachineCount())
1160
1161                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1162                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1163                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1164
1165                 self.machineSelectPage = MachineSelectPage(self)
1166                 self.ultimakerSelectParts = SelectParts(self)
1167                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1168                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1169                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1170                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1171                 self.bedLevelPage = bedLevelWizardMain(self)
1172                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1173                 self.printrbotSelectType = PrintrbotPage(self)
1174                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1175                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1176                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1177
1178                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1179                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1180                 self.lulzbotToolheadPage = LulzbotToolheadSelectPage(self)
1181                 self.taz5NozzleSelectPage = Taz5NozzleSelectPage(self)
1182
1183                 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
1184                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1185                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1186                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1187                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1188                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
1189                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1190                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1191                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.lulzbotToolheadPage)
1192                 wx.wizard.WizardPageSimple.Chain(self.lulzbotToolheadPage, self.lulzbotReadyPage)
1193
1194                 self.RunWizard(self.machineSelectPage)
1195                 self.Destroy()
1196
1197         def OnPageChanging(self, e):
1198                 e.GetPage().StoreData()
1199
1200         def OnPageChanged(self, e):
1201                 if e.GetPage().AllowNext():
1202                         self.FindWindowById(wx.ID_FORWARD).Enable()
1203                 else:
1204                         self.FindWindowById(wx.ID_FORWARD).Disable()
1205                 if e.GetPage().AllowBack():
1206                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1207                 else:
1208                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1209
1210         def OnCancel(self, e):
1211                 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1212                 profile.setActiveMachine(self._old_machine_index)
1213                 profile.removeMachine(new_machine_index)
1214
1215 class bedLevelWizardMain(InfoPage):
1216         def __init__(self, parent):
1217                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1218
1219                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1220                 self.AddSeperator()
1221                 self.AddText(_('It will do the following steps'))
1222                 self.AddText(_('* Move the printer head to each corner'))
1223                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1224                 self.AddText(_('* Print a line around the bed to check if it is level'))
1225                 self.AddSeperator()
1226
1227                 self.connectButton = self.AddButton(_('Connect to printer'))
1228                 self.comm = None
1229
1230                 self.infoBox = self.AddInfoBox()
1231                 self.resumeButton = self.AddButton(_('Resume'))
1232                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1233                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1234                 self.resumeButton.Enable(False)
1235
1236                 self.upButton.Enable(False)
1237                 self.downButton.Enable(False)
1238                 self.upButton2.Enable(False)
1239                 self.downButton2.Enable(False)
1240
1241                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1242                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1243                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1244                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1245                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1246                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1247
1248         def OnConnect(self, e = None):
1249                 if self.comm is not None:
1250                         self.comm.close()
1251                         del self.comm
1252                         self.comm = None
1253                         wx.CallAfter(self.OnConnect)
1254                         return
1255                 self.connectButton.Enable(False)
1256                 self.comm = machineCom.MachineCom(callbackObject=self)
1257                 self.infoBox.SetBusy(_('Connecting to machine.'))
1258                 self._wizardState = 0
1259
1260         def OnBedUp(self, e):
1261                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1262                 self.comm.sendCommand('G92 Z10')
1263                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1264                 self.comm.sendCommand('M400')
1265
1266         def OnBedDown(self, e):
1267                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1268                 self.comm.sendCommand('G92 Z10')
1269                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1270                 self.comm.sendCommand('M400')
1271
1272         def OnBedUp2(self, e):
1273                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1274                 self.comm.sendCommand('G92 Z10')
1275                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1276                 self.comm.sendCommand('M400')
1277
1278         def OnBedDown2(self, e):
1279                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1280                 self.comm.sendCommand('G92 Z10')
1281                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1282                 self.comm.sendCommand('M400')
1283
1284         def AllowNext(self):
1285                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1286                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1287                 return True
1288
1289         def OnResume(self, e):
1290                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1291                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1292                 if self._wizardState == -1:
1293                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1294                         wx.CallAfter(self.upButton.Enable, False)
1295                         wx.CallAfter(self.downButton.Enable, False)
1296                         wx.CallAfter(self.upButton2.Enable, False)
1297                         wx.CallAfter(self.downButton2.Enable, False)
1298                         self.comm.sendCommand('M105')
1299                         self.comm.sendCommand('G28')
1300                         self._wizardState = 1
1301                 elif self._wizardState == 2:
1302                         if profile.getMachineSetting('has_heated_bed') == 'True':
1303                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1304                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1305                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1306                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1307                                 self.comm.sendCommand('M400')
1308                                 self._wizardState = 3
1309                         else:
1310                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1311                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1312                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1313                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1314                                 self.comm.sendCommand('M400')
1315                                 self._wizardState = 3
1316                 elif self._wizardState == 4:
1317                         if profile.getMachineSetting('has_heated_bed') == 'True':
1318                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1319                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1320                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1321                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1322                                 self.comm.sendCommand('M400')
1323                                 self._wizardState = 7
1324                         else:
1325                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1326                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1327                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1328                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1329                                 self.comm.sendCommand('M400')
1330                                 self._wizardState = 5
1331                 elif self._wizardState == 6:
1332                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1333                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1334                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1335                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1336                         self.comm.sendCommand('M400')
1337                         self._wizardState = 7
1338                 elif self._wizardState == 8:
1339                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1340                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1341                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1342                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1343                         self._wizardState = 9
1344                 elif self._wizardState == 10:
1345                         self._wizardState = 11
1346                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1347                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1348                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1349                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1350                         w = profile.getMachineSettingFloat('machine_width') - 10
1351                         d = profile.getMachineSettingFloat('machine_depth')
1352                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1353                         filamentArea = math.pi * filamentRadius * filamentRadius
1354                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1355                         eValue = 0.0
1356
1357                         gcodeList = [
1358                                 'G1 Z2 F%d' % (feedZ),
1359                                 'G92 E0',
1360                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1361                                 'G1 Z0.3 F%d' % (feedZ)]
1362                         eValue += 5.0
1363                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1364
1365                         for i in xrange(0, 3):
1366                                 dist = 5.0 + 0.4 * float(i)
1367                                 eValue += (d - 2.0*dist) * ePerMM
1368                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1369                                 eValue += (w - 2.0*dist) * ePerMM
1370                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1371                                 eValue += (d - 2.0*dist) * ePerMM
1372                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1373                                 eValue += (w - 2.0*dist) * ePerMM
1374                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1375
1376                         gcodeList.append('M400')
1377                         self.comm.printGCode(gcodeList)
1378                 self.resumeButton.Enable(False)
1379
1380         def mcLog(self, message):
1381                 print 'Log:', message
1382
1383         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1384                 if self._wizardState == 1:
1385                         self._wizardState = 2
1386                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1387                         wx.CallAfter(self.resumeButton.Enable, True)
1388                 elif self._wizardState == 3:
1389                         self._wizardState = 4
1390                         if profile.getMachineSetting('has_heated_bed') == 'True':
1391                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1392                         else:
1393                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1394                         wx.CallAfter(self.resumeButton.Enable, True)
1395                 elif self._wizardState == 5:
1396                         self._wizardState = 6
1397                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1398                         wx.CallAfter(self.resumeButton.Enable, True)
1399                 elif self._wizardState == 7:
1400                         self._wizardState = 8
1401                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1402                         wx.CallAfter(self.resumeButton.Enable, True)
1403                 elif self._wizardState == 9:
1404                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1405                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1406                         else:
1407                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1408                                 wx.CallAfter(self.resumeButton.Enable, True)
1409                                 self._wizardState = 10
1410
1411         def mcStateChange(self, state):
1412                 if self.comm is None:
1413                         return
1414                 if self.comm.isOperational():
1415                         if self._wizardState == 0:
1416                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1417                                 wx.CallAfter(self.upButton.Enable, True)
1418                                 wx.CallAfter(self.downButton.Enable, True)
1419                                 wx.CallAfter(self.upButton2.Enable, True)
1420                                 wx.CallAfter(self.downButton2.Enable, True)
1421                                 wx.CallAfter(self.resumeButton.Enable, True)
1422                                 self._wizardState = -1
1423                         elif self._wizardState == 11 and not self.comm.isPrinting():
1424                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1425                                 self.comm.sendCommand('G92 E0')
1426                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1427                                 self.comm.sendCommand('M104 S0')
1428                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1429                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1430                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1431                                 wx.CallAfter(self.connectButton.Enable, True)
1432                                 self._wizardState = 12
1433                 elif self.comm.isError():
1434                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1435
1436         def mcMessage(self, message):
1437                 pass
1438
1439         def mcProgress(self, lineNr):
1440                 pass
1441
1442         def mcZChange(self, newZ):
1443                 pass
1444
1445 class headOffsetCalibrationPage(InfoPage):
1446         def __init__(self, parent):
1447                 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1448
1449                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1450                 self.AddSeperator()
1451
1452                 self.connectButton = self.AddButton(_('Connect to printer'))
1453                 self.comm = None
1454
1455                 self.infoBox = self.AddInfoBox()
1456                 self.textEntry = self.AddTextCtrl('')
1457                 self.textEntry.Enable(False)
1458                 self.resumeButton = self.AddButton(_('Resume'))
1459                 self.resumeButton.Enable(False)
1460
1461                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1462                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1463
1464         def AllowBack(self):
1465                 return True
1466
1467         def OnConnect(self, e = None):
1468                 if self.comm is not None:
1469                         self.comm.close()
1470                         del self.comm
1471                         self.comm = None
1472                         wx.CallAfter(self.OnConnect)
1473                         return
1474                 self.connectButton.Enable(False)
1475                 self.comm = machineCom.MachineCom(callbackObject=self)
1476                 self.infoBox.SetBusy(_('Connecting to machine.'))
1477                 self._wizardState = 0
1478
1479         def OnResume(self, e):
1480                 if self._wizardState == 2:
1481                         self._wizardState = 3
1482                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1483
1484                         w = profile.getMachineSettingFloat('machine_width')
1485                         d = profile.getMachineSettingFloat('machine_depth')
1486
1487                         gcode = gcodeGenerator.gcodeGenerator()
1488                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1489                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1490                         gcode.addCmd('T0')
1491                         gcode.addPrime(15)
1492                         gcode.addCmd('T1')
1493                         gcode.addPrime(15)
1494
1495                         gcode.addCmd('T0')
1496                         gcode.addMove(w/2, 5)
1497                         gcode.addMove(z=0.2)
1498                         gcode.addPrime()
1499                         gcode.addExtrude(w/2, d-5.0)
1500                         gcode.addRetract()
1501                         gcode.addMove(5, d/2)
1502                         gcode.addPrime()
1503                         gcode.addExtrude(w-5.0, d/2)
1504                         gcode.addRetract(15)
1505
1506                         gcode.addCmd('T1')
1507                         gcode.addMove(w/2, 5)
1508                         gcode.addPrime()
1509                         gcode.addExtrude(w/2, d-5.0)
1510                         gcode.addRetract()
1511                         gcode.addMove(5, d/2)
1512                         gcode.addPrime()
1513                         gcode.addExtrude(w-5.0, d/2)
1514                         gcode.addRetract(15)
1515                         gcode.addCmd('T0')
1516
1517                         gcode.addMove(z=25)
1518                         gcode.addMove(0, 0)
1519                         gcode.addCmd('M400')
1520
1521                         self.comm.printGCode(gcode.list())
1522                         self.resumeButton.Enable(False)
1523                 elif self._wizardState == 4:
1524                         try:
1525                                 float(self.textEntry.GetValue())
1526                         except ValueError:
1527                                 return
1528                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1529                         self._wizardState = 5
1530                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1531                         self.textEntry.SetValue('0.0')
1532                         self.textEntry.Enable(True)
1533                 elif self._wizardState == 5:
1534                         try:
1535                                 float(self.textEntry.GetValue())
1536                         except ValueError:
1537                                 return
1538                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1539                         self._wizardState = 6
1540                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1541                         self.textEntry.SetValue('')
1542                         self.textEntry.Enable(False)
1543                         self.resumeButton.Enable(False)
1544
1545                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1546                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1547                         gcode = gcodeGenerator.gcodeGenerator()
1548                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1549                         gcode.setPrintSpeed(25)
1550                         gcode.addHome()
1551                         gcode.addCmd('T0')
1552                         gcode.addMove(50, 40, 0.2)
1553                         gcode.addPrime(15)
1554                         for n in xrange(0, 10):
1555                                 gcode.addExtrude(50 + n * 10, 150)
1556                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1557                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1558                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1559                         gcode.addMove(40, 50)
1560                         for n in xrange(0, 10):
1561                                 gcode.addExtrude(150, 50 + n * 10)
1562                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1563                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1564                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1565                         gcode.addRetract(15)
1566
1567                         gcode.addCmd('T1')
1568                         gcode.addMove(50 - x, 30 - y, 0.2)
1569                         gcode.addPrime(15)
1570                         for n in xrange(0, 10):
1571                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1572                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1573                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1574                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1575                         gcode.addMove(30 - x, 50 - y, 0.2)
1576                         for n in xrange(0, 10):
1577                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1578                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1579                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1580                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1581                         gcode.addRetract(15)
1582                         gcode.addMove(z=15)
1583                         gcode.addCmd('M400')
1584                         gcode.addCmd('M104 T0 S0')
1585                         gcode.addCmd('M104 T1 S0')
1586                         self.comm.printGCode(gcode.list())
1587                 elif self._wizardState == 7:
1588                         try:
1589                                 n = int(self.textEntry.GetValue()) - 1
1590                         except:
1591                                 return
1592                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1593                         x += -1.0 + n * 0.1
1594                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1595                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1596                         self.textEntry.SetValue('10')
1597                         self._wizardState = 8
1598                 elif self._wizardState == 8:
1599                         try:
1600                                 n = int(self.textEntry.GetValue()) - 1
1601                         except:
1602                                 return
1603                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1604                         y += -1.0 + n * 0.1
1605                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1606                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1607                         self.infoBox.SetReadyIndicator()
1608                         self._wizardState = 8
1609                         self.comm.close()
1610                         self.resumeButton.Enable(False)
1611
1612         def mcLog(self, message):
1613                 print 'Log:', message
1614
1615         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1616                 if self._wizardState == 1:
1617                         if temp[0] >= 210 and temp[1] >= 210:
1618                                 self._wizardState = 2
1619                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1620                                 wx.CallAfter(self.resumeButton.Enable, True)
1621                                 wx.CallAfter(self.resumeButton.SetFocus)
1622
1623         def mcStateChange(self, state):
1624                 if self.comm is None:
1625                         return
1626                 if self.comm.isOperational():
1627                         if self._wizardState == 0:
1628                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1629                                 self.comm.sendCommand('M105')
1630                                 self.comm.sendCommand('M104 S220 T0')
1631                                 self.comm.sendCommand('M104 S220 T1')
1632                                 self.comm.sendCommand('G28')
1633                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1634                                 self._wizardState = 1
1635                         if not self.comm.isPrinting():
1636                                 if self._wizardState == 3:
1637                                         self._wizardState = 4
1638                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1639                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1640                                         wx.CallAfter(self.textEntry.Enable, True)
1641                                         wx.CallAfter(self.resumeButton.Enable, True)
1642                                         wx.CallAfter(self.resumeButton.SetFocus)
1643                                 elif self._wizardState == 6:
1644                                         self._wizardState = 7
1645                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1646                                         wx.CallAfter(self.textEntry.SetValue, '10')
1647                                         wx.CallAfter(self.textEntry.Enable, True)
1648                                         wx.CallAfter(self.resumeButton.Enable, True)
1649                                         wx.CallAfter(self.resumeButton.SetFocus)
1650
1651                 elif self.comm.isError():
1652                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1653
1654         def mcMessage(self, message):
1655                 pass
1656
1657         def mcProgress(self, lineNr):
1658                 pass
1659
1660         def mcZChange(self, newZ):
1661                 pass
1662
1663 class bedLevelWizard(wx.wizard.Wizard):
1664         def __init__(self):
1665                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1666
1667                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1668                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1669
1670                 self.mainPage = bedLevelWizardMain(self)
1671                 self.headOffsetCalibration = None
1672
1673                 self.RunWizard(self.mainPage)
1674                 self.Destroy()
1675
1676         def OnPageChanging(self, e):
1677                 e.GetPage().StoreData()
1678
1679         def OnPageChanged(self, e):
1680                 if e.GetPage().AllowNext():
1681                         self.FindWindowById(wx.ID_FORWARD).Enable()
1682                 else:
1683                         self.FindWindowById(wx.ID_FORWARD).Disable()
1684                 if e.GetPage().AllowBack():
1685                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1686                 else:
1687                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1688
1689 class headOffsetWizard(wx.wizard.Wizard):
1690         def __init__(self):
1691                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1692
1693                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1694                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1695
1696                 self.mainPage = headOffsetCalibrationPage(self)
1697
1698                 self.RunWizard(self.mainPage)
1699                 self.Destroy()
1700
1701         def OnPageChanging(self, e):
1702                 e.GetPage().StoreData()
1703
1704         def OnPageChanged(self, e):
1705                 if e.GetPage().AllowNext():
1706                         self.FindWindowById(wx.ID_FORWARD).Enable()
1707                 else:
1708                         self.FindWindowById(wx.ID_FORWARD).Disable()
1709                 if e.GetPage().AllowBack():
1710                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1711                 else:
1712                         self.FindWindowById(wx.ID_BACKWARD).Disable()