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