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