1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
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
20 class InfoBox(wx.Panel):
21 def __init__(self, parent):
22 super(InfoBox, self).__init__(parent)
23 self.SetBackgroundColour('#FFFF80')
25 self.sizer = wx.GridBagSizer(5, 5)
26 self.SetSizer(self.sizer)
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'))
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'))
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)
46 self.extraInfoButton.Show(False)
48 self.extraInfoUrl = ''
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)
55 def SetInfo(self, info):
56 self.SetBackgroundColour('#FFFF80')
57 self.text.SetLabel(info)
58 self.extraInfoButton.Show(False)
61 def SetError(self, info, extraInfoUrl):
62 self.extraInfoUrl = extraInfoUrl
63 self.SetBackgroundColour('#FF8080')
64 self.text.SetLabel(info)
65 self.extraInfoButton.Show(True)
67 self.SetErrorIndicator()
70 def SetAttention(self, info):
71 self.SetBackgroundColour('#FFFF80')
72 self.text.SetLabel(info)
73 self.extraInfoButton.Show(False)
74 self.SetAttentionIndicator()
78 def SetBusy(self, info):
80 self.SetBusyIndicator()
82 def SetBusyIndicator(self):
84 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
86 def doExtraInfo(self, e):
87 webbrowser.open(self.extraInfoUrl)
89 def doBusyUpdate(self, e):
90 if self.busyState is None:
93 if self.busyState >= len(self.busyBitmap):
95 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
97 def SetReadyIndicator(self):
99 self.bitmap.SetBitmap(self.readyBitmap)
101 def SetErrorIndicator(self):
102 self.busyState = None
103 self.bitmap.SetBitmap(self.errorBitmap)
105 def SetAttentionIndicator(self):
106 self.busyState = None
107 self.bitmap.SetBitmap(self.attentionBitmap)
110 class InfoPage(wx.wizard.WizardPageSimple):
111 def __init__(self, parent, title):
112 wx.wizard.WizardPageSimple.__init__(self, parent)
114 sizer = wx.GridBagSizer(5, 5)
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)
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)
132 def AddSeperator(self):
133 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
136 def AddHiddenSeperator(self):
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)
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)
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)
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)
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))
172 return button1, button2
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)
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)
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)
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)
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)
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)
227 class PrintrbotPage(InfoPage):
228 def __init__(self, parent):
229 self._printer_info = {
230 # X, Y, Z, Filament Diameter, PrintTemperature, Print Speed, Travel Speed, Retract speed, Retract amount
231 "Original": (130, 130, 130, 2.95, 208, 40, 70, 30, 1),
232 "Simple Maker's Edition v1": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
233 "Simple Maker's Edition v2 (2013 Printrbot Simple)": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
234 "Simple Maker's Edition v3 (2014 Printrbot Simple)": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
235 "Simple Maker's Edition v4 (Model 1405)": (100, 100, 100, 1.75, 208, 40, 70, 30, 1),
236 "Simple Metal": (150, 150, 150, 1.75, 208, 40, 70, 30, 1),
237 "Jr v1": (150, 100, 80, 1.75, 208, 40, 70, 30, 1),
238 "Jr v2": (150, 150, 150, 1.75, 208, 40, 70, 30, 1),
239 "LC v2": (150, 150, 150, 1.75, 208, 40, 70, 30, 1),
240 "Plus v2": (200, 200, 200, 1.75, 208, 40, 70, 30, 1),
241 "Plus v2.1": (200, 200, 200, 1.75, 208, 40, 70, 30, 1),
242 "Plus v2.2 (Model 1404/140422)": (250, 250, 250, 1.75, 208, 40, 70, 30, 1),
243 "Plus v2.3 (Model 140501)": (250, 250, 250, 1.75, 208, 40, 70, 30, 1),
244 "Plus v2.4 (Model 140507)": (250, 250, 250, 1.75, 208, 40, 70, 30, 1),
247 super(PrintrbotPage, self).__init__(parent, _("Printrbot Selection"))
248 self.AddText(_("Select which Printrbot machine you have:"))
249 keys = self._printer_info.keys()
253 item = self.AddRadioButton(name)
254 item.data = self._printer_info[name]
255 self._items.append(item)
258 profile.putMachineSetting('machine_name', 'Printrbot ???')
259 for item in self._items:
262 profile.putMachineSetting('machine_name', 'Printrbot ' + item.GetLabel())
263 profile.putMachineSetting('machine_width', data[0])
264 profile.putMachineSetting('machine_depth', data[1])
265 profile.putMachineSetting('machine_height', data[2])
266 profile.putProfileSetting('nozzle_size', '0.5')
267 profile.putProfileSetting('filament_diameter', data[3])
268 profile.putProfileSetting('print_temperature', data[4])
269 profile.putProfileSetting('print_speed', data[5])
270 profile.putProfileSetting('travel_speed', data[6])
271 profile.putProfileSetting('retraction_speed', data[7])
272 profile.putProfileSetting('retraction_amount', data[8])
273 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
274 profile.putMachineSetting('has_heated_bed', 'False')
275 profile.putMachineSetting('machine_center_is_zero', 'False')
276 profile.putMachineSetting('extruder_head_size_min_x', '0')
277 profile.putMachineSetting('extruder_head_size_min_y', '0')
278 profile.putMachineSetting('extruder_head_size_max_x', '0')
279 profile.putMachineSetting('extruder_head_size_max_y', '0')
280 profile.putMachineSetting('extruder_head_size_height', '0')
282 class OtherMachineSelectPage(InfoPage):
283 def __init__(self, parent):
284 super(OtherMachineSelectPage, self).__init__(parent, _("Other machine information"))
285 self.AddText(_("The following pre-defined machine profiles are available"))
286 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."))
288 machines = resources.getDefaultMachineProfiles()
290 for filename in machines:
291 name = os.path.splitext(os.path.basename(filename))[0]
292 item = self.AddRadioButton(name)
293 item.filename = filename
294 item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
295 self.options.append(item)
297 item = self.AddRadioButton(_('Custom...'))
299 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
301 def OnProfileSelect(self, e):
302 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
304 def OnOtherSelect(self, e):
305 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
308 for option in self.options:
309 if option.GetValue():
310 profile.loadProfile(option.filename)
311 profile.loadMachineSettings(option.filename)
313 class OtherMachineInfoPage(InfoPage):
314 def __init__(self, parent):
315 super(OtherMachineInfoPage, self).__init__(parent, _("Cura Ready!"))
316 self.AddText(_("Cura is now ready to be used!"))
318 class CustomRepRapInfoPage(InfoPage):
319 def __init__(self, parent):
320 super(CustomRepRapInfoPage, self).__init__(parent, _("Custom RepRap information"))
321 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
322 self.AddText(_("Be sure to review the default profile before running it on your machine."))
323 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
325 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
327 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
328 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
329 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
330 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "55")
331 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
332 self.heatedBed = self.AddCheckbox(_("Heated bed"))
333 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
336 profile.putMachineSetting('machine_name', self.machineName.GetValue())
337 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
338 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
339 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
340 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
341 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
342 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
343 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
344 profile.putMachineSetting('extruder_head_size_min_x', '0')
345 profile.putMachineSetting('extruder_head_size_min_y', '0')
346 profile.putMachineSetting('extruder_head_size_max_x', '0')
347 profile.putMachineSetting('extruder_head_size_max_y', '0')
348 profile.putMachineSetting('extruder_head_size_height', '0')
349 profile.checkAndUpdateMachineName()
351 class MachineSelectPage(InfoPage):
352 def __init__(self, parent):
353 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
354 self.AddText(_("What kind of machine do you have:"))
356 self.LulzbotMiniRadio = self.AddRadioButton("LulzBot Mini", style=wx.RB_GROUP)
357 self.LulzbotMiniRadio.Bind(wx.EVT_RADIOBUTTON, self.OnLulzbotSelect)
358 self.LulzbotMiniRadio.SetValue(True)
359 self.LulzbotTazRadio = self.AddRadioButton("LulzBot TAZ")
360 self.LulzbotTazRadio.Bind(wx.EVT_RADIOBUTTON, self.OnLulzbotSelect)
361 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2")
362 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
363 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
364 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
365 self.UltimakerOPRadio = self.AddRadioButton("Ultimaker Original+")
366 self.UltimakerOPRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerOPSelect)
367 self.PrintrbotRadio = self.AddRadioButton("Printrbot")
368 self.PrintrbotRadio.Bind(wx.EVT_RADIOBUTTON, self.OnPrintrbotSelect)
369 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot, Witbox)"))
370 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
372 def OnUltimaker2Select(self, e):
373 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
375 def OnUltimakerSelect(self, e):
376 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
378 def OnUltimakerOPSelect(self, e):
379 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
381 def OnPrintrbotSelect(self, e):
382 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().printrbotSelectType)
384 def OnLulzbotSelect(self, e):
385 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
387 def OnOtherSelect(self, e):
388 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
391 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
395 profile.putProfileSetting('retraction_enable', 'True')
396 if self.Ultimaker2Radio.GetValue():
397 profile.putMachineSetting('machine_width', '230')
398 profile.putMachineSetting('machine_depth', '225')
399 profile.putMachineSetting('machine_height', '205')
400 profile.putMachineSetting('machine_name', 'ultimaker2')
401 profile.putMachineSetting('machine_type', 'ultimaker2')
402 profile.putMachineSetting('machine_center_is_zero', 'False')
403 profile.putMachineSetting('has_heated_bed', 'True')
404 profile.putMachineSetting('gcode_flavor', 'UltiGCode')
405 profile.putMachineSetting('extruder_head_size_min_x', '40.0')
406 profile.putMachineSetting('extruder_head_size_min_y', '10.0')
407 profile.putMachineSetting('extruder_head_size_max_x', '60.0')
408 profile.putMachineSetting('extruder_head_size_max_y', '30.0')
409 profile.putMachineSetting('extruder_head_size_height', '48.0')
410 profile.putProfileSetting('nozzle_size', '0.4')
411 profile.putProfileSetting('fan_full_height', '5.0')
412 profile.putMachineSetting('extruder_offset_x1', '18.0')
413 profile.putMachineSetting('extruder_offset_y1', '0.0')
414 elif self.UltimakerRadio.GetValue():
415 profile.putMachineSetting('machine_width', '205')
416 profile.putMachineSetting('machine_depth', '205')
417 profile.putMachineSetting('machine_height', '200')
418 profile.putMachineSetting('machine_name', 'ultimaker original')
419 profile.putMachineSetting('machine_type', 'ultimaker')
420 profile.putMachineSetting('machine_center_is_zero', 'False')
421 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
422 profile.putProfileSetting('nozzle_size', '0.4')
423 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
424 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
425 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
426 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
427 profile.putMachineSetting('extruder_head_size_height', '55.0')
428 elif self.UltimakerOPRadio.GetValue():
429 profile.putMachineSetting('machine_width', '205')
430 profile.putMachineSetting('machine_depth', '205')
431 profile.putMachineSetting('machine_height', '200')
432 profile.putMachineSetting('machine_name', 'ultimaker original+')
433 profile.putMachineSetting('machine_type', 'ultimaker_plus')
434 profile.putMachineSetting('machine_center_is_zero', 'False')
435 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
436 profile.putProfileSetting('nozzle_size', '0.4')
437 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
438 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
439 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
440 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
441 profile.putMachineSetting('extruder_head_size_height', '55.0')
442 profile.putMachineSetting('has_heated_bed', 'True')
443 profile.putMachineSetting('extruder_amount', '1')
444 profile.putProfileSetting('retraction_enable', 'True')
445 elif self.LulzbotTazRadio.GetValue() or self.LulzbotMiniRadio.GetValue():
446 if self.LulzbotTazRadio.GetValue():
447 profile.putMachineSetting('machine_width', '298')
448 profile.putMachineSetting('machine_depth', '275')
449 profile.putMachineSetting('machine_height', '250')
450 profile.putProfileSetting('nozzle_size', '0.35')
451 profile.putMachineSetting('machine_name', 'LulzBot TAZ')
452 profile.putMachineSetting('machine_type', 'lulzbot_TAZ')
453 profile.putMachineSetting('serial_baud', '115200')
455 profile.putMachineSetting('machine_width', '155')
456 profile.putMachineSetting('machine_depth', '155')
457 profile.putMachineSetting('machine_height', '163')
458 profile.putProfileSetting('nozzle_size', '0.5')
459 profile.putMachineSetting('machine_name', 'LulzBot Mini')
460 profile.putMachineSetting('machine_type', 'lulzbot_mini')
461 profile.putMachineSetting('serial_baud', '115200')
462 profile.putMachineSetting('machine_center_is_zero', 'False')
463 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
464 profile.putMachineSetting('has_heated_bed', 'True')
465 profile.putMachineSetting('extruder_head_size_min_x', '0.0')
466 profile.putMachineSetting('extruder_head_size_min_y', '0.0')
467 profile.putMachineSetting('extruder_head_size_max_x', '0.0')
468 profile.putMachineSetting('extruder_head_size_max_y', '0.0')
469 profile.putMachineSetting('extruder_head_size_height', '0.0')
471 profile.putMachineSetting('machine_width', '80')
472 profile.putMachineSetting('machine_depth', '80')
473 profile.putMachineSetting('machine_height', '60')
474 profile.putMachineSetting('machine_name', 'reprap')
475 profile.putMachineSetting('machine_type', 'reprap')
476 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
477 profile.putPreference('startMode', 'Normal')
478 profile.putProfileSetting('nozzle_size', '0.5')
479 profile.checkAndUpdateMachineName()
480 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
482 class SelectParts(InfoPage):
483 def __init__(self, parent):
484 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
485 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."))
487 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
488 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
489 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
490 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
492 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."))
493 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
494 self.springExtruder.SetValue(True)
497 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
498 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
499 profile.putMachineSetting('has_heated_bed', 'True')
501 profile.putMachineSetting('has_heated_bed', 'False')
502 if self.dualExtrusion.GetValue():
503 profile.putMachineSetting('extruder_amount', '2')
504 profile.putMachineSetting('machine_depth', '195')
506 profile.putMachineSetting('extruder_amount', '1')
507 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
508 profile.putProfileSetting('retraction_enable', 'True')
510 profile.putProfileSetting('retraction_enable', 'False')
513 class UltimakerFirmwareUpgradePage(InfoPage):
514 def __init__(self, parent):
515 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
516 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."))
517 self.AddHiddenSeperator()
518 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
519 self.AddHiddenSeperator()
520 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."))
521 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Upgrade to Marlin firmware'), _('Skip upgrade'))
522 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
523 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
524 self.AddHiddenSeperator()
525 if profile.getMachineSetting('machine_type') == 'ultimaker':
526 self.AddText(_("Do not upgrade to this firmware if:"))
527 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
528 self.AddText(_("* Build your own heated bed"))
529 self.AddText(_("* Have other changes in the firmware"))
530 # button = self.AddButton('Goto this page for a custom firmware')
531 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
536 def OnUpgradeClick(self, e):
537 if firmwareInstall.InstallFirmware():
538 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
540 def OnSkipClick(self, e):
541 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
542 self.GetParent().ShowPage(self.GetNext())
544 def OnUrlClick(self, e):
545 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
547 class UltimakerCheckupPage(InfoPage):
548 def __init__(self, parent):
549 super(UltimakerCheckupPage, self).__init__(parent, _("Ultimaker Checkup"))
551 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
552 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
553 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
554 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
555 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
556 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
557 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
558 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
559 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
560 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
563 _("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."))
564 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
565 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
566 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
568 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
569 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
570 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
572 self.infoBox = self.AddInfoBox()
573 self.machineState = self.AddText("")
574 self.temperatureLabel = self.AddText("")
575 self.errorLogButton = self.AddButton(_("Show error log"))
576 self.errorLogButton.Show(False)
578 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
580 self.xMinStop = False
581 self.xMaxStop = False
582 self.yMinStop = False
583 self.yMaxStop = False
584 self.zMinStop = False
585 self.zMaxStop = False
587 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
590 if self.comm is not None:
594 self.endstopBitmap.Show(False)
597 def OnSkipClick(self, e):
598 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
599 self.GetParent().ShowPage(self.GetNext())
601 def OnCheckClick(self, e=None):
602 self.errorLogButton.Show(False)
603 if self.comm is not None:
607 wx.CallAfter(self.OnCheckClick)
609 self.infoBox.SetBusy(_("Connecting to machine."))
610 self.commState.SetBitmap(self.unknownBitmap)
611 self.tempState.SetBitmap(self.unknownBitmap)
612 self.stopState.SetBitmap(self.unknownBitmap)
613 self.checkupState = 0
614 self.checkExtruderNr = 0
615 self.comm = machineCom.MachineCom(callbackObject=self)
617 def OnErrorLog(self, e):
618 printWindow.LogWindow('\n'.join(self.comm.getLog()))
620 def mcLog(self, message):
623 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
624 if not self.comm.isOperational():
626 if self.checkupState == 0:
627 self.tempCheckTimeout = 20
628 if temp[self.checkExtruderNr] > 70:
629 self.checkupState = 1
630 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
631 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
632 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
634 self.startTemp = temp[self.checkExtruderNr]
635 self.checkupState = 2
636 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
637 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
638 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
639 elif self.checkupState == 1:
640 if temp[self.checkExtruderNr] < 60:
641 self.startTemp = temp[self.checkExtruderNr]
642 self.checkupState = 2
643 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
644 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
645 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
646 elif self.checkupState == 2:
647 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
648 if temp[self.checkExtruderNr] > self.startTemp + 40:
649 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
650 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
651 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
652 self.checkExtruderNr = 0
653 self.checkupState = 3
654 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
655 wx.CallAfter(self.endstopBitmap.Show, True)
656 wx.CallAfter(self.Layout)
657 self.comm.sendCommand('M119')
658 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
660 self.checkupState = 0
661 self.checkExtruderNr += 1
663 self.tempCheckTimeout -= 1
664 if self.tempCheckTimeout < 1:
665 self.checkupState = -1
666 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
667 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
668 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
669 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
670 elif self.checkupState >= 3 and self.checkupState < 10:
671 self.comm.sendCommand('M119')
672 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
674 def mcStateChange(self, state):
675 if self.comm is None:
677 if self.comm.isOperational():
678 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
679 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
680 elif self.comm.isError():
681 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
682 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
683 wx.CallAfter(self.endstopBitmap.Show, False)
684 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
685 wx.CallAfter(self.errorLogButton.Show, True)
686 wx.CallAfter(self.Layout)
688 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
690 def mcMessage(self, message):
691 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
692 for data in message.split(' '):
694 tag, value = data.split(':', 1)
696 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
698 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
700 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
702 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
704 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
706 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
708 tag, value = map(str.strip, message.split(':', 1))
710 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
712 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
714 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
716 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
718 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
720 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
721 if 'z_max' in message:
722 self.comm.sendCommand('M119')
724 if self.checkupState == 3:
725 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
726 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
727 self.checkupState = 5
728 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
729 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
731 self.checkupState = 4
732 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
733 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
734 elif self.checkupState == 4:
735 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
736 self.checkupState = 5
737 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
738 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
739 elif self.checkupState == 5:
740 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
741 self.checkupState = 6
742 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
743 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
744 elif self.checkupState == 6:
745 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
746 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
747 self.checkupState = 8
748 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
749 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
751 self.checkupState = 7
752 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
753 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
754 elif self.checkupState == 7:
755 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
756 self.checkupState = 8
757 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
758 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
759 elif self.checkupState == 8:
760 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
761 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
762 self.checkupState = 10
764 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
765 wx.CallAfter(self.infoBox.SetReadyIndicator)
766 wx.CallAfter(self.endstopBitmap.Show, False)
767 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
768 wx.CallAfter(self.OnSkipClick, None)
770 self.checkupState = 9
771 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
772 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
773 elif self.checkupState == 9:
774 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
775 self.checkupState = 10
777 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
778 wx.CallAfter(self.infoBox.SetReadyIndicator)
779 wx.CallAfter(self.endstopBitmap.Show, False)
780 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
781 wx.CallAfter(self.OnSkipClick, None)
783 def mcProgress(self, lineNr):
786 def mcZChange(self, newZ):
790 class UltimakerCalibrationPage(InfoPage):
791 def __init__(self, parent):
792 super(UltimakerCalibrationPage, self).__init__(parent, _("Ultimaker Calibration"))
794 self.AddText("Your Ultimaker requires some calibration.")
795 self.AddText("This calibration is needed for a proper extrusion amount.")
797 self.AddText("The following values are needed:")
798 self.AddText("* Diameter of filament")
799 self.AddText("* Number of steps per mm of filament extrusion")
801 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
803 self.AddText("First we need the diameter of your filament:")
804 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
806 "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.")
807 self.AddText("Note: This value can be changed later at any time.")
810 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
813 class UltimakerCalibrateStepsPerEPage(InfoPage):
814 def __init__(self, parent):
815 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, _("Ultimaker Calibration"))
817 #if profile.getMachineSetting('steps_per_e') == '0':
818 # profile.putMachineSetting('steps_per_e', '865.888')
820 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
821 self.AddText(_("First remove any filament from your machine."))
822 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
823 self.AddText(_("We'll push the filament 100mm"))
824 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
825 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
826 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
827 self.AddText(_("This results in the following steps per E:"))
828 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
829 self.AddText(_("You can repeat these steps to get better calibration."))
832 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
833 self.heatButton = self.AddButton(_("Heatup for filament removal"))
835 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
836 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
837 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
839 def OnSaveLengthClick(self, e):
840 currentEValue = float(self.stepsPerEInput.GetValue())
841 realExtrudeLength = float(self.lengthInput.GetValue())
842 newEValue = currentEValue * 100 / realExtrudeLength
843 self.stepsPerEInput.SetValue(str(newEValue))
844 self.lengthInput.SetValue("100")
846 def OnExtrudeClick(self, e):
847 t = threading.Thread(target=self.OnExtrudeRun)
851 def OnExtrudeRun(self):
852 self.heatButton.Enable(False)
853 self.extrudeButton.Enable(False)
854 currentEValue = float(self.stepsPerEInput.GetValue())
855 self.comm = machineCom.MachineCom()
856 if not self.comm.isOpen():
858 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
859 'Printer error', wx.OK | wx.ICON_INFORMATION)
860 self.heatButton.Enable(True)
861 self.extrudeButton.Enable(True)
864 line = self.comm.readline()
869 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
872 self.sendGCommand('M302') #Disable cold extrusion protection
873 self.sendGCommand("M92 E%f" % (currentEValue))
874 self.sendGCommand("G92 E0")
875 self.sendGCommand("G1 E100 F600")
878 self.extrudeButton.Enable()
879 self.heatButton.Enable()
881 def OnHeatClick(self, e):
882 t = threading.Thread(target=self.OnHeatRun)
887 self.heatButton.Enable(False)
888 self.extrudeButton.Enable(False)
889 self.comm = machineCom.MachineCom()
890 if not self.comm.isOpen():
892 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
893 'Printer error', wx.OK | wx.ICON_INFORMATION)
894 self.heatButton.Enable(True)
895 self.extrudeButton.Enable(True)
898 line = self.comm.readline()
900 self.heatButton.Enable(True)
901 self.extrudeButton.Enable(True)
905 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
908 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
910 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
911 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
912 self.sendGCommand('M104 S0')
915 self.heatButton.Enable(True)
916 self.extrudeButton.Enable(True)
918 def sendGCommand(self, cmd):
919 self.comm.sendCommand(cmd) #Disable cold extrusion protection
921 line = self.comm.readline()
924 if line.startswith('ok'):
928 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
930 class Ultimaker2ReadyPage(InfoPage):
931 def __init__(self, parent):
932 super(Ultimaker2ReadyPage, self).__init__(parent, _("Ultimaker2"))
933 self.AddText(_('Congratulations on your the purchase of your brand new Ultimaker2.'))
934 self.AddText(_('Cura is now ready to be used with your Ultimaker2.'))
937 class LulzbotReadyPage(InfoPage):
938 def __init__(self, parent):
939 super(LulzbotReadyPage, self).__init__(parent, _("LulzBot TAZ/Mini"))
940 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
942 self.AddText(_('For more information about using Cura with your LulzBot'))
943 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
946 class configWizard(wx.wizard.Wizard):
947 def __init__(self, addNew = False):
948 super(configWizard, self).__init__(None, -1, _("Configuration Wizard"))
950 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
951 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
953 self.machineSelectPage = MachineSelectPage(self)
954 self.ultimakerSelectParts = SelectParts(self)
955 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
956 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
957 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
958 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
959 self.bedLevelPage = bedLevelWizardMain(self)
960 self.headOffsetCalibration = headOffsetCalibrationPage(self)
961 self.printrbotSelectType = PrintrbotPage(self)
962 self.otherMachineSelectPage = OtherMachineSelectPage(self)
963 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
964 self.otherMachineInfoPage = OtherMachineInfoPage(self)
966 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
967 self.lulzbotReadyPage = LulzbotReadyPage(self)
969 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
970 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
971 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
972 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
973 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
974 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
975 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
976 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
978 self.FitToPage(self.machineSelectPage)
979 self.GetPageAreaSizer().Add(self.machineSelectPage)
981 self.RunWizard(self.machineSelectPage)
984 def OnPageChanging(self, e):
985 e.GetPage().StoreData()
987 def OnPageChanged(self, e):
988 if e.GetPage().AllowNext():
989 self.FindWindowById(wx.ID_FORWARD).Enable()
991 self.FindWindowById(wx.ID_FORWARD).Disable()
992 if e.GetPage().AllowBack():
993 self.FindWindowById(wx.ID_BACKWARD).Enable()
995 self.FindWindowById(wx.ID_BACKWARD).Disable()
997 class bedLevelWizardMain(InfoPage):
998 def __init__(self, parent):
999 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1001 self.AddText(_('This wizard will help you in leveling your printer bed'))
1003 self.AddText(_('It will do the following steps'))
1004 self.AddText(_('* Move the printer head to each corner'))
1005 self.AddText(_(' and let you adjust the height of the bed to the nozzle'))
1006 self.AddText(_('* Print a line around the bed to check if it is level'))
1009 self.connectButton = self.AddButton(_('Connect to printer'))
1012 self.infoBox = self.AddInfoBox()
1013 self.resumeButton = self.AddButton(_('Resume'))
1014 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1015 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1016 self.resumeButton.Enable(False)
1018 self.upButton.Enable(False)
1019 self.downButton.Enable(False)
1020 self.upButton2.Enable(False)
1021 self.downButton2.Enable(False)
1023 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1024 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1025 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1026 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1027 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1028 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1030 def OnConnect(self, e = None):
1031 if self.comm is not None:
1035 wx.CallAfter(self.OnConnect)
1037 self.connectButton.Enable(False)
1038 self.comm = machineCom.MachineCom(callbackObject=self)
1039 self.infoBox.SetBusy(_('Connecting to machine.'))
1040 self._wizardState = 0
1042 def OnBedUp(self, e):
1043 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1044 self.comm.sendCommand('G92 Z10')
1045 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1046 self.comm.sendCommand('M400')
1048 def OnBedDown(self, e):
1049 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1050 self.comm.sendCommand('G92 Z10')
1051 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1052 self.comm.sendCommand('M400')
1054 def OnBedUp2(self, e):
1055 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1056 self.comm.sendCommand('G92 Z10')
1057 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1058 self.comm.sendCommand('M400')
1060 def OnBedDown2(self, e):
1061 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1062 self.comm.sendCommand('G92 Z10')
1063 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1064 self.comm.sendCommand('M400')
1066 def AllowNext(self):
1067 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1068 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1071 def OnResume(self, e):
1072 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1073 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1074 if self._wizardState == -1:
1075 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1076 wx.CallAfter(self.upButton.Enable, False)
1077 wx.CallAfter(self.downButton.Enable, False)
1078 wx.CallAfter(self.upButton2.Enable, False)
1079 wx.CallAfter(self.downButton2.Enable, False)
1080 self.comm.sendCommand('M105')
1081 self.comm.sendCommand('G28')
1082 self._wizardState = 1
1083 elif self._wizardState == 2:
1084 if profile.getMachineSetting('has_heated_bed') == 'True':
1085 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1086 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1087 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1088 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1089 self.comm.sendCommand('M400')
1090 self._wizardState = 3
1092 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1093 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1094 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1095 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1096 self.comm.sendCommand('M400')
1097 self._wizardState = 3
1098 elif self._wizardState == 4:
1099 if profile.getMachineSetting('has_heated_bed') == 'True':
1100 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1101 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1102 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1103 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1104 self.comm.sendCommand('M400')
1105 self._wizardState = 7
1107 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1108 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1109 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1110 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1111 self.comm.sendCommand('M400')
1112 self._wizardState = 5
1113 elif self._wizardState == 6:
1114 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1115 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1116 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1117 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1118 self.comm.sendCommand('M400')
1119 self._wizardState = 7
1120 elif self._wizardState == 8:
1121 wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1122 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1123 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1124 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1125 self._wizardState = 9
1126 elif self._wizardState == 10:
1127 self._wizardState = 11
1128 wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1129 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1130 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1131 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1132 w = profile.getMachineSettingFloat('machine_width') - 10
1133 d = profile.getMachineSettingFloat('machine_depth')
1134 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1135 filamentArea = math.pi * filamentRadius * filamentRadius
1136 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1140 'G1 Z2 F%d' % (feedZ),
1142 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1143 'G1 Z0.3 F%d' % (feedZ)]
1145 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1147 for i in xrange(0, 3):
1148 dist = 5.0 + 0.4 * float(i)
1149 eValue += (d - 2.0*dist) * ePerMM
1150 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1151 eValue += (w - 2.0*dist) * ePerMM
1152 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1153 eValue += (d - 2.0*dist) * ePerMM
1154 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1155 eValue += (w - 2.0*dist) * ePerMM
1156 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1158 gcodeList.append('M400')
1159 self.comm.printGCode(gcodeList)
1160 self.resumeButton.Enable(False)
1162 def mcLog(self, message):
1163 print 'Log:', message
1165 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1166 if self._wizardState == 1:
1167 self._wizardState = 2
1168 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1169 wx.CallAfter(self.resumeButton.Enable, True)
1170 elif self._wizardState == 3:
1171 self._wizardState = 4
1172 if profile.getMachineSetting('has_heated_bed') == 'True':
1173 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1175 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1176 wx.CallAfter(self.resumeButton.Enable, True)
1177 elif self._wizardState == 5:
1178 self._wizardState = 6
1179 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1180 wx.CallAfter(self.resumeButton.Enable, True)
1181 elif self._wizardState == 7:
1182 self._wizardState = 8
1183 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1184 wx.CallAfter(self.resumeButton.Enable, True)
1185 elif self._wizardState == 9:
1186 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1187 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1189 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1190 wx.CallAfter(self.resumeButton.Enable, True)
1191 self._wizardState = 10
1193 def mcStateChange(self, state):
1194 if self.comm is None:
1196 if self.comm.isOperational():
1197 if self._wizardState == 0:
1198 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1199 wx.CallAfter(self.upButton.Enable, True)
1200 wx.CallAfter(self.downButton.Enable, True)
1201 wx.CallAfter(self.upButton2.Enable, True)
1202 wx.CallAfter(self.downButton2.Enable, True)
1203 wx.CallAfter(self.resumeButton.Enable, True)
1204 self._wizardState = -1
1205 elif self._wizardState == 11 and not self.comm.isPrinting():
1206 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1207 self.comm.sendCommand('G92 E0')
1208 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1209 self.comm.sendCommand('M104 S0')
1210 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1211 wx.CallAfter(self.infoBox.SetReadyIndicator)
1212 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1213 wx.CallAfter(self.connectButton.Enable, True)
1214 self._wizardState = 12
1215 elif self.comm.isError():
1216 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1218 def mcMessage(self, message):
1221 def mcProgress(self, lineNr):
1224 def mcZChange(self, newZ):
1227 class headOffsetCalibrationPage(InfoPage):
1228 def __init__(self, parent):
1229 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1231 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1234 self.connectButton = self.AddButton(_('Connect to printer'))
1237 self.infoBox = self.AddInfoBox()
1238 self.textEntry = self.AddTextCtrl('')
1239 self.textEntry.Enable(False)
1240 self.resumeButton = self.AddButton(_('Resume'))
1241 self.resumeButton.Enable(False)
1243 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1244 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1246 def AllowBack(self):
1249 def OnConnect(self, e = None):
1250 if self.comm is not None:
1254 wx.CallAfter(self.OnConnect)
1256 self.connectButton.Enable(False)
1257 self.comm = machineCom.MachineCom(callbackObject=self)
1258 self.infoBox.SetBusy(_('Connecting to machine.'))
1259 self._wizardState = 0
1261 def OnResume(self, e):
1262 if self._wizardState == 2:
1263 self._wizardState = 3
1264 wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1266 w = profile.getMachineSettingFloat('machine_width')
1267 d = profile.getMachineSettingFloat('machine_depth')
1269 gcode = gcodeGenerator.gcodeGenerator()
1270 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1271 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1278 gcode.addMove(w/2, 5)
1279 gcode.addMove(z=0.2)
1281 gcode.addExtrude(w/2, d-5.0)
1283 gcode.addMove(5, d/2)
1285 gcode.addExtrude(w-5.0, d/2)
1286 gcode.addRetract(15)
1289 gcode.addMove(w/2, 5)
1291 gcode.addExtrude(w/2, d-5.0)
1293 gcode.addMove(5, d/2)
1295 gcode.addExtrude(w-5.0, d/2)
1296 gcode.addRetract(15)
1301 gcode.addCmd('M400')
1303 self.comm.printGCode(gcode.list())
1304 self.resumeButton.Enable(False)
1305 elif self._wizardState == 4:
1307 float(self.textEntry.GetValue())
1310 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1311 self._wizardState = 5
1312 self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1313 self.textEntry.SetValue('0.0')
1314 self.textEntry.Enable(True)
1315 elif self._wizardState == 5:
1317 float(self.textEntry.GetValue())
1320 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1321 self._wizardState = 6
1322 self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1323 self.textEntry.SetValue('')
1324 self.textEntry.Enable(False)
1325 self.resumeButton.Enable(False)
1327 x = profile.getMachineSettingFloat('extruder_offset_x1')
1328 y = profile.getMachineSettingFloat('extruder_offset_y1')
1329 gcode = gcodeGenerator.gcodeGenerator()
1330 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1331 gcode.setPrintSpeed(25)
1334 gcode.addMove(50, 40, 0.2)
1336 for n in xrange(0, 10):
1337 gcode.addExtrude(50 + n * 10, 150)
1338 gcode.addExtrude(50 + n * 10 + 5, 150)
1339 gcode.addExtrude(50 + n * 10 + 5, 40)
1340 gcode.addExtrude(50 + n * 10 + 10, 40)
1341 gcode.addMove(40, 50)
1342 for n in xrange(0, 10):
1343 gcode.addExtrude(150, 50 + n * 10)
1344 gcode.addExtrude(150, 50 + n * 10 + 5)
1345 gcode.addExtrude(40, 50 + n * 10 + 5)
1346 gcode.addExtrude(40, 50 + n * 10 + 10)
1347 gcode.addRetract(15)
1350 gcode.addMove(50 - x, 30 - y, 0.2)
1352 for n in xrange(0, 10):
1353 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1354 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1355 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1356 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1357 gcode.addMove(30 - x, 50 - y, 0.2)
1358 for n in xrange(0, 10):
1359 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1360 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1361 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1362 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1363 gcode.addRetract(15)
1365 gcode.addCmd('M400')
1366 gcode.addCmd('M104 T0 S0')
1367 gcode.addCmd('M104 T1 S0')
1368 self.comm.printGCode(gcode.list())
1369 elif self._wizardState == 7:
1371 n = int(self.textEntry.GetValue()) - 1
1374 x = profile.getMachineSettingFloat('extruder_offset_x1')
1376 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1377 self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1378 self.textEntry.SetValue('10')
1379 self._wizardState = 8
1380 elif self._wizardState == 8:
1382 n = int(self.textEntry.GetValue()) - 1
1385 y = profile.getMachineSettingFloat('extruder_offset_y1')
1387 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1388 self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1389 self.infoBox.SetReadyIndicator()
1390 self._wizardState = 8
1392 self.resumeButton.Enable(False)
1394 def mcLog(self, message):
1395 print 'Log:', message
1397 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1398 if self._wizardState == 1:
1399 if temp[0] >= 210 and temp[1] >= 210:
1400 self._wizardState = 2
1401 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1402 wx.CallAfter(self.resumeButton.Enable, True)
1403 wx.CallAfter(self.resumeButton.SetFocus)
1405 def mcStateChange(self, state):
1406 if self.comm is None:
1408 if self.comm.isOperational():
1409 if self._wizardState == 0:
1410 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1411 self.comm.sendCommand('M105')
1412 self.comm.sendCommand('M104 S220 T0')
1413 self.comm.sendCommand('M104 S220 T1')
1414 self.comm.sendCommand('G28')
1415 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1416 self._wizardState = 1
1417 if not self.comm.isPrinting():
1418 if self._wizardState == 3:
1419 self._wizardState = 4
1420 wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1421 wx.CallAfter(self.textEntry.SetValue, '0.0')
1422 wx.CallAfter(self.textEntry.Enable, True)
1423 wx.CallAfter(self.resumeButton.Enable, True)
1424 wx.CallAfter(self.resumeButton.SetFocus)
1425 elif self._wizardState == 6:
1426 self._wizardState = 7
1427 wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1428 wx.CallAfter(self.textEntry.SetValue, '10')
1429 wx.CallAfter(self.textEntry.Enable, True)
1430 wx.CallAfter(self.resumeButton.Enable, True)
1431 wx.CallAfter(self.resumeButton.SetFocus)
1433 elif self.comm.isError():
1434 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1436 def mcMessage(self, message):
1439 def mcProgress(self, lineNr):
1442 def mcZChange(self, newZ):
1445 class bedLevelWizard(wx.wizard.Wizard):
1447 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1449 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1450 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1452 self.mainPage = bedLevelWizardMain(self)
1453 self.headOffsetCalibration = None
1455 self.FitToPage(self.mainPage)
1456 self.GetPageAreaSizer().Add(self.mainPage)
1458 self.RunWizard(self.mainPage)
1461 def OnPageChanging(self, e):
1462 e.GetPage().StoreData()
1464 def OnPageChanged(self, e):
1465 if e.GetPage().AllowNext():
1466 self.FindWindowById(wx.ID_FORWARD).Enable()
1468 self.FindWindowById(wx.ID_FORWARD).Disable()
1469 if e.GetPage().AllowBack():
1470 self.FindWindowById(wx.ID_BACKWARD).Enable()
1472 self.FindWindowById(wx.ID_BACKWARD).Disable()
1474 class headOffsetWizard(wx.wizard.Wizard):
1476 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1478 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1479 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1481 self.mainPage = headOffsetCalibrationPage(self)
1483 self.FitToPage(self.mainPage)
1484 self.GetPageAreaSizer().Add(self.mainPage)
1486 self.RunWizard(self.mainPage)
1489 def OnPageChanging(self, e):
1490 e.GetPage().StoreData()
1492 def OnPageChanged(self, e):
1493 if e.GetPage().AllowNext():
1494 self.FindWindowById(wx.ID_FORWARD).Enable()
1496 self.FindWindowById(wx.ID_FORWARD).Disable()
1497 if e.GetPage().AllowBack():
1498 self.FindWindowById(wx.ID_BACKWARD).Enable()
1500 self.FindWindowById(wx.ID_BACKWARD).Disable()