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)
109 class ImageButton(wx.Panel):
110 DefaultOverlay = wx.Bitmap(resources.getPathForImage('ImageButton_Overlay.png'))
113 __last_group__ = None
114 def __init__(self, parent, label, bitmap, extra_label=None, overlay=DefaultOverlay, style=None):
115 super(ImageButton, self).__init__(parent)
117 if style is ImageButton.IB_GROUP:
118 ImageButton.__last_group__ = self
119 ImageButton.__groups__[self] = [self]
122 if ImageButton.__last_group__:
123 ImageButton.__groups__[ImageButton.__last_group__].append(self)
124 self.group = ImageButton.__last_group__
127 self.sizer = wx.BoxSizer(wx.VERTICAL)
128 self.SetSizer(self.sizer)
130 self.overlay = self.createOverlay(bitmap, overlay)
131 self.text = wx.StaticText(self, -1, label)
132 self.bmp = wx.StaticBitmap(self, -1, self.bitmap)
134 self.extra_text = wx.StaticText(self, -1, extra_label)
135 self.selected = False
138 self.sizer.Add(self.text, 0, flag=wx.ALL|wx.ALIGN_CENTER, border=5)
139 self.sizer.Add(self.bmp, 1, flag=wx.ALL|wx.ALIGN_CENTER|wx.EXPAND, border=5)
141 self.sizer.Add(self.extra_text, 0, flag=wx.ALL|wx.ALIGN_CENTER, border=5)
142 self.bmp.Bind(wx.EVT_LEFT_UP, self.OnLeftClick)
146 ImageButton.__groups__[self.group].remove(self)
147 if self == self.group:
148 for ib in ImageButton.__groups__[self.group]:
150 del ImageButton.__groups__[self.group]
151 if ImageButton.__last_group__ == self:
152 ImageButton.__last_group__ = None
154 def OnLeftClick(self, e):
160 def SetValue(self, value):
161 old_value = self.selected
162 self.selected = bool(value)
163 self.bmp.SetBitmap(self.overlay if self.GetValue() else self.bitmap)
164 if self.selected and self.group:
165 for ib in ImageButton.__groups__[self.group]:
170 if self.callback and not old_value and self.selected:
173 def OnSelected(self, callback):
174 self.callback = callback
176 def createOverlay(self, bitmap, overlay):
177 result = bitmap.GetSubBitmap(wx.Rect(0, 0, *bitmap.Size))
178 (width, height) = bitmap.GetSize()
179 overlay_image = wx.ImageFromBitmap(overlay)
180 overlay_image = overlay_image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
181 overlay_scaled = wx.BitmapFromImage(overlay_image)
183 dc.SelectObject(result)
184 dc.DrawBitmap(overlay_scaled, 0, 0)
185 dc.SelectObject(wx.NullBitmap)
189 class InfoPage(wx.wizard.WizardPageSimple):
190 def __init__(self, parent, title):
191 wx.wizard.WizardPageSimple.__init__(self, parent)
193 parent.GetPageAreaSizer().Add(self)
194 sizer = wx.GridBagSizer(5, 5)
198 self.title = wx.StaticText(self, -1, title)
199 font = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)
200 self.title.SetFont(font)
201 # HACK ALERT: For some reason, the StaticText keeps its same size as if
202 # the font was not modified, this causes the text to wrap and to
203 # get out of bounds of the widgets area and hide other widgets.
204 # The only way I found for the widget to get its right size was to calculate
205 # the new font's extent and set the min size on the widget
208 w,h = dc.GetTextExtent(title)
209 self.title.SetMinSize((w, h))
210 sizer.Add(self.title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
211 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
212 sizer.AddGrowableCol(1)
216 def AddText(self, info):
217 text = wx.StaticText(self, -1, info)
218 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
222 def AddSeperator(self):
223 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
226 def AddHiddenSeperator(self):
229 def AddInfoBox(self):
230 infoBox = InfoBox(self)
231 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
235 def AddRadioButton(self, label, style=0):
236 radio = wx.RadioButton(self, -1, label, style=style)
237 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
241 def AddCheckbox(self, label, checked=False):
242 check = wx.CheckBox(self, -1)
243 text = wx.StaticText(self, -1, label)
244 check.SetValue(checked)
245 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
246 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
250 def AddButton(self, label):
251 button = wx.Button(self, -1, label)
252 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
256 def AddDualButton(self, label1, label2):
257 button1 = wx.Button(self, -1, label1)
258 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
259 button2 = wx.Button(self, -1, label2)
260 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
262 return button1, button2
264 def AddTextCtrl(self, value):
265 ret = wx.TextCtrl(self, -1, value)
266 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
270 def AddLabelTextCtrl(self, info, value):
271 text = wx.StaticText(self, -1, info)
272 ret = wx.TextCtrl(self, -1, value)
273 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
274 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
278 def AddTextCtrlButton(self, value, buttonText):
279 text = wx.TextCtrl(self, -1, value)
280 button = wx.Button(self, -1, buttonText)
281 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
282 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
286 def AddBitmap(self, bitmap):
287 bitmap = wx.StaticBitmap(self, -1, bitmap)
288 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
293 panel = wx.Panel(self, -1)
294 sizer = wx.GridBagSizer(2, 2)
295 panel.SetSizer(sizer)
296 self.GetSizer().Add(panel, pos=(self.rowNr, 0), span=(1, 2), flag=wx.ALL | wx.EXPAND)
300 def AddCheckmark(self, label, bitmap):
301 check = wx.StaticBitmap(self, -1, bitmap)
302 text = wx.StaticText(self, -1, label)
303 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
304 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
308 def AddCombo(self, label, options):
309 combo = wx.ComboBox(self, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
310 text = wx.StaticText(self, -1, label)
311 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER)
312 self.GetSizer().Add(combo, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
325 class PrintrbotPage(InfoPage):
326 def __init__(self, parent):
327 self._printer_info = [
328 # X, Y, Z, Nozzle Size, Filament Diameter, PrintTemperature, Print Speed, Travel Speed, Retract speed, Retract amount, use bed level sensor
329 ("Simple Metal", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, True),
330 ("Metal Plus", 250, 250, 250, 0.4, 1.75, 208, 40, 70, 30, 1, True),
331 ("Simple Makers Kit", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, True),
332 (":" + _("Older models"),),
333 ("Original", 130, 130, 130, 0.5, 2.95, 208, 40, 70, 30, 1, False),
334 ("Simple Maker's Edition v1", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
335 ("Simple Maker's Edition v2 (2013 Printrbot Simple)", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
336 ("Simple Maker's Edition v3 (2014 Printrbot Simple)", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
337 ("Jr v1", 115, 120, 80, 0.4, 1.75, 208, 40, 70, 30, 1, False),
338 ("Jr v2", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
339 ("LC v1", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
340 ("LC v2", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
341 ("Plus v1", 200, 200, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
342 ("Plus v2", 200, 200, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
343 ("Plus v2.1", 185, 220, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
344 ("Plus v2.2 (Model 1404/140422/140501/140507)", 250, 250, 250, 0.4, 1.75, 208, 40, 70, 30, 1, True),
345 ("Go v2 Large", 505, 306, 310, 0.4, 1.75, 208, 35, 70, 30, 1, True),
348 super(PrintrbotPage, self).__init__(parent, _("Printrbot Selection"))
349 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Printrbot_logo.png')))
350 self.AddText(_("Select which Printrbot machine you have:"))
352 for printer in self._printer_info:
353 if printer[0].startswith(":"):
355 self.AddText(printer[0][1:])
357 item = self.AddRadioButton(printer[0])
358 item.data = printer[1:]
359 self._items.append(item)
362 profile.putMachineSetting('machine_name', 'Printrbot ???')
363 for item in self._items:
366 profile.putMachineSetting('machine_name', 'Printrbot ' + item.GetLabel())
367 profile.putMachineSetting('machine_width', data[0])
368 profile.putMachineSetting('machine_depth', data[1])
369 profile.putMachineSetting('machine_height', data[2])
370 profile.putProfileSetting('nozzle_size', data[3])
371 profile.putProfileSetting('filament_diameter', data[4])
372 profile.putProfileSetting('print_temperature', data[5])
373 profile.putProfileSetting('print_speed', data[6])
374 profile.putProfileSetting('travel_speed', data[7])
375 profile.putProfileSetting('retraction_speed', data[8])
376 profile.putProfileSetting('retraction_amount', data[9])
377 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
378 profile.putMachineSetting('has_heated_bed', 'False')
379 profile.putMachineSetting('machine_center_is_zero', 'False')
380 profile.putMachineSetting('extruder_head_size_min_x', '0')
381 profile.putMachineSetting('extruder_head_size_min_y', '0')
382 profile.putMachineSetting('extruder_head_size_max_x', '0')
383 profile.putMachineSetting('extruder_head_size_max_y', '0')
384 profile.putMachineSetting('extruder_head_size_height', '0')
386 profile.setAlterationFile('start.gcode', """;Sliced at: {day} {date} {time}
387 ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}
388 ;Print time: {print_time}
389 ;Filament used: {filament_amount}m {filament_weight}g
390 ;Filament cost: {filament_cost}
391 ;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line
392 ;M109 S{print_temperature} ;Uncomment to add your own temperature line
394 G90 ;absolute positioning
395 M82 ;set extruder to absolute mode
396 M107 ;start with the fan off
397 G28 X0 Y0 ;move X/Y to min endstops
398 G28 Z0 ;move Z to min endstops
399 G29 ;Run the auto bed leveling
400 G1 Z15.0 F{travel_speed} ;move the platform down 15mm
401 G92 E0 ;zero the extruded length
402 G1 F200 E3 ;extrude 3mm of feed stock
403 G92 E0 ;zero the extruded length again
405 ;Put printing message on LCD screen
409 class OtherMachineSelectPage(InfoPage):
410 def __init__(self, parent):
411 super(OtherMachineSelectPage, self).__init__(parent, _("Other machine information"))
412 self.AddText(_("The following pre-defined machine profiles are available"))
413 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."))
415 machines = resources.getDefaultMachineProfiles()
417 for filename in machines:
418 name = os.path.splitext(os.path.basename(filename))[0]
419 item = self.AddRadioButton(name)
420 item.filename = filename
421 item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
422 self.options.append(item)
424 item = self.AddRadioButton(_('Custom...'))
426 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
428 def OnProfileSelect(self, e):
429 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
431 def OnOtherSelect(self, e):
432 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
435 for option in self.options:
436 if option.GetValue():
437 profile.loadProfile(option.filename)
438 profile.loadMachineSettings(option.filename)
440 class OtherMachineInfoPage(InfoPage):
441 def __init__(self, parent):
442 super(OtherMachineInfoPage, self).__init__(parent, _("Cura Ready!"))
443 self.AddText(_("Cura is now ready to be used!"))
445 class CustomRepRapInfoPage(InfoPage):
446 def __init__(self, parent):
447 super(CustomRepRapInfoPage, self).__init__(parent, _("Custom RepRap information"))
448 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
449 self.AddText(_("Be sure to review the default profile before running it on your machine."))
450 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
452 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
454 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
455 self.machineWidth = self.AddLabelTextCtrl(_("Machine width X (mm)"), "80")
456 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth Y (mm)"), "80")
457 self.machineHeight = self.AddLabelTextCtrl(_("Machine height Z (mm)"), "55")
458 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
459 self.heatedBed = self.AddCheckbox(_("Heated bed"))
460 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
463 profile.putMachineSetting('machine_name', self.machineName.GetValue())
464 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
465 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
466 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
467 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
468 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
469 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
470 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
471 profile.putMachineSetting('extruder_head_size_min_x', '0')
472 profile.putMachineSetting('extruder_head_size_min_y', '0')
473 profile.putMachineSetting('extruder_head_size_max_x', '0')
474 profile.putMachineSetting('extruder_head_size_max_y', '0')
475 profile.putMachineSetting('extruder_head_size_height', '0')
476 profile.checkAndUpdateMachineName()
478 class MachineSelectPage(InfoPage):
479 def __init__(self, parent):
480 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
481 self.AddText(_("What kind of machine do you have:"))
483 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2")
484 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
485 self.Ultimaker2ExtRadio = self.AddRadioButton("Ultimaker2extended")
486 self.Ultimaker2ExtRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
487 self.Ultimaker2GoRadio = self.AddRadioButton("Ultimaker2go")
488 self.Ultimaker2GoRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
489 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
490 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
491 self.UltimakerOPRadio = self.AddRadioButton("Ultimaker Original+")
492 self.UltimakerOPRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerOPSelect)
493 self.PrintrbotRadio = self.AddRadioButton("Printrbot")
494 self.PrintrbotRadio.Bind(wx.EVT_RADIOBUTTON, self.OnPrintrbotSelect)
495 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot, Witbox)"))
496 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
498 def OnUltimaker2Select(self, e):
499 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
501 def OnUltimakerSelect(self, e):
502 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
504 def OnUltimakerOPSelect(self, e):
505 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
507 def OnPrintrbotSelect(self, e):
508 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().printrbotSelectType)
510 def OnOtherSelect(self, e):
511 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
514 profile.putProfileSetting('retraction_enable', 'True')
515 if self.Ultimaker2Radio.GetValue() or self.Ultimaker2GoRadio.GetValue() or self.Ultimaker2ExtRadio.GetValue():
516 if self.Ultimaker2Radio.GetValue():
517 profile.putMachineSetting('machine_width', '230')
518 profile.putMachineSetting('machine_depth', '225')
519 profile.putMachineSetting('machine_height', '205')
520 profile.putMachineSetting('machine_name', 'ultimaker2')
521 profile.putMachineSetting('machine_type', 'ultimaker2')
522 profile.putMachineSetting('has_heated_bed', 'True')
523 if self.Ultimaker2GoRadio.GetValue():
524 profile.putMachineSetting('machine_width', '120')
525 profile.putMachineSetting('machine_depth', '120')
526 profile.putMachineSetting('machine_height', '115')
527 profile.putMachineSetting('machine_name', 'ultimaker2go')
528 profile.putMachineSetting('machine_type', 'ultimaker2go')
529 profile.putMachineSetting('has_heated_bed', 'False')
530 if self.Ultimaker2ExtRadio.GetValue():
531 profile.putMachineSetting('machine_width', '230')
532 profile.putMachineSetting('machine_depth', '225')
533 profile.putMachineSetting('machine_height', '315')
534 profile.putMachineSetting('machine_name', 'ultimaker2extended')
535 profile.putMachineSetting('machine_type', 'ultimaker2extended')
536 profile.putMachineSetting('has_heated_bed', 'False')
537 profile.putMachineSetting('machine_center_is_zero', 'False')
538 profile.putMachineSetting('gcode_flavor', 'UltiGCode')
539 profile.putMachineSetting('extruder_head_size_min_x', '40.0')
540 profile.putMachineSetting('extruder_head_size_min_y', '10.0')
541 profile.putMachineSetting('extruder_head_size_max_x', '60.0')
542 profile.putMachineSetting('extruder_head_size_max_y', '30.0')
543 profile.putMachineSetting('extruder_head_size_height', '48.0')
544 profile.putProfileSetting('nozzle_size', '0.4')
545 profile.putProfileSetting('fan_full_height', '5.0')
546 profile.putMachineSetting('extruder_offset_x1', '18.0')
547 profile.putMachineSetting('extruder_offset_y1', '0.0')
548 elif self.UltimakerRadio.GetValue():
549 profile.putMachineSetting('machine_width', '205')
550 profile.putMachineSetting('machine_depth', '205')
551 profile.putMachineSetting('machine_height', '200')
552 profile.putMachineSetting('machine_name', 'ultimaker original')
553 profile.putMachineSetting('machine_type', 'ultimaker')
554 profile.putMachineSetting('machine_center_is_zero', 'False')
555 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
556 profile.putProfileSetting('nozzle_size', '0.4')
557 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
558 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
559 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
560 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
561 profile.putMachineSetting('extruder_head_size_height', '55.0')
562 elif self.UltimakerOPRadio.GetValue():
563 profile.putMachineSetting('machine_width', '205')
564 profile.putMachineSetting('machine_depth', '205')
565 profile.putMachineSetting('machine_height', '200')
566 profile.putMachineSetting('machine_name', 'ultimaker original+')
567 profile.putMachineSetting('machine_type', 'ultimaker_plus')
568 profile.putMachineSetting('machine_center_is_zero', 'False')
569 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
570 profile.putProfileSetting('nozzle_size', '0.4')
571 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
572 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
573 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
574 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
575 profile.putMachineSetting('extruder_head_size_height', '55.0')
576 profile.putMachineSetting('has_heated_bed', 'True')
577 profile.putMachineSetting('extruder_amount', '1')
578 profile.putProfileSetting('retraction_enable', 'True')
580 profile.putMachineSetting('machine_width', '80')
581 profile.putMachineSetting('machine_depth', '80')
582 profile.putMachineSetting('machine_height', '60')
583 profile.putMachineSetting('machine_name', 'reprap')
584 profile.putMachineSetting('machine_type', 'reprap')
585 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
586 profile.putPreference('startMode', 'Normal')
587 profile.putProfileSetting('nozzle_size', '0.5')
588 profile.checkAndUpdateMachineName()
589 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
591 class SelectParts(InfoPage):
592 def __init__(self, parent):
593 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
594 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."))
596 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
597 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
598 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
599 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
601 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."))
602 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
603 self.springExtruder.SetValue(True)
606 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
607 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
608 profile.putMachineSetting('has_heated_bed', 'True')
610 profile.putMachineSetting('has_heated_bed', 'False')
611 if self.dualExtrusion.GetValue():
612 profile.putMachineSetting('extruder_amount', '2')
613 profile.putMachineSetting('machine_depth', '195')
615 profile.putMachineSetting('extruder_amount', '1')
616 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
617 profile.putProfileSetting('retraction_enable', 'True')
619 profile.putProfileSetting('retraction_enable', 'False')
622 class UltimakerFirmwareUpgradePage(InfoPage):
623 def __init__(self, parent):
624 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
625 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."))
626 self.AddHiddenSeperator()
627 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
628 self.AddHiddenSeperator()
629 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."))
630 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Upgrade to Marlin firmware'), _('Skip upgrade'))
631 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
632 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
633 self.AddHiddenSeperator()
634 if profile.getMachineSetting('machine_type') == 'ultimaker':
635 self.AddText(_("Do not upgrade to this firmware if:"))
636 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
637 self.AddText(_("* Build your own heated bed"))
638 self.AddText(_("* Have other changes in the firmware"))
639 # button = self.AddButton('Goto this page for a custom firmware')
640 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
645 def OnUpgradeClick(self, e):
646 if firmwareInstall.InstallFirmware():
647 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
649 def OnSkipClick(self, e):
650 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
651 self.GetParent().ShowPage(self.GetNext())
653 def OnUrlClick(self, e):
654 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
656 class UltimakerCheckupPage(InfoPage):
657 def __init__(self, parent):
658 super(UltimakerCheckupPage, self).__init__(parent, _("Ultimaker Checkup"))
660 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
661 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
662 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
663 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
664 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
665 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
666 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
667 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
668 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
669 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
672 _("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."))
673 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
674 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
675 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
677 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
678 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
679 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
681 self.infoBox = self.AddInfoBox()
682 self.machineState = self.AddText("")
683 self.temperatureLabel = self.AddText("")
684 self.errorLogButton = self.AddButton(_("Show error log"))
685 self.errorLogButton.Show(False)
687 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
689 self.xMinStop = False
690 self.xMaxStop = False
691 self.yMinStop = False
692 self.yMaxStop = False
693 self.zMinStop = False
694 self.zMaxStop = False
696 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
699 if self.comm is not None:
703 self.endstopBitmap.Show(False)
706 def OnSkipClick(self, e):
707 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
708 self.GetParent().ShowPage(self.GetNext())
710 def OnCheckClick(self, e=None):
711 self.errorLogButton.Show(False)
712 if self.comm is not None:
716 wx.CallAfter(self.OnCheckClick)
718 self.infoBox.SetBusy(_("Connecting to machine."))
719 self.commState.SetBitmap(self.unknownBitmap)
720 self.tempState.SetBitmap(self.unknownBitmap)
721 self.stopState.SetBitmap(self.unknownBitmap)
722 self.checkupState = 0
723 self.checkExtruderNr = 0
724 self.comm = machineCom.MachineCom(callbackObject=self)
726 def OnErrorLog(self, e):
727 printWindow.LogWindow('\n'.join(self.comm.getLog()))
729 def mcLog(self, message):
732 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
733 if not self.comm.isOperational():
735 if self.checkupState == 0:
736 self.tempCheckTimeout = 20
737 if temp[self.checkExtruderNr] > 70:
738 self.checkupState = 1
739 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
740 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
741 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
743 self.startTemp = temp[self.checkExtruderNr]
744 self.checkupState = 2
745 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
746 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
747 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
748 elif self.checkupState == 1:
749 if temp[self.checkExtruderNr] < 60:
750 self.startTemp = temp[self.checkExtruderNr]
751 self.checkupState = 2
752 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
753 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
754 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
755 elif self.checkupState == 2:
756 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
757 if temp[self.checkExtruderNr] > self.startTemp + 40:
758 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
759 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
760 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
761 self.checkExtruderNr = 0
762 self.checkupState = 3
763 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
764 wx.CallAfter(self.endstopBitmap.Show, True)
765 wx.CallAfter(self.Layout)
766 self.comm.sendCommand('M119')
767 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
769 self.checkupState = 0
770 self.checkExtruderNr += 1
772 self.tempCheckTimeout -= 1
773 if self.tempCheckTimeout < 1:
774 self.checkupState = -1
775 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
776 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
777 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
778 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
779 elif self.checkupState >= 3 and self.checkupState < 10:
780 self.comm.sendCommand('M119')
781 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
783 def mcStateChange(self, state):
784 if self.comm is None:
786 if self.comm.isOperational():
787 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
788 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
789 elif self.comm.isError():
790 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
791 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
792 wx.CallAfter(self.endstopBitmap.Show, False)
793 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
794 wx.CallAfter(self.errorLogButton.Show, True)
795 wx.CallAfter(self.Layout)
797 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
799 def mcMessage(self, message):
800 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
801 for data in message.split(' '):
803 tag, value = data.split(':', 1)
805 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
807 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
809 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
811 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
813 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
815 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
817 tag, value = map(str.strip, message.split(':', 1))
819 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
821 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
823 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
825 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
827 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
829 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
830 if 'z_max' in message:
831 self.comm.sendCommand('M119')
833 if self.checkupState == 3:
834 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
835 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
836 self.checkupState = 5
837 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
838 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
840 self.checkupState = 4
841 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
842 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
843 elif self.checkupState == 4:
844 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
845 self.checkupState = 5
846 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
847 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
848 elif self.checkupState == 5:
849 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
850 self.checkupState = 6
851 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
852 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
853 elif self.checkupState == 6:
854 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
855 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
856 self.checkupState = 8
857 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
858 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
860 self.checkupState = 7
861 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
862 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
863 elif self.checkupState == 7:
864 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
865 self.checkupState = 8
866 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
867 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
868 elif self.checkupState == 8:
869 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
870 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
871 self.checkupState = 10
873 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
874 wx.CallAfter(self.infoBox.SetReadyIndicator)
875 wx.CallAfter(self.endstopBitmap.Show, False)
876 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
877 wx.CallAfter(self.OnSkipClick, None)
879 self.checkupState = 9
880 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
881 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
882 elif self.checkupState == 9:
883 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
884 self.checkupState = 10
886 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
887 wx.CallAfter(self.infoBox.SetReadyIndicator)
888 wx.CallAfter(self.endstopBitmap.Show, False)
889 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
890 wx.CallAfter(self.OnSkipClick, None)
892 def mcProgress(self, lineNr):
895 def mcZChange(self, newZ):
899 class UltimakerCalibrationPage(InfoPage):
900 def __init__(self, parent):
901 super(UltimakerCalibrationPage, self).__init__(parent, _("Ultimaker Calibration"))
903 self.AddText("Your Ultimaker requires some calibration.")
904 self.AddText("This calibration is needed for a proper extrusion amount.")
906 self.AddText("The following values are needed:")
907 self.AddText("* Diameter of filament")
908 self.AddText("* Number of steps per mm of filament extrusion")
910 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
912 self.AddText("First we need the diameter of your filament:")
913 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
915 "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.")
916 self.AddText("Note: This value can be changed later at any time.")
919 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
922 class UltimakerCalibrateStepsPerEPage(InfoPage):
923 def __init__(self, parent):
924 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, _("Ultimaker Calibration"))
926 #if profile.getMachineSetting('steps_per_e') == '0':
927 # profile.putMachineSetting('steps_per_e', '865.888')
929 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
930 self.AddText(_("First remove any filament from your machine."))
931 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
932 self.AddText(_("We'll push the filament 100mm"))
933 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
934 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
935 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
936 self.AddText(_("This results in the following steps per E:"))
937 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
938 self.AddText(_("You can repeat these steps to get better calibration."))
941 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
942 self.heatButton = self.AddButton(_("Heatup for filament removal"))
944 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
945 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
946 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
948 def OnSaveLengthClick(self, e):
949 currentEValue = float(self.stepsPerEInput.GetValue())
950 realExtrudeLength = float(self.lengthInput.GetValue())
951 newEValue = currentEValue * 100 / realExtrudeLength
952 self.stepsPerEInput.SetValue(str(newEValue))
953 self.lengthInput.SetValue("100")
955 def OnExtrudeClick(self, e):
956 t = threading.Thread(target=self.OnExtrudeRun)
960 def OnExtrudeRun(self):
961 self.heatButton.Enable(False)
962 self.extrudeButton.Enable(False)
963 currentEValue = float(self.stepsPerEInput.GetValue())
964 self.comm = machineCom.MachineCom()
965 if not self.comm.isOpen():
967 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
968 'Printer error', wx.OK | wx.ICON_INFORMATION)
969 self.heatButton.Enable(True)
970 self.extrudeButton.Enable(True)
973 line = self.comm.readline()
978 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
981 self.sendGCommand('M302') #Disable cold extrusion protection
982 self.sendGCommand("M92 E%f" % (currentEValue))
983 self.sendGCommand("G92 E0")
984 self.sendGCommand("G1 E100 F600")
987 self.extrudeButton.Enable()
988 self.heatButton.Enable()
990 def OnHeatClick(self, e):
991 t = threading.Thread(target=self.OnHeatRun)
996 self.heatButton.Enable(False)
997 self.extrudeButton.Enable(False)
998 self.comm = machineCom.MachineCom()
999 if not self.comm.isOpen():
1001 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
1002 'Printer error', wx.OK | wx.ICON_INFORMATION)
1003 self.heatButton.Enable(True)
1004 self.extrudeButton.Enable(True)
1007 line = self.comm.readline()
1009 self.heatButton.Enable(True)
1010 self.extrudeButton.Enable(True)
1014 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
1017 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
1019 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
1020 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
1021 self.sendGCommand('M104 S0')
1024 self.heatButton.Enable(True)
1025 self.extrudeButton.Enable(True)
1027 def sendGCommand(self, cmd):
1028 self.comm.sendCommand(cmd) #Disable cold extrusion protection
1030 line = self.comm.readline()
1033 if line.startswith('ok'):
1036 def StoreData(self):
1037 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
1039 class Ultimaker2ReadyPage(InfoPage):
1040 def __init__(self, parent):
1041 super(Ultimaker2ReadyPage, self).__init__(parent, _("Ultimaker2"))
1042 self.AddText(_('Congratulations on your the purchase of your brand new Ultimaker2.'))
1043 self.AddText(_('Cura is now ready to be used with your Ultimaker2.'))
1046 class LulzbotMachineSelectPage(InfoPage):
1050 def __init__(self, parent):
1051 super(LulzbotMachineSelectPage, self).__init__(parent, _("Select your machine"))
1052 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1053 self.AddText(_("Select your printer :"))
1055 self.panel = self.AddPanel()
1057 self.LulzbotMini = ImageButton(self.panel, _("LulzBot Mini"), self.GetBitmap('Lulzbot_mini.jpg'), style=ImageButton.IB_GROUP)
1058 self.LulzbotMini.OnSelected(self.OnLulzbotSelected)
1059 self.LulzbotMini.SetValue(True)
1060 self.LulzbotTaz5 = ImageButton(self.panel, _("LulzBot TAZ 5"), self.GetBitmap('Lulzbot_TAZ5.png'))
1061 self.LulzbotTaz5.OnSelected(self.OnLulzbotSelected)
1062 self.LulzbotTaz4 = ImageButton(self.panel, _("LulzBot TAZ 4"), self.GetBitmap('Lulzbot_TAZ4.jpg'))
1063 self.LulzbotTaz4.OnSelected(self.OnLulzbotSelected)
1064 self.OtherPrinters = ImageButton(self.panel, _("Non-LulzBot Printers"), self.GetBitmap('Other_Printers.jpg'))
1065 self.OtherPrinters.OnSelected(self.OnOthersSelected)
1067 self.panel.GetSizer().Add(self.LulzbotMini, pos=(0, 0))
1068 self.panel.GetSizer().Add(self.LulzbotTaz4, pos=(0, 1))
1069 self.panel.GetSizer().Add(self.LulzbotTaz5, pos=(1, 0))
1070 self.panel.GetSizer().Add(self.OtherPrinters, pos=(1, 1))
1072 def GetBitmap(self, filename):
1073 image = wx.Image(resources.getPathForImage(filename))
1074 image_scaled = image.Scale(LulzbotMachineSelectPage.IMAGE_WIDTH,
1075 LulzbotMachineSelectPage.IMAGE_HEIGHT, wx.IMAGE_QUALITY_HIGH)
1076 return wx.BitmapFromImage(image_scaled)
1078 def OnOthersSelected(self):
1079 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().machineSelectPage)
1081 def OnLulzbotSelected(self):
1082 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotToolheadPage)
1084 def AllowNext(self):
1087 def AllowBack(self):
1090 def StoreData(self):
1091 if self.LulzbotTaz4.GetValue() or self.LulzbotTaz5.GetValue() or self.LulzbotMini.GetValue():
1092 if self.LulzbotTaz4.GetValue():
1093 profile.putMachineSetting('machine_width', '290')
1094 profile.putMachineSetting('machine_depth', '275')
1095 profile.putMachineSetting('machine_height', '250')
1096 profile.putProfileSetting('nozzle_size', '0.35')
1097 profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1098 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4')
1099 profile.putMachineSetting('serial_baud', '115200')
1100 elif self.LulzbotTaz5.GetValue():
1101 profile.putMachineSetting('machine_width', '290')
1102 profile.putMachineSetting('machine_depth', '275')
1103 profile.putMachineSetting('machine_height', '250')
1104 profile.putMachineSetting('serial_baud', '115200')
1105 # Machine type and name are set in the nozzle select page
1107 profile.putMachineSetting('machine_width', '155')
1108 profile.putMachineSetting('machine_depth', '155')
1109 profile.putMachineSetting('machine_height', '163')
1110 profile.putProfileSetting('nozzle_size', '0.5')
1111 profile.putMachineSetting('machine_name', 'LulzBot Mini')
1112 profile.putMachineSetting('machine_type', 'lulzbot_mini')
1113 profile.putMachineSetting('serial_baud', '115200')
1114 profile.putMachineSetting('extruder_head_size_min_x', '40')
1115 profile.putMachineSetting('extruder_head_size_max_x', '75')
1116 profile.putMachineSetting('extruder_head_size_min_y', '25')
1117 profile.putMachineSetting('extruder_head_size_max_y', '55')
1118 profile.putMachineSetting('extruder_head_size_height', '17')
1120 profile.putMachineSetting('machine_center_is_zero', 'False')
1121 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
1122 profile.putMachineSetting('has_heated_bed', 'True')
1123 profile.putMachineSetting('extruder_head_size_min_x', '0.0')
1124 profile.putMachineSetting('extruder_head_size_min_y', '0.0')
1125 profile.putMachineSetting('extruder_head_size_max_x', '0.0')
1126 profile.putMachineSetting('extruder_head_size_max_y', '0.0')
1127 profile.putMachineSetting('extruder_head_size_height', '0.0')
1128 profile.putProfileSetting('retraction_enable', 'True')
1129 profile.putPreference('startMode', 'Simple')
1130 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
1131 profile.checkAndUpdateMachineName()
1133 class LulzbotReadyPage(InfoPage):
1134 def __init__(self, parent):
1135 super(LulzbotReadyPage, self).__init__(parent, _("LulzBot TAZ/Mini"))
1136 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1137 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
1139 self.AddText(_('For more information about using Cura with your LulzBot'))
1140 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
1143 class LulzbotToolheadSelectPage(InfoPage):
1144 url='http://lulzbot.com/toolhead-identification'
1146 def __init__(self, parent):
1147 super(LulzbotToolheadSelectPage, self).__init__(parent, _("LulzBot Toolhead Selection"))
1149 self.mini_choices = [_('Standard'), _('Flexystruder')]
1150 self.taz_choices = [_('Standard v1'),
1151 _('Standard v2 0.35 mm nozzle'), _('Standard v2 0.5 mm nozzle'),
1152 _('Flexystruder v1'), _('Flexystruder v2'),
1153 _('Dually v1'), _('Dually v2'),
1154 _('FlexyDually v1'), _('FlexyDually v2')]
1155 self.description_map = {
1156 _('Standard'): _('This is the standard toolhead that comes with the Lulzbot Mini'),
1157 _('Flexystruder'): _('This is the Flexystruder for the Lulzbot Mini\nIt is used for printing Flexible materials'),
1158 _('Standard v1'): _('This is the standard toolhead that comes with the Lulzbot TAZ 1-2-3 and TAZ 4.\nIt uses the Budaschnozzle for the hotend'),
1159 _('Standard v2 0.35 mm nozzle'): _('This is the standard toolhead that comes with the Lulzbot TAZ 5.\nIt uses the Hexagon hotend and a 0.35 mm nozzle'),
1160 _('Standard v2 0.5 mm nozzle'): _('This is the standard toolhead that comes with the Lulzbot TAZ 5.\nIt uses the Hexagon hotend and a 0.5 mm nozzle'),
1161 _('Flexystruder v1'): _('It\'s the flexy!'),
1162 _('Flexystruder v2'): _('It\'s the flexy v2!'),
1163 _('Dually v1'): _('It\'s the dualy v1!'),
1164 _('Dually v2'): _('It\'s the dual v2!'),
1165 _('FlexyDually v1'): _('It\'s the flexy dually v1!'),
1166 _('FlexyDually v2'): _('It\'s the flexy dual v2!')
1169 _('Standard'): 'Lulzbot_Toolhead_Mini_Standard.jpg',
1170 _('Flexystruder'): 'Lulzbot_logo.png',
1171 _('Standard v1'): 'Lulzbot_logo.png',
1172 _('Standard v2 0.35 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1173 _('Standard v2 0.5 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1174 _('Flexystruder v1'): 'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg',
1175 _('Flexystruder v2'): 'Lulzbot_logo.png',
1176 _('Dually v1'): 'Lulzbot_Toolhead_TAZ_Dually_v1.jpg',
1177 _('Dually v2'): 'Lulzbot_logo.png',
1178 _('FlexyDually v1'): 'Lulzbot_logo.png',
1179 _('FlexyDually v2'): 'Lulzbot_logo.png'
1181 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1182 printer_name = profile.getMachineSetting('machine_type')
1183 self.Bind(wx.wizard.EVT_WIZARD_PAGE_SHOWN, self.OnPageShown)
1185 self.AddText(_('Please select your currently installed Tool Head'))
1186 txt = self.AddText(_('It is important to select the correct Tool head for your printer.\n' +
1187 'Flashing the wrong firmware on your printer can cause damage to your printer and to your toolhead\n' +
1188 'If you are not sure which toolhead you have, please refer to this webpage for more information: '))
1189 txt.SetForegroundColour(wx.RED)
1190 button = self.AddButton(self.url)
1191 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1194 #self.combo = self.AddCombo(_('Currently installed Toolhead'), [''])
1195 #self.combo.SetEditable(False)
1196 #self.combo.Bind(wx.EVT_COMBOBOX, self.OnToolheadSelected)
1197 #self.description = self.AddText('\n\n')
1198 #self.description.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD))
1199 #self.image = self.AddBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[self.mini_choices[0]])))
1200 self.panel = self.AddPanel()
1201 ib1 = ImageButton(self.panel, "Mini", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg')), "Some description", style=ImageButton.IB_GROUP)
1202 ib2 = ImageButton(self.panel, "TAZ 4 ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Single_v2.jpg')), "Some description 2")
1203 ib3 = ImageButton(self.panel, "TAZ 5 ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg')))
1204 ib4 = ImageButton(self.panel, "Others ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Dually_v1.jpg')))
1205 self.panel.GetSizer().Add(ib1, pos=(0, 0), flag=wx.EXPAND)
1206 self.panel.GetSizer().Add(ib2, pos=(0, 1), flag=wx.EXPAND)
1207 self.panel.GetSizer().Add(ib3, pos=(1, 0), flag=wx.EXPAND)
1208 self.panel.GetSizer().Add(ib4, pos=(1, 1), flag=wx.EXPAND)
1211 def OnPageShown(self, e):
1212 printer_name = profile.getMachineSetting('machine_type')
1213 if printer_name == 'lulzbot_mini':
1214 choices = self.mini_choices
1217 choices = self.taz_choices
1218 if printer_name == 'lulzbot_TAZ_4':
1220 elif printer_name == 'lulzbot_TAZ_5':
1226 self.combo.AppendItems(choices)
1227 self.combo.SetValue(choices[default])
1228 self.OnToolheadSelected(e)
1230 def OnUrlClick(self, e):
1231 webbrowser.open(LulzbotToolheadSelectPage.url)
1233 def OnToolheadSelected(self, e):
1234 toolhead = self.combo.GetValue()
1235 if self.description_map.has_key(toolhead):
1236 self.image.SetBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[toolhead])))
1237 self.description.SetLabel(self.description_map[toolhead])
1239 self.image.SetBitmap(wx.NullBitmap)
1240 self.description.SetLabel('\n\n')
1245 class Taz5NozzleSelectPage(InfoPage):
1246 url='http://lulzbot.com/printer-identification'
1248 def __init__(self, parent):
1249 super(Taz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ5"))
1250 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1252 self.AddText(_(' '))
1253 self.AddText(_('Please select nozzle size:'))
1254 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1255 self.Nozzle35Radio.SetValue(True)
1256 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1257 self.AddText(_(' '))
1260 self.AddText(_('If you are not sure which nozzle size you have'))
1261 self.AddText(_('please check this webpage: '))
1262 button = self.AddButton(Taz5NozzleSelectPage.url)
1263 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1265 def OnUrlClick(self, e):
1266 webbrowser.open(Taz5NozzleSelectPage.url)
1268 def StoreData(self):
1269 if self.Nozzle35Radio.GetValue():
1270 profile.putProfileSetting('nozzle_size', '0.35')
1271 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.35 nozzle)')
1272 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1275 profile.putProfileSetting('nozzle_size', '0.5')
1276 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.5 nozzle)')
1277 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_05nozzle')
1279 class ConfigWizard(wx.wizard.Wizard):
1280 def __init__(self, addNew = False):
1281 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1283 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1285 profile.setActiveMachine(profile.getMachineCount())
1287 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1288 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1289 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1291 self.machineSelectPage = MachineSelectPage(self)
1292 self.ultimakerSelectParts = SelectParts(self)
1293 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1294 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1295 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1296 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1297 self.bedLevelPage = bedLevelWizardMain(self)
1298 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1299 self.printrbotSelectType = PrintrbotPage(self)
1300 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1301 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1302 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1304 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1305 self.lulzbotReadyPage = LulzbotReadyPage(self)
1306 self.taz5NozzleSelectPage = Taz5NozzleSelectPage(self)
1307 self.lulzbotToolheadPage = LulzbotToolheadSelectPage(self)
1308 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1310 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotToolheadPage)
1311 wx.wizard.WizardPageSimple.Chain(self.lulzbotToolheadPage, self.lulzbotReadyPage)
1312 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1313 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1314 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1315 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1316 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1317 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1319 self.RunWizard(self.lulzbotMachineSelectPage)
1322 def OnPageChanging(self, e):
1323 e.GetPage().StoreData()
1325 def OnPageChanged(self, e):
1326 if e.GetPage().AllowNext():
1327 self.FindWindowById(wx.ID_FORWARD).Enable()
1329 self.FindWindowById(wx.ID_FORWARD).Disable()
1330 if e.GetPage().AllowBack():
1331 self.FindWindowById(wx.ID_BACKWARD).Enable()
1333 self.FindWindowById(wx.ID_BACKWARD).Disable()
1335 def OnCancel(self, e):
1336 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1337 profile.setActiveMachine(self._old_machine_index)
1338 profile.removeMachine(new_machine_index)
1340 class bedLevelWizardMain(InfoPage):
1341 def __init__(self, parent):
1342 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1344 self.AddText(_('This wizard will help you in leveling your printer bed'))
1346 self.AddText(_('It will do the following steps'))
1347 self.AddText(_('* Move the printer head to each corner'))
1348 self.AddText(_(' and let you adjust the height of the bed to the nozzle'))
1349 self.AddText(_('* Print a line around the bed to check if it is level'))
1352 self.connectButton = self.AddButton(_('Connect to printer'))
1355 self.infoBox = self.AddInfoBox()
1356 self.resumeButton = self.AddButton(_('Resume'))
1357 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1358 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1359 self.resumeButton.Enable(False)
1361 self.upButton.Enable(False)
1362 self.downButton.Enable(False)
1363 self.upButton2.Enable(False)
1364 self.downButton2.Enable(False)
1366 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1367 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1368 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1369 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1370 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1371 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1373 def OnConnect(self, e = None):
1374 if self.comm is not None:
1378 wx.CallAfter(self.OnConnect)
1380 self.connectButton.Enable(False)
1381 self.comm = machineCom.MachineCom(callbackObject=self)
1382 self.infoBox.SetBusy(_('Connecting to machine.'))
1383 self._wizardState = 0
1385 def OnBedUp(self, e):
1386 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1387 self.comm.sendCommand('G92 Z10')
1388 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1389 self.comm.sendCommand('M400')
1391 def OnBedDown(self, e):
1392 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1393 self.comm.sendCommand('G92 Z10')
1394 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1395 self.comm.sendCommand('M400')
1397 def OnBedUp2(self, e):
1398 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1399 self.comm.sendCommand('G92 Z10')
1400 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1401 self.comm.sendCommand('M400')
1403 def OnBedDown2(self, e):
1404 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1405 self.comm.sendCommand('G92 Z10')
1406 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1407 self.comm.sendCommand('M400')
1409 def AllowNext(self):
1410 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1411 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1414 def OnResume(self, e):
1415 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1416 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1417 if self._wizardState == -1:
1418 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1419 wx.CallAfter(self.upButton.Enable, False)
1420 wx.CallAfter(self.downButton.Enable, False)
1421 wx.CallAfter(self.upButton2.Enable, False)
1422 wx.CallAfter(self.downButton2.Enable, False)
1423 self.comm.sendCommand('M105')
1424 self.comm.sendCommand('G28')
1425 self._wizardState = 1
1426 elif self._wizardState == 2:
1427 if profile.getMachineSetting('has_heated_bed') == 'True':
1428 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1429 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1430 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1431 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1432 self.comm.sendCommand('M400')
1433 self._wizardState = 3
1435 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1436 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1437 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1438 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1439 self.comm.sendCommand('M400')
1440 self._wizardState = 3
1441 elif self._wizardState == 4:
1442 if profile.getMachineSetting('has_heated_bed') == 'True':
1443 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1444 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1445 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1446 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1447 self.comm.sendCommand('M400')
1448 self._wizardState = 7
1450 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1451 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1452 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1453 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1454 self.comm.sendCommand('M400')
1455 self._wizardState = 5
1456 elif self._wizardState == 6:
1457 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1458 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1459 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1460 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1461 self.comm.sendCommand('M400')
1462 self._wizardState = 7
1463 elif self._wizardState == 8:
1464 wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1465 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1466 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1467 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1468 self._wizardState = 9
1469 elif self._wizardState == 10:
1470 self._wizardState = 11
1471 wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1472 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1473 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1474 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1475 w = profile.getMachineSettingFloat('machine_width') - 10
1476 d = profile.getMachineSettingFloat('machine_depth')
1477 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1478 filamentArea = math.pi * filamentRadius * filamentRadius
1479 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1483 'G1 Z2 F%d' % (feedZ),
1485 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1486 'G1 Z0.3 F%d' % (feedZ)]
1488 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1490 for i in xrange(0, 3):
1491 dist = 5.0 + 0.4 * float(i)
1492 eValue += (d - 2.0*dist) * ePerMM
1493 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1494 eValue += (w - 2.0*dist) * ePerMM
1495 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1496 eValue += (d - 2.0*dist) * ePerMM
1497 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1498 eValue += (w - 2.0*dist) * ePerMM
1499 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1501 gcodeList.append('M400')
1502 self.comm.printGCode(gcodeList)
1503 self.resumeButton.Enable(False)
1505 def mcLog(self, message):
1506 print 'Log:', message
1508 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1509 if self._wizardState == 1:
1510 self._wizardState = 2
1511 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1512 wx.CallAfter(self.resumeButton.Enable, True)
1513 elif self._wizardState == 3:
1514 self._wizardState = 4
1515 if profile.getMachineSetting('has_heated_bed') == 'True':
1516 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1518 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1519 wx.CallAfter(self.resumeButton.Enable, True)
1520 elif self._wizardState == 5:
1521 self._wizardState = 6
1522 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1523 wx.CallAfter(self.resumeButton.Enable, True)
1524 elif self._wizardState == 7:
1525 self._wizardState = 8
1526 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1527 wx.CallAfter(self.resumeButton.Enable, True)
1528 elif self._wizardState == 9:
1529 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1530 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1532 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1533 wx.CallAfter(self.resumeButton.Enable, True)
1534 self._wizardState = 10
1536 def mcStateChange(self, state):
1537 if self.comm is None:
1539 if self.comm.isOperational():
1540 if self._wizardState == 0:
1541 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1542 wx.CallAfter(self.upButton.Enable, True)
1543 wx.CallAfter(self.downButton.Enable, True)
1544 wx.CallAfter(self.upButton2.Enable, True)
1545 wx.CallAfter(self.downButton2.Enable, True)
1546 wx.CallAfter(self.resumeButton.Enable, True)
1547 self._wizardState = -1
1548 elif self._wizardState == 11 and not self.comm.isPrinting():
1549 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1550 self.comm.sendCommand('G92 E0')
1551 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1552 self.comm.sendCommand('M104 S0')
1553 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1554 wx.CallAfter(self.infoBox.SetReadyIndicator)
1555 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1556 wx.CallAfter(self.connectButton.Enable, True)
1557 self._wizardState = 12
1558 elif self.comm.isError():
1559 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1561 def mcMessage(self, message):
1564 def mcProgress(self, lineNr):
1567 def mcZChange(self, newZ):
1570 class headOffsetCalibrationPage(InfoPage):
1571 def __init__(self, parent):
1572 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1574 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1577 self.connectButton = self.AddButton(_('Connect to printer'))
1580 self.infoBox = self.AddInfoBox()
1581 self.textEntry = self.AddTextCtrl('')
1582 self.textEntry.Enable(False)
1583 self.resumeButton = self.AddButton(_('Resume'))
1584 self.resumeButton.Enable(False)
1586 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1587 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1589 def AllowBack(self):
1592 def OnConnect(self, e = None):
1593 if self.comm is not None:
1597 wx.CallAfter(self.OnConnect)
1599 self.connectButton.Enable(False)
1600 self.comm = machineCom.MachineCom(callbackObject=self)
1601 self.infoBox.SetBusy(_('Connecting to machine.'))
1602 self._wizardState = 0
1604 def OnResume(self, e):
1605 if self._wizardState == 2:
1606 self._wizardState = 3
1607 wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1609 w = profile.getMachineSettingFloat('machine_width')
1610 d = profile.getMachineSettingFloat('machine_depth')
1612 gcode = gcodeGenerator.gcodeGenerator()
1613 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1614 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1621 gcode.addMove(w/2, 5)
1622 gcode.addMove(z=0.2)
1624 gcode.addExtrude(w/2, d-5.0)
1626 gcode.addMove(5, d/2)
1628 gcode.addExtrude(w-5.0, d/2)
1629 gcode.addRetract(15)
1632 gcode.addMove(w/2, 5)
1634 gcode.addExtrude(w/2, d-5.0)
1636 gcode.addMove(5, d/2)
1638 gcode.addExtrude(w-5.0, d/2)
1639 gcode.addRetract(15)
1644 gcode.addCmd('M400')
1646 self.comm.printGCode(gcode.list())
1647 self.resumeButton.Enable(False)
1648 elif self._wizardState == 4:
1650 float(self.textEntry.GetValue())
1653 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1654 self._wizardState = 5
1655 self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1656 self.textEntry.SetValue('0.0')
1657 self.textEntry.Enable(True)
1658 elif self._wizardState == 5:
1660 float(self.textEntry.GetValue())
1663 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1664 self._wizardState = 6
1665 self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1666 self.textEntry.SetValue('')
1667 self.textEntry.Enable(False)
1668 self.resumeButton.Enable(False)
1670 x = profile.getMachineSettingFloat('extruder_offset_x1')
1671 y = profile.getMachineSettingFloat('extruder_offset_y1')
1672 gcode = gcodeGenerator.gcodeGenerator()
1673 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1674 gcode.setPrintSpeed(25)
1677 gcode.addMove(50, 40, 0.2)
1679 for n in xrange(0, 10):
1680 gcode.addExtrude(50 + n * 10, 150)
1681 gcode.addExtrude(50 + n * 10 + 5, 150)
1682 gcode.addExtrude(50 + n * 10 + 5, 40)
1683 gcode.addExtrude(50 + n * 10 + 10, 40)
1684 gcode.addMove(40, 50)
1685 for n in xrange(0, 10):
1686 gcode.addExtrude(150, 50 + n * 10)
1687 gcode.addExtrude(150, 50 + n * 10 + 5)
1688 gcode.addExtrude(40, 50 + n * 10 + 5)
1689 gcode.addExtrude(40, 50 + n * 10 + 10)
1690 gcode.addRetract(15)
1693 gcode.addMove(50 - x, 30 - y, 0.2)
1695 for n in xrange(0, 10):
1696 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1697 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1698 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1699 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1700 gcode.addMove(30 - x, 50 - y, 0.2)
1701 for n in xrange(0, 10):
1702 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1703 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1704 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1705 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1706 gcode.addRetract(15)
1708 gcode.addCmd('M400')
1709 gcode.addCmd('M104 T0 S0')
1710 gcode.addCmd('M104 T1 S0')
1711 self.comm.printGCode(gcode.list())
1712 elif self._wizardState == 7:
1714 n = int(self.textEntry.GetValue()) - 1
1717 x = profile.getMachineSettingFloat('extruder_offset_x1')
1719 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1720 self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1721 self.textEntry.SetValue('10')
1722 self._wizardState = 8
1723 elif self._wizardState == 8:
1725 n = int(self.textEntry.GetValue()) - 1
1728 y = profile.getMachineSettingFloat('extruder_offset_y1')
1730 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1731 self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1732 self.infoBox.SetReadyIndicator()
1733 self._wizardState = 8
1735 self.resumeButton.Enable(False)
1737 def mcLog(self, message):
1738 print 'Log:', message
1740 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1741 if self._wizardState == 1:
1742 if temp[0] >= 210 and temp[1] >= 210:
1743 self._wizardState = 2
1744 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1745 wx.CallAfter(self.resumeButton.Enable, True)
1746 wx.CallAfter(self.resumeButton.SetFocus)
1748 def mcStateChange(self, state):
1749 if self.comm is None:
1751 if self.comm.isOperational():
1752 if self._wizardState == 0:
1753 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1754 self.comm.sendCommand('M105')
1755 self.comm.sendCommand('M104 S220 T0')
1756 self.comm.sendCommand('M104 S220 T1')
1757 self.comm.sendCommand('G28')
1758 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1759 self._wizardState = 1
1760 if not self.comm.isPrinting():
1761 if self._wizardState == 3:
1762 self._wizardState = 4
1763 wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1764 wx.CallAfter(self.textEntry.SetValue, '0.0')
1765 wx.CallAfter(self.textEntry.Enable, True)
1766 wx.CallAfter(self.resumeButton.Enable, True)
1767 wx.CallAfter(self.resumeButton.SetFocus)
1768 elif self._wizardState == 6:
1769 self._wizardState = 7
1770 wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1771 wx.CallAfter(self.textEntry.SetValue, '10')
1772 wx.CallAfter(self.textEntry.Enable, True)
1773 wx.CallAfter(self.resumeButton.Enable, True)
1774 wx.CallAfter(self.resumeButton.SetFocus)
1776 elif self.comm.isError():
1777 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1779 def mcMessage(self, message):
1782 def mcProgress(self, lineNr):
1785 def mcZChange(self, newZ):
1788 class bedLevelWizard(wx.wizard.Wizard):
1790 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1792 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1793 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1795 self.mainPage = bedLevelWizardMain(self)
1796 self.headOffsetCalibration = None
1798 self.RunWizard(self.mainPage)
1801 def OnPageChanging(self, e):
1802 e.GetPage().StoreData()
1804 def OnPageChanged(self, e):
1805 if e.GetPage().AllowNext():
1806 self.FindWindowById(wx.ID_FORWARD).Enable()
1808 self.FindWindowById(wx.ID_FORWARD).Disable()
1809 if e.GetPage().AllowBack():
1810 self.FindWindowById(wx.ID_BACKWARD).Enable()
1812 self.FindWindowById(wx.ID_BACKWARD).Disable()
1814 class headOffsetWizard(wx.wizard.Wizard):
1816 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1818 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1819 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1821 self.mainPage = headOffsetCalibrationPage(self)
1823 self.RunWizard(self.mainPage)
1826 def OnPageChanging(self, e):
1827 e.GetPage().StoreData()
1829 def OnPageChanged(self, e):
1830 if e.GetPage().AllowNext():
1831 self.FindWindowById(wx.ID_FORWARD).Enable()
1833 self.FindWindowById(wx.ID_FORWARD).Disable()
1834 if e.GetPage().AllowBack():
1835 self.FindWindowById(wx.ID_BACKWARD).Enable()
1837 self.FindWindowById(wx.ID_BACKWARD).Disable()