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')
470 profile.putPreference('startMode', 'Simple')
472 profile.putMachineSetting('machine_width', '80')
473 profile.putMachineSetting('machine_depth', '80')
474 profile.putMachineSetting('machine_height', '60')
475 profile.putMachineSetting('machine_name', 'reprap')
476 profile.putMachineSetting('machine_type', 'reprap')
477 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
478 profile.putPreference('startMode', 'Normal')
479 profile.putProfileSetting('nozzle_size', '0.5')
480 profile.checkAndUpdateMachineName()
481 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
483 class SelectParts(InfoPage):
484 def __init__(self, parent):
485 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
486 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."))
488 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
489 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
490 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
491 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
493 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."))
494 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
495 self.springExtruder.SetValue(True)
498 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
499 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
500 profile.putMachineSetting('has_heated_bed', 'True')
502 profile.putMachineSetting('has_heated_bed', 'False')
503 if self.dualExtrusion.GetValue():
504 profile.putMachineSetting('extruder_amount', '2')
505 profile.putMachineSetting('machine_depth', '195')
507 profile.putMachineSetting('extruder_amount', '1')
508 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
509 profile.putProfileSetting('retraction_enable', 'True')
511 profile.putProfileSetting('retraction_enable', 'False')
514 class UltimakerFirmwareUpgradePage(InfoPage):
515 def __init__(self, parent):
516 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
517 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."))
518 self.AddHiddenSeperator()
519 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
520 self.AddHiddenSeperator()
521 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."))
522 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
523 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
524 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
525 self.AddHiddenSeperator()
526 if profile.getMachineSetting('machine_type') == 'ultimaker':
527 self.AddText(_("Do not upgrade to this firmware if:"))
528 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
529 self.AddText(_("* Build your own heated bed"))
530 self.AddText(_("* Have other changes in the firmware"))
531 # button = self.AddButton('Goto this page for a custom firmware')
532 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
537 def OnUpgradeClick(self, e):
538 if firmwareInstall.InstallFirmware():
539 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
541 def OnSkipClick(self, e):
542 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
543 self.GetParent().ShowPage(self.GetNext())
545 def OnUrlClick(self, e):
546 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
548 class UltimakerCheckupPage(InfoPage):
549 def __init__(self, parent):
550 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
552 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
553 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
554 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
555 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
556 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
557 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
558 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
559 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
560 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
561 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
564 _("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."))
565 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
566 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
567 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
569 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
570 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
571 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
573 self.infoBox = self.AddInfoBox()
574 self.machineState = self.AddText("")
575 self.temperatureLabel = self.AddText("")
576 self.errorLogButton = self.AddButton(_("Show error log"))
577 self.errorLogButton.Show(False)
579 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
581 self.xMinStop = False
582 self.xMaxStop = False
583 self.yMinStop = False
584 self.yMaxStop = False
585 self.zMinStop = False
586 self.zMaxStop = False
588 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
591 if self.comm is not None:
595 self.endstopBitmap.Show(False)
598 def OnSkipClick(self, e):
599 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
600 self.GetParent().ShowPage(self.GetNext())
602 def OnCheckClick(self, e=None):
603 self.errorLogButton.Show(False)
604 if self.comm is not None:
608 wx.CallAfter(self.OnCheckClick)
610 self.infoBox.SetBusy(_("Connecting to machine."))
611 self.commState.SetBitmap(self.unknownBitmap)
612 self.tempState.SetBitmap(self.unknownBitmap)
613 self.stopState.SetBitmap(self.unknownBitmap)
614 self.checkupState = 0
615 self.checkExtruderNr = 0
616 self.comm = machineCom.MachineCom(callbackObject=self)
618 def OnErrorLog(self, e):
619 printWindow.LogWindow('\n'.join(self.comm.getLog()))
621 def mcLog(self, message):
624 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
625 if not self.comm.isOperational():
627 if self.checkupState == 0:
628 self.tempCheckTimeout = 20
629 if temp[self.checkExtruderNr] > 70:
630 self.checkupState = 1
631 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
632 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
633 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
635 self.startTemp = temp[self.checkExtruderNr]
636 self.checkupState = 2
637 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
638 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
639 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
640 elif self.checkupState == 1:
641 if temp[self.checkExtruderNr] < 60:
642 self.startTemp = temp[self.checkExtruderNr]
643 self.checkupState = 2
644 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
645 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
646 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
647 elif self.checkupState == 2:
648 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
649 if temp[self.checkExtruderNr] > self.startTemp + 40:
650 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
651 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
652 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
653 self.checkExtruderNr = 0
654 self.checkupState = 3
655 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
656 wx.CallAfter(self.endstopBitmap.Show, True)
657 wx.CallAfter(self.Layout)
658 self.comm.sendCommand('M119')
659 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
661 self.checkupState = 0
662 self.checkExtruderNr += 1
664 self.tempCheckTimeout -= 1
665 if self.tempCheckTimeout < 1:
666 self.checkupState = -1
667 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
668 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
669 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
670 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
671 elif self.checkupState >= 3 and self.checkupState < 10:
672 self.comm.sendCommand('M119')
673 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
675 def mcStateChange(self, state):
676 if self.comm is None:
678 if self.comm.isOperational():
679 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
680 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
681 elif self.comm.isError():
682 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
683 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
684 wx.CallAfter(self.endstopBitmap.Show, False)
685 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
686 wx.CallAfter(self.errorLogButton.Show, True)
687 wx.CallAfter(self.Layout)
689 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
691 def mcMessage(self, message):
692 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
693 for data in message.split(' '):
695 tag, value = data.split(':', 1)
697 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
699 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
701 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
703 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
705 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
707 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
709 tag, value = map(str.strip, message.split(':', 1))
711 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
713 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
715 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
717 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
719 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
721 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
722 if 'z_max' in message:
723 self.comm.sendCommand('M119')
725 if self.checkupState == 3:
726 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
727 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
728 self.checkupState = 5
729 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
730 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
732 self.checkupState = 4
733 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
734 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
735 elif self.checkupState == 4:
736 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
737 self.checkupState = 5
738 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
739 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
740 elif self.checkupState == 5:
741 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
742 self.checkupState = 6
743 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
744 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
745 elif self.checkupState == 6:
746 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
747 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
748 self.checkupState = 8
749 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
750 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
752 self.checkupState = 7
753 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
754 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
755 elif self.checkupState == 7:
756 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
757 self.checkupState = 8
758 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
759 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
760 elif self.checkupState == 8:
761 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
762 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
763 self.checkupState = 10
765 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
766 wx.CallAfter(self.infoBox.SetReadyIndicator)
767 wx.CallAfter(self.endstopBitmap.Show, False)
768 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
769 wx.CallAfter(self.OnSkipClick, None)
771 self.checkupState = 9
772 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
773 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
774 elif self.checkupState == 9:
775 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
776 self.checkupState = 10
778 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
779 wx.CallAfter(self.infoBox.SetReadyIndicator)
780 wx.CallAfter(self.endstopBitmap.Show, False)
781 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
782 wx.CallAfter(self.OnSkipClick, None)
784 def mcProgress(self, lineNr):
787 def mcZChange(self, newZ):
791 class UltimakerCalibrationPage(InfoPage):
792 def __init__(self, parent):
793 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
795 self.AddText("Your Ultimaker requires some calibration.")
796 self.AddText("This calibration is needed for a proper extrusion amount.")
798 self.AddText("The following values are needed:")
799 self.AddText("* Diameter of filament")
800 self.AddText("* Number of steps per mm of filament extrusion")
802 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
804 self.AddText("First we need the diameter of your filament:")
805 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
807 "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.")
808 self.AddText("Note: This value can be changed later at any time.")
811 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
814 class UltimakerCalibrateStepsPerEPage(InfoPage):
815 def __init__(self, parent):
816 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
818 #if profile.getMachineSetting('steps_per_e') == '0':
819 # profile.putMachineSetting('steps_per_e', '865.888')
821 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
822 self.AddText(_("First remove any filament from your machine."))
823 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
824 self.AddText(_("We'll push the filament 100mm"))
825 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
826 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
827 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
828 self.AddText(_("This results in the following steps per E:"))
829 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
830 self.AddText(_("You can repeat these steps to get better calibration."))
833 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
834 self.heatButton = self.AddButton(_("Heatup for filament removal"))
836 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
837 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
838 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
840 def OnSaveLengthClick(self, e):
841 currentEValue = float(self.stepsPerEInput.GetValue())
842 realExtrudeLength = float(self.lengthInput.GetValue())
843 newEValue = currentEValue * 100 / realExtrudeLength
844 self.stepsPerEInput.SetValue(str(newEValue))
845 self.lengthInput.SetValue("100")
847 def OnExtrudeClick(self, e):
848 t = threading.Thread(target=self.OnExtrudeRun)
852 def OnExtrudeRun(self):
853 self.heatButton.Enable(False)
854 self.extrudeButton.Enable(False)
855 currentEValue = float(self.stepsPerEInput.GetValue())
856 self.comm = machineCom.MachineCom()
857 if not self.comm.isOpen():
859 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
860 'Printer error', wx.OK | wx.ICON_INFORMATION)
861 self.heatButton.Enable(True)
862 self.extrudeButton.Enable(True)
865 line = self.comm.readline()
870 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
873 self.sendGCommand('M302') #Disable cold extrusion protection
874 self.sendGCommand("M92 E%f" % (currentEValue))
875 self.sendGCommand("G92 E0")
876 self.sendGCommand("G1 E100 F600")
879 self.extrudeButton.Enable()
880 self.heatButton.Enable()
882 def OnHeatClick(self, e):
883 t = threading.Thread(target=self.OnHeatRun)
888 self.heatButton.Enable(False)
889 self.extrudeButton.Enable(False)
890 self.comm = machineCom.MachineCom()
891 if not self.comm.isOpen():
893 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
894 'Printer error', wx.OK | wx.ICON_INFORMATION)
895 self.heatButton.Enable(True)
896 self.extrudeButton.Enable(True)
899 line = self.comm.readline()
901 self.heatButton.Enable(True)
902 self.extrudeButton.Enable(True)
906 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
909 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
911 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
912 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
913 self.sendGCommand('M104 S0')
916 self.heatButton.Enable(True)
917 self.extrudeButton.Enable(True)
919 def sendGCommand(self, cmd):
920 self.comm.sendCommand(cmd) #Disable cold extrusion protection
922 line = self.comm.readline()
925 if line.startswith('ok'):
929 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
931 class Ultimaker2ReadyPage(InfoPage):
932 def __init__(self, parent):
933 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
934 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
935 self.AddText('Cura is now ready to be used with your Ultimaker2.')
938 class LulzbotReadyPage(InfoPage):
939 def __init__(self, parent):
940 super(LulzbotReadyPage, self).__init__(parent, "LulzBot TAZ/Mini")
941 self.AddText('Cura is now ready to be used with your LulzBot 3D printer.')
943 self.AddText('For more information about using Cura with your LulzBot')
944 self.AddText('3D printer, please visit www.LulzBot.com/cura')
947 class configWizard(wx.wizard.Wizard):
948 def __init__(self, addNew = False):
949 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
951 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
952 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
954 self.machineSelectPage = MachineSelectPage(self)
955 self.ultimakerSelectParts = SelectParts(self)
956 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
957 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
958 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
959 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
960 self.bedLevelPage = bedLevelWizardMain(self)
961 self.headOffsetCalibration = headOffsetCalibrationPage(self)
962 self.printrbotSelectType = PrintrbotPage(self)
963 self.otherMachineSelectPage = OtherMachineSelectPage(self)
964 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
965 self.otherMachineInfoPage = OtherMachineInfoPage(self)
967 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
968 self.lulzbotReadyPage = LulzbotReadyPage(self)
970 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
971 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
972 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
973 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
974 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
975 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
976 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
977 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
979 self.FitToPage(self.machineSelectPage)
980 self.GetPageAreaSizer().Add(self.machineSelectPage)
982 self.RunWizard(self.machineSelectPage)
985 def OnPageChanging(self, e):
986 e.GetPage().StoreData()
988 def OnPageChanged(self, e):
989 if e.GetPage().AllowNext():
990 self.FindWindowById(wx.ID_FORWARD).Enable()
992 self.FindWindowById(wx.ID_FORWARD).Disable()
993 if e.GetPage().AllowBack():
994 self.FindWindowById(wx.ID_BACKWARD).Enable()
996 self.FindWindowById(wx.ID_BACKWARD).Disable()
998 class bedLevelWizardMain(InfoPage):
999 def __init__(self, parent):
1000 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
1002 self.AddText('This wizard will help you in leveling your printer bed')
1004 self.AddText('It will do the following steps')
1005 self.AddText('* Move the printer head to each corner')
1006 self.AddText(' and let you adjust the height of the bed to the nozzle')
1007 self.AddText('* Print a line around the bed to check if it is level')
1010 self.connectButton = self.AddButton('Connect to printer')
1013 self.infoBox = self.AddInfoBox()
1014 self.resumeButton = self.AddButton('Resume')
1015 self.upButton, self.downButton = self.AddDualButton('Up 0.2mm', 'Down 0.2mm')
1016 self.upButton2, self.downButton2 = self.AddDualButton('Up 10mm', 'Down 10mm')
1017 self.resumeButton.Enable(False)
1019 self.upButton.Enable(False)
1020 self.downButton.Enable(False)
1021 self.upButton2.Enable(False)
1022 self.downButton2.Enable(False)
1024 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1025 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1026 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1027 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1028 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1029 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1031 def OnConnect(self, e = None):
1032 if self.comm is not None:
1036 wx.CallAfter(self.OnConnect)
1038 self.connectButton.Enable(False)
1039 self.comm = machineCom.MachineCom(callbackObject=self)
1040 self.infoBox.SetBusy('Connecting to machine.')
1041 self._wizardState = 0
1043 def OnBedUp(self, e):
1044 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1045 self.comm.sendCommand('G92 Z10')
1046 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1047 self.comm.sendCommand('M400')
1049 def OnBedDown(self, e):
1050 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1051 self.comm.sendCommand('G92 Z10')
1052 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1053 self.comm.sendCommand('M400')
1055 def OnBedUp2(self, e):
1056 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1057 self.comm.sendCommand('G92 Z10')
1058 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1059 self.comm.sendCommand('M400')
1061 def OnBedDown2(self, e):
1062 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1063 self.comm.sendCommand('G92 Z10')
1064 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1065 self.comm.sendCommand('M400')
1067 def AllowNext(self):
1068 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1069 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1072 def OnResume(self, e):
1073 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1074 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1075 if self._wizardState == -1:
1076 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
1077 wx.CallAfter(self.upButton.Enable, False)
1078 wx.CallAfter(self.downButton.Enable, False)
1079 wx.CallAfter(self.upButton2.Enable, False)
1080 wx.CallAfter(self.downButton2.Enable, False)
1081 self.comm.sendCommand('M105')
1082 self.comm.sendCommand('G28')
1083 self._wizardState = 1
1084 elif self._wizardState == 2:
1085 if profile.getMachineSetting('has_heated_bed') == 'True':
1086 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back center...')
1087 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1088 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1089 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1090 self.comm.sendCommand('M400')
1091 self._wizardState = 3
1093 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
1094 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1095 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1096 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1097 self.comm.sendCommand('M400')
1098 self._wizardState = 3
1099 elif self._wizardState == 4:
1100 if profile.getMachineSetting('has_heated_bed') == 'True':
1101 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
1102 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1103 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1104 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1105 self.comm.sendCommand('M400')
1106 self._wizardState = 7
1108 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
1109 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1110 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1111 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1112 self.comm.sendCommand('M400')
1113 self._wizardState = 5
1114 elif self._wizardState == 6:
1115 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
1116 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1117 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1118 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1119 self.comm.sendCommand('M400')
1120 self._wizardState = 7
1121 elif self._wizardState == 8:
1122 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
1123 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1124 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1125 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1126 self._wizardState = 9
1127 elif self._wizardState == 10:
1128 self._wizardState = 11
1129 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
1130 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1131 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1132 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1133 w = profile.getMachineSettingFloat('machine_width') - 10
1134 d = profile.getMachineSettingFloat('machine_depth')
1135 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1136 filamentArea = math.pi * filamentRadius * filamentRadius
1137 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1141 'G1 Z2 F%d' % (feedZ),
1143 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1144 'G1 Z0.3 F%d' % (feedZ)]
1146 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1148 for i in xrange(0, 3):
1149 dist = 5.0 + 0.4 * float(i)
1150 eValue += (d - 2.0*dist) * ePerMM
1151 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1152 eValue += (w - 2.0*dist) * ePerMM
1153 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1154 eValue += (d - 2.0*dist) * ePerMM
1155 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1156 eValue += (w - 2.0*dist) * ePerMM
1157 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1159 gcodeList.append('M400')
1160 self.comm.printGCode(gcodeList)
1161 self.resumeButton.Enable(False)
1163 def mcLog(self, message):
1164 print 'Log:', message
1166 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1167 if self._wizardState == 1:
1168 self._wizardState = 2
1169 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
1170 wx.CallAfter(self.resumeButton.Enable, True)
1171 elif self._wizardState == 3:
1172 self._wizardState = 4
1173 if profile.getMachineSetting('has_heated_bed') == 'True':
1174 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.')
1176 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
1177 wx.CallAfter(self.resumeButton.Enable, True)
1178 elif self._wizardState == 5:
1179 self._wizardState = 6
1180 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
1181 wx.CallAfter(self.resumeButton.Enable, True)
1182 elif self._wizardState == 7:
1183 self._wizardState = 8
1184 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
1185 wx.CallAfter(self.resumeButton.Enable, True)
1186 elif self._wizardState == 9:
1187 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1188 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1190 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
1191 wx.CallAfter(self.resumeButton.Enable, True)
1192 self._wizardState = 10
1194 def mcStateChange(self, state):
1195 if self.comm is None:
1197 if self.comm.isOperational():
1198 if self._wizardState == 0:
1199 wx.CallAfter(self.infoBox.SetAttention, 'Use the up/down buttons to move the bed and adjust your Z endstop.')
1200 wx.CallAfter(self.upButton.Enable, True)
1201 wx.CallAfter(self.downButton.Enable, True)
1202 wx.CallAfter(self.upButton2.Enable, True)
1203 wx.CallAfter(self.downButton2.Enable, True)
1204 wx.CallAfter(self.resumeButton.Enable, True)
1205 self._wizardState = -1
1206 elif self._wizardState == 11 and not self.comm.isPrinting():
1207 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1208 self.comm.sendCommand('G92 E0')
1209 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1210 self.comm.sendCommand('M104 S0')
1211 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
1212 wx.CallAfter(self.infoBox.SetReadyIndicator)
1213 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1214 wx.CallAfter(self.connectButton.Enable, True)
1215 self._wizardState = 12
1216 elif self.comm.isError():
1217 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1219 def mcMessage(self, message):
1222 def mcProgress(self, lineNr):
1225 def mcZChange(self, newZ):
1228 class headOffsetCalibrationPage(InfoPage):
1229 def __init__(self, parent):
1230 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1232 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
1235 self.connectButton = self.AddButton('Connect to printer')
1238 self.infoBox = self.AddInfoBox()
1239 self.textEntry = self.AddTextCtrl('')
1240 self.textEntry.Enable(False)
1241 self.resumeButton = self.AddButton('Resume')
1242 self.resumeButton.Enable(False)
1244 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1245 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1247 def AllowBack(self):
1250 def OnConnect(self, e = None):
1251 if self.comm is not None:
1255 wx.CallAfter(self.OnConnect)
1257 self.connectButton.Enable(False)
1258 self.comm = machineCom.MachineCom(callbackObject=self)
1259 self.infoBox.SetBusy('Connecting to machine.')
1260 self._wizardState = 0
1262 def OnResume(self, e):
1263 if self._wizardState == 2:
1264 self._wizardState = 3
1265 wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1267 w = profile.getMachineSettingFloat('machine_width')
1268 d = profile.getMachineSettingFloat('machine_depth')
1270 gcode = gcodeGenerator.gcodeGenerator()
1271 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1272 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1279 gcode.addMove(w/2, 5)
1280 gcode.addMove(z=0.2)
1282 gcode.addExtrude(w/2, d-5.0)
1284 gcode.addMove(5, d/2)
1286 gcode.addExtrude(w-5.0, d/2)
1287 gcode.addRetract(15)
1290 gcode.addMove(w/2, 5)
1292 gcode.addExtrude(w/2, d-5.0)
1294 gcode.addMove(5, d/2)
1296 gcode.addExtrude(w-5.0, d/2)
1297 gcode.addRetract(15)
1302 gcode.addCmd('M400')
1304 self.comm.printGCode(gcode.list())
1305 self.resumeButton.Enable(False)
1306 elif self._wizardState == 4:
1308 float(self.textEntry.GetValue())
1311 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1312 self._wizardState = 5
1313 self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1314 self.textEntry.SetValue('0.0')
1315 self.textEntry.Enable(True)
1316 elif self._wizardState == 5:
1318 float(self.textEntry.GetValue())
1321 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1322 self._wizardState = 6
1323 self.infoBox.SetBusy('Printing the fine calibration lines.')
1324 self.textEntry.SetValue('')
1325 self.textEntry.Enable(False)
1326 self.resumeButton.Enable(False)
1328 x = profile.getMachineSettingFloat('extruder_offset_x1')
1329 y = profile.getMachineSettingFloat('extruder_offset_y1')
1330 gcode = gcodeGenerator.gcodeGenerator()
1331 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1332 gcode.setPrintSpeed(25)
1335 gcode.addMove(50, 40, 0.2)
1337 for n in xrange(0, 10):
1338 gcode.addExtrude(50 + n * 10, 150)
1339 gcode.addExtrude(50 + n * 10 + 5, 150)
1340 gcode.addExtrude(50 + n * 10 + 5, 40)
1341 gcode.addExtrude(50 + n * 10 + 10, 40)
1342 gcode.addMove(40, 50)
1343 for n in xrange(0, 10):
1344 gcode.addExtrude(150, 50 + n * 10)
1345 gcode.addExtrude(150, 50 + n * 10 + 5)
1346 gcode.addExtrude(40, 50 + n * 10 + 5)
1347 gcode.addExtrude(40, 50 + n * 10 + 10)
1348 gcode.addRetract(15)
1351 gcode.addMove(50 - x, 30 - y, 0.2)
1353 for n in xrange(0, 10):
1354 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1355 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1356 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1357 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1358 gcode.addMove(30 - x, 50 - y, 0.2)
1359 for n in xrange(0, 10):
1360 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1361 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1362 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1363 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1364 gcode.addRetract(15)
1366 gcode.addCmd('M400')
1367 gcode.addCmd('M104 T0 S0')
1368 gcode.addCmd('M104 T1 S0')
1369 self.comm.printGCode(gcode.list())
1370 elif self._wizardState == 7:
1372 n = int(self.textEntry.GetValue()) - 1
1375 x = profile.getMachineSettingFloat('extruder_offset_x1')
1377 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1378 self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1379 self.textEntry.SetValue('10')
1380 self._wizardState = 8
1381 elif self._wizardState == 8:
1383 n = int(self.textEntry.GetValue()) - 1
1386 y = profile.getMachineSettingFloat('extruder_offset_y1')
1388 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1389 self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1390 self.infoBox.SetReadyIndicator()
1391 self._wizardState = 8
1393 self.resumeButton.Enable(False)
1395 def mcLog(self, message):
1396 print 'Log:', message
1398 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1399 if self._wizardState == 1:
1400 if temp[0] >= 210 and temp[1] >= 210:
1401 self._wizardState = 2
1402 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1403 wx.CallAfter(self.resumeButton.Enable, True)
1404 wx.CallAfter(self.resumeButton.SetFocus)
1406 def mcStateChange(self, state):
1407 if self.comm is None:
1409 if self.comm.isOperational():
1410 if self._wizardState == 0:
1411 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1412 self.comm.sendCommand('M105')
1413 self.comm.sendCommand('M104 S220 T0')
1414 self.comm.sendCommand('M104 S220 T1')
1415 self.comm.sendCommand('G28')
1416 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1417 self._wizardState = 1
1418 if not self.comm.isPrinting():
1419 if self._wizardState == 3:
1420 self._wizardState = 4
1421 wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1422 wx.CallAfter(self.textEntry.SetValue, '0.0')
1423 wx.CallAfter(self.textEntry.Enable, True)
1424 wx.CallAfter(self.resumeButton.Enable, True)
1425 wx.CallAfter(self.resumeButton.SetFocus)
1426 elif self._wizardState == 6:
1427 self._wizardState = 7
1428 wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1429 wx.CallAfter(self.textEntry.SetValue, '10')
1430 wx.CallAfter(self.textEntry.Enable, True)
1431 wx.CallAfter(self.resumeButton.Enable, True)
1432 wx.CallAfter(self.resumeButton.SetFocus)
1434 elif self.comm.isError():
1435 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1437 def mcMessage(self, message):
1440 def mcProgress(self, lineNr):
1443 def mcZChange(self, newZ):
1446 class bedLevelWizard(wx.wizard.Wizard):
1448 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1450 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1451 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1453 self.mainPage = bedLevelWizardMain(self)
1454 self.headOffsetCalibration = None
1456 self.FitToPage(self.mainPage)
1457 self.GetPageAreaSizer().Add(self.mainPage)
1459 self.RunWizard(self.mainPage)
1462 def OnPageChanging(self, e):
1463 e.GetPage().StoreData()
1465 def OnPageChanged(self, e):
1466 if e.GetPage().AllowNext():
1467 self.FindWindowById(wx.ID_FORWARD).Enable()
1469 self.FindWindowById(wx.ID_FORWARD).Disable()
1470 if e.GetPage().AllowBack():
1471 self.FindWindowById(wx.ID_BACKWARD).Enable()
1473 self.FindWindowById(wx.ID_BACKWARD).Disable()
1475 class headOffsetWizard(wx.wizard.Wizard):
1477 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1479 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1480 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1482 self.mainPage = headOffsetCalibrationPage(self)
1484 self.FitToPage(self.mainPage)
1485 self.GetPageAreaSizer().Add(self.mainPage)
1487 self.RunWizard(self.mainPage)
1490 def OnPageChanging(self, e):
1491 e.GetPage().StoreData()
1493 def OnPageChanged(self, e):
1494 if e.GetPage().AllowNext():
1495 self.FindWindowById(wx.ID_FORWARD).Enable()
1497 self.FindWindowById(wx.ID_FORWARD).Disable()
1498 if e.GetPage().AllowBack():
1499 self.FindWindowById(wx.ID_BACKWARD).Enable()
1501 self.FindWindowById(wx.ID_BACKWARD).Disable()