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.StaticBoxSizer(wx.StaticBox(self), wx.VERTICAL)
128 self.SetSizer(self.sizer)
130 self.original_overlay = overlay
131 self.overlay = self.createOverlay(bitmap, overlay)
132 self.text = wx.StaticText(self, -1, label)
133 self.bmp = wx.StaticBitmap(self, -1, self.bitmap)
134 self.extra_text = wx.StaticText(self, -1, extra_label if extra_label else '')
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)
140 self.sizer.Add(self.extra_text, 0, flag=wx.ALL|wx.ALIGN_CENTER, border=5)
141 self.bmp.Bind(wx.EVT_LEFT_UP, self.OnLeftClick)
145 ImageButton.__groups__[self.group].remove(self)
146 if self == self.group:
147 for ib in ImageButton.__groups__[self.group]:
149 del ImageButton.__groups__[self.group]
150 if ImageButton.__last_group__ == self:
151 ImageButton.__last_group__ = None
153 def OnLeftClick(self, e):
159 def SetValue(self, value):
160 old_value = self.selected
161 self.selected = bool(value)
162 self.bmp.SetBitmap(self.overlay if self.GetValue() else self.bitmap)
163 if self.selected and self.group:
164 for ib in ImageButton.__groups__[self.group]:
169 if self.callback and not old_value and self.selected:
172 def SetLabel(self, label):
173 self.text.SetLabel(label)
176 def SetExtraLabel(self, label):
177 self.extra_text.SetLabel(label)
180 def SetBitmap(self, bitmap):
182 self.overlay = self.createOverlay(bitmap, self.original_overlay)
183 self.bmp.SetBitmap(self.overlay if self.GetValue() else self.bitmap)
186 def SetOverlay(self, overlay):
187 self.original_overlay = overlay
188 self.overlay = self.createOverlay(self.bitmap, self.original_overlay)
189 self.bmp.SetBitmap(self.overlay if self.GetValue() else self.bitmap)
192 def OnSelected(self, callback):
193 self.callback = callback
195 def createOverlay(self, bitmap, overlay):
196 result = bitmap.GetSubBitmap(wx.Rect(0, 0, *bitmap.Size))
197 (width, height) = bitmap.GetSize()
198 overlay_image = wx.ImageFromBitmap(overlay)
199 overlay_image = overlay_image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
200 overlay_scaled = wx.BitmapFromImage(overlay_image)
202 dc.SelectObject(result)
203 dc.DrawBitmap(overlay_scaled, 0, 0)
204 dc.SelectObject(wx.NullBitmap)
208 class InfoPage(wx.wizard.WizardPageSimple):
209 def __init__(self, parent, title):
210 wx.wizard.WizardPageSimple.__init__(self, parent)
212 parent.GetPageAreaSizer().Add(self)
213 sizer = wx.GridBagSizer(5, 5)
217 self.title = wx.StaticText(self, -1, title)
218 font = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)
219 self.title.SetFont(font)
220 # HACK ALERT: For some reason, the StaticText keeps its same size as if
221 # the font was not modified, this causes the text to wrap and to
222 # get out of bounds of the widgets area and hide other widgets.
223 # The only way I found for the widget to get its right size was to calculate
224 # the new font's extent and set the min size on the widget
225 self.title.SetMinSize(self.GetTextExtent(font, title))
226 sizer.Add(self.title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
227 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
228 sizer.AddGrowableCol(1)
232 def GetTextExtent(self, font, text):
235 w,h = dc.GetTextExtent(text)
238 def AddText(self, info):
239 text = wx.StaticText(self, -1, info)
240 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
244 def AddSeperator(self):
245 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
248 def AddHiddenSeperator(self):
251 def AddInfoBox(self):
252 infoBox = InfoBox(self)
253 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
257 def AddRadioButton(self, label, style=0):
258 radio = wx.RadioButton(self, -1, label, style=style)
259 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
263 def AddCheckbox(self, label, checked=False):
264 check = wx.CheckBox(self, -1)
265 text = wx.StaticText(self, -1, label)
266 check.SetValue(checked)
267 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
268 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
272 def AddButton(self, label):
273 button = wx.Button(self, -1, label)
274 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
278 def AddDualButton(self, label1, label2):
279 button1 = wx.Button(self, -1, label1)
280 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
281 button2 = wx.Button(self, -1, label2)
282 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
284 return button1, button2
286 def AddTextCtrl(self, value):
287 ret = wx.TextCtrl(self, -1, value)
288 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
292 def AddLabelTextCtrl(self, info, value):
293 text = wx.StaticText(self, -1, info)
294 ret = wx.TextCtrl(self, -1, value)
295 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
296 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
300 def AddTextCtrlButton(self, value, buttonText):
301 text = wx.TextCtrl(self, -1, value)
302 button = wx.Button(self, -1, buttonText)
303 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
304 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
308 def AddBitmap(self, bitmap):
309 bitmap = wx.StaticBitmap(self, -1, bitmap)
310 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
315 panel = wx.Panel(self, -1)
316 sizer = wx.GridBagSizer(5, 5)
317 panel.SetSizer(sizer)
318 self.GetSizer().Add(panel, pos=(self.rowNr, 0), span=(1, 2), flag=wx.ALL | wx.EXPAND)
322 def AddCheckmark(self, label, bitmap):
323 check = wx.StaticBitmap(self, -1, bitmap)
324 text = wx.StaticText(self, -1, label)
325 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
326 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
330 def AddCombo(self, label, options):
331 combo = wx.ComboBox(self, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
332 text = wx.StaticText(self, -1, label)
333 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER)
334 self.GetSizer().Add(combo, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
338 def AddImageButton(self, panel, x, y, label, filename, image_size=None,
339 extra_label=None, overlay=ImageButton.DefaultOverlay, style=None):
340 ib = ImageButton(panel, label, self.GetBitmap(filename, image_size), extra_label, overlay, style)
341 panel.GetSizer().Add(ib, pos=(x, y), flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=10)
344 def GetBitmap(self, filename, image_size):
345 if image_size == None:
346 return wx.Bitmap(resources.getPathForImage(filename))
348 image = wx.Image(resources.getPathForImage(filename))
349 image_scaled = image.Scale(image_size[0], image_size[1], wx.IMAGE_QUALITY_HIGH)
350 return wx.BitmapFromImage(image_scaled)
361 class PrintrbotPage(InfoPage):
362 def __init__(self, parent):
363 self._printer_info = [
364 # X, Y, Z, Nozzle Size, Filament Diameter, PrintTemperature, Print Speed, Travel Speed, Retract speed, Retract amount, use bed level sensor
365 ("Simple Metal", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, True),
366 ("Metal Plus", 250, 250, 250, 0.4, 1.75, 208, 40, 70, 30, 1, True),
367 ("Simple Makers Kit", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, True),
368 (":" + _("Older models"),),
369 ("Original", 130, 130, 130, 0.5, 2.95, 208, 40, 70, 30, 1, False),
370 ("Simple Maker's Edition v1", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
371 ("Simple Maker's Edition v2 (2013 Printrbot Simple)", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
372 ("Simple Maker's Edition v3 (2014 Printrbot Simple)", 100, 100, 100, 0.4, 1.75, 208, 40, 70, 30, 1, False),
373 ("Jr v1", 115, 120, 80, 0.4, 1.75, 208, 40, 70, 30, 1, False),
374 ("Jr v2", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
375 ("LC v1", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
376 ("LC v2", 150, 150, 150, 0.4, 1.75, 208, 40, 70, 30, 1, False),
377 ("Plus v1", 200, 200, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
378 ("Plus v2", 200, 200, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
379 ("Plus v2.1", 185, 220, 200, 0.4, 1.75, 208, 40, 70, 30, 1, False),
380 ("Plus v2.2 (Model 1404/140422/140501/140507)", 250, 250, 250, 0.4, 1.75, 208, 40, 70, 30, 1, True),
381 ("Go v2 Large", 505, 306, 310, 0.4, 1.75, 208, 35, 70, 30, 1, True),
384 super(PrintrbotPage, self).__init__(parent, _("Printrbot Selection"))
385 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Printrbot_logo.png')))
386 self.AddText(_("Select which Printrbot machine you have:"))
388 for printer in self._printer_info:
389 if printer[0].startswith(":"):
391 self.AddText(printer[0][1:])
393 item = self.AddRadioButton(printer[0])
394 item.data = printer[1:]
395 self._items.append(item)
398 profile.putMachineSetting('machine_name', 'Printrbot ???')
399 for item in self._items:
402 profile.putMachineSetting('machine_name', 'Printrbot ' + item.GetLabel())
403 profile.putMachineSetting('machine_width', data[0])
404 profile.putMachineSetting('machine_depth', data[1])
405 profile.putMachineSetting('machine_height', data[2])
406 profile.putProfileSetting('nozzle_size', data[3])
407 profile.putProfileSetting('filament_diameter', data[4])
408 profile.putProfileSetting('print_temperature', data[5])
409 profile.putProfileSetting('print_speed', data[6])
410 profile.putProfileSetting('travel_speed', data[7])
411 profile.putProfileSetting('retraction_speed', data[8])
412 profile.putProfileSetting('retraction_amount', data[9])
413 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
414 profile.putMachineSetting('has_heated_bed', 'False')
415 profile.putMachineSetting('machine_center_is_zero', 'False')
416 profile.putMachineSetting('extruder_head_size_min_x', '0')
417 profile.putMachineSetting('extruder_head_size_min_y', '0')
418 profile.putMachineSetting('extruder_head_size_max_x', '0')
419 profile.putMachineSetting('extruder_head_size_max_y', '0')
420 profile.putMachineSetting('extruder_head_size_height', '0')
422 profile.setAlterationFile('start.gcode', """;Sliced at: {day} {date} {time}
423 ;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}
424 ;Print time: {print_time}
425 ;Filament used: {filament_amount}m {filament_weight}g
426 ;Filament cost: {filament_cost}
427 ;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line
428 ;M109 S{print_temperature} ;Uncomment to add your own temperature line
430 G90 ;absolute positioning
431 M82 ;set extruder to absolute mode
432 M107 ;start with the fan off
433 G28 X0 Y0 ;move X/Y to min endstops
434 G28 Z0 ;move Z to min endstops
435 G29 ;Run the auto bed leveling
436 G1 Z15.0 F{travel_speed} ;move the platform down 15mm
437 G92 E0 ;zero the extruded length
438 G1 F200 E3 ;extrude 3mm of feed stock
439 G92 E0 ;zero the extruded length again
441 ;Put printing message on LCD screen
445 class OtherMachineSelectPage(InfoPage):
446 def __init__(self, parent):
447 super(OtherMachineSelectPage, self).__init__(parent, _("Other machine information"))
448 self.AddText(_("The following pre-defined machine profiles are available"))
449 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."))
451 machines = resources.getDefaultMachineProfiles()
453 for filename in machines:
454 name = os.path.splitext(os.path.basename(filename))[0]
455 item = self.AddRadioButton(name)
456 item.filename = filename
457 item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
458 self.options.append(item)
460 item = self.AddRadioButton(_('Custom...'))
462 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
464 def OnProfileSelect(self, e):
465 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
467 def OnOtherSelect(self, e):
468 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
471 for option in self.options:
472 if option.GetValue():
473 profile.loadProfile(option.filename)
474 profile.loadMachineSettings(option.filename)
476 class OtherMachineInfoPage(InfoPage):
477 def __init__(self, parent):
478 super(OtherMachineInfoPage, self).__init__(parent, _("Cura Ready!"))
479 self.AddText(_("Cura is now ready to be used!"))
481 class CustomRepRapInfoPage(InfoPage):
482 def __init__(self, parent):
483 super(CustomRepRapInfoPage, self).__init__(parent, _("Custom RepRap information"))
484 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
485 self.AddText(_("Be sure to review the default profile before running it on your machine."))
486 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
488 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
490 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
491 self.machineWidth = self.AddLabelTextCtrl(_("Machine width X (mm)"), "80")
492 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth Y (mm)"), "80")
493 self.machineHeight = self.AddLabelTextCtrl(_("Machine height Z (mm)"), "55")
494 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
495 self.heatedBed = self.AddCheckbox(_("Heated bed"))
496 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
499 profile.putMachineSetting('machine_name', self.machineName.GetValue())
500 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
501 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
502 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
503 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
504 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
505 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
506 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
507 profile.putMachineSetting('extruder_head_size_min_x', '0')
508 profile.putMachineSetting('extruder_head_size_min_y', '0')
509 profile.putMachineSetting('extruder_head_size_max_x', '0')
510 profile.putMachineSetting('extruder_head_size_max_y', '0')
511 profile.putMachineSetting('extruder_head_size_height', '0')
512 profile.checkAndUpdateMachineName()
514 class MachineSelectPage(InfoPage):
515 def __init__(self, parent):
516 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
517 self.AddText(_("What kind of machine do you have:"))
519 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2")
520 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
521 self.Ultimaker2ExtRadio = self.AddRadioButton("Ultimaker2extended")
522 self.Ultimaker2ExtRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
523 self.Ultimaker2GoRadio = self.AddRadioButton("Ultimaker2go")
524 self.Ultimaker2GoRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
525 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
526 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
527 self.UltimakerOPRadio = self.AddRadioButton("Ultimaker Original+")
528 self.UltimakerOPRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerOPSelect)
529 self.PrintrbotRadio = self.AddRadioButton("Printrbot")
530 self.PrintrbotRadio.Bind(wx.EVT_RADIOBUTTON, self.OnPrintrbotSelect)
531 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot, Witbox)"))
532 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
534 def OnUltimaker2Select(self, e):
535 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
537 def OnUltimakerSelect(self, e):
538 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
540 def OnUltimakerOPSelect(self, e):
541 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
543 def OnPrintrbotSelect(self, e):
544 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().printrbotSelectType)
546 def OnOtherSelect(self, e):
547 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
550 profile.putProfileSetting('retraction_enable', 'True')
551 if self.Ultimaker2Radio.GetValue() or self.Ultimaker2GoRadio.GetValue() or self.Ultimaker2ExtRadio.GetValue():
552 if self.Ultimaker2Radio.GetValue():
553 profile.putMachineSetting('machine_width', '230')
554 profile.putMachineSetting('machine_depth', '225')
555 profile.putMachineSetting('machine_height', '205')
556 profile.putMachineSetting('machine_name', 'ultimaker2')
557 profile.putMachineSetting('machine_type', 'ultimaker2')
558 profile.putMachineSetting('has_heated_bed', 'True')
559 if self.Ultimaker2GoRadio.GetValue():
560 profile.putMachineSetting('machine_width', '120')
561 profile.putMachineSetting('machine_depth', '120')
562 profile.putMachineSetting('machine_height', '115')
563 profile.putMachineSetting('machine_name', 'ultimaker2go')
564 profile.putMachineSetting('machine_type', 'ultimaker2go')
565 profile.putMachineSetting('has_heated_bed', 'False')
566 if self.Ultimaker2ExtRadio.GetValue():
567 profile.putMachineSetting('machine_width', '230')
568 profile.putMachineSetting('machine_depth', '225')
569 profile.putMachineSetting('machine_height', '315')
570 profile.putMachineSetting('machine_name', 'ultimaker2extended')
571 profile.putMachineSetting('machine_type', 'ultimaker2extended')
572 profile.putMachineSetting('has_heated_bed', 'False')
573 profile.putMachineSetting('machine_center_is_zero', 'False')
574 profile.putMachineSetting('gcode_flavor', 'UltiGCode')
575 profile.putMachineSetting('extruder_head_size_min_x', '40.0')
576 profile.putMachineSetting('extruder_head_size_min_y', '10.0')
577 profile.putMachineSetting('extruder_head_size_max_x', '60.0')
578 profile.putMachineSetting('extruder_head_size_max_y', '30.0')
579 profile.putMachineSetting('extruder_head_size_height', '48.0')
580 profile.putProfileSetting('nozzle_size', '0.4')
581 profile.putProfileSetting('fan_full_height', '5.0')
582 profile.putMachineSetting('extruder_offset_x1', '18.0')
583 profile.putMachineSetting('extruder_offset_y1', '0.0')
584 elif self.UltimakerRadio.GetValue():
585 profile.putMachineSetting('machine_width', '205')
586 profile.putMachineSetting('machine_depth', '205')
587 profile.putMachineSetting('machine_height', '200')
588 profile.putMachineSetting('machine_name', 'ultimaker original')
589 profile.putMachineSetting('machine_type', 'ultimaker')
590 profile.putMachineSetting('machine_center_is_zero', 'False')
591 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
592 profile.putProfileSetting('nozzle_size', '0.4')
593 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
594 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
595 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
596 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
597 profile.putMachineSetting('extruder_head_size_height', '55.0')
598 elif self.UltimakerOPRadio.GetValue():
599 profile.putMachineSetting('machine_width', '205')
600 profile.putMachineSetting('machine_depth', '205')
601 profile.putMachineSetting('machine_height', '200')
602 profile.putMachineSetting('machine_name', 'ultimaker original+')
603 profile.putMachineSetting('machine_type', 'ultimaker_plus')
604 profile.putMachineSetting('machine_center_is_zero', 'False')
605 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
606 profile.putProfileSetting('nozzle_size', '0.4')
607 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
608 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
609 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
610 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
611 profile.putMachineSetting('extruder_head_size_height', '55.0')
612 profile.putMachineSetting('has_heated_bed', 'True')
613 profile.putMachineSetting('extruder_amount', '1')
614 profile.putProfileSetting('retraction_enable', 'True')
616 profile.putMachineSetting('machine_width', '80')
617 profile.putMachineSetting('machine_depth', '80')
618 profile.putMachineSetting('machine_height', '60')
619 profile.putMachineSetting('machine_name', 'reprap')
620 profile.putMachineSetting('machine_type', 'reprap')
621 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
622 profile.putPreference('startMode', 'Normal')
623 profile.putProfileSetting('nozzle_size', '0.5')
624 profile.checkAndUpdateMachineName()
625 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
627 class SelectParts(InfoPage):
628 def __init__(self, parent):
629 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
630 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."))
632 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
633 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
634 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
635 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
637 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."))
638 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
639 self.springExtruder.SetValue(True)
642 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
643 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
644 profile.putMachineSetting('has_heated_bed', 'True')
646 profile.putMachineSetting('has_heated_bed', 'False')
647 if self.dualExtrusion.GetValue():
648 profile.putMachineSetting('extruder_amount', '2')
649 profile.putMachineSetting('machine_depth', '195')
651 profile.putMachineSetting('extruder_amount', '1')
652 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
653 profile.putProfileSetting('retraction_enable', 'True')
655 profile.putProfileSetting('retraction_enable', 'False')
658 class UltimakerFirmwareUpgradePage(InfoPage):
659 def __init__(self, parent):
660 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
661 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."))
662 self.AddHiddenSeperator()
663 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
664 self.AddHiddenSeperator()
665 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."))
666 upgradeButton, skipUpgradeButton = self.AddDualButton(_('Upgrade to Marlin firmware'), _('Skip upgrade'))
667 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
668 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
669 self.AddHiddenSeperator()
670 if profile.getMachineSetting('machine_type') == 'ultimaker':
671 self.AddText(_("Do not upgrade to this firmware if:"))
672 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
673 self.AddText(_("* Build your own heated bed"))
674 self.AddText(_("* Have other changes in the firmware"))
675 # button = self.AddButton('Goto this page for a custom firmware')
676 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
681 def OnUpgradeClick(self, e):
682 if firmwareInstall.InstallFirmware():
683 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
685 def OnSkipClick(self, e):
686 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
687 self.GetParent().ShowPage(self.GetNext())
689 def OnUrlClick(self, e):
690 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
692 class UltimakerCheckupPage(InfoPage):
693 def __init__(self, parent):
694 super(UltimakerCheckupPage, self).__init__(parent, _("Ultimaker Checkup"))
696 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
697 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
698 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
699 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
700 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
701 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
702 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
703 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
704 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
705 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
708 _("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."))
709 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
710 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
711 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
713 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
714 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
715 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
717 self.infoBox = self.AddInfoBox()
718 self.machineState = self.AddText("")
719 self.temperatureLabel = self.AddText("")
720 self.errorLogButton = self.AddButton(_("Show error log"))
721 self.errorLogButton.Show(False)
723 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
725 self.xMinStop = False
726 self.xMaxStop = False
727 self.yMinStop = False
728 self.yMaxStop = False
729 self.zMinStop = False
730 self.zMaxStop = False
732 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
735 if self.comm is not None:
739 self.endstopBitmap.Show(False)
742 def OnSkipClick(self, e):
743 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
744 self.GetParent().ShowPage(self.GetNext())
746 def OnCheckClick(self, e=None):
747 self.errorLogButton.Show(False)
748 if self.comm is not None:
752 wx.CallAfter(self.OnCheckClick)
754 self.infoBox.SetBusy(_("Connecting to machine."))
755 self.commState.SetBitmap(self.unknownBitmap)
756 self.tempState.SetBitmap(self.unknownBitmap)
757 self.stopState.SetBitmap(self.unknownBitmap)
758 self.checkupState = 0
759 self.checkExtruderNr = 0
760 self.comm = machineCom.MachineCom(callbackObject=self)
762 def OnErrorLog(self, e):
763 printWindow.LogWindow('\n'.join(self.comm.getLog()))
765 def mcLog(self, message):
768 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
769 if not self.comm.isOperational():
771 if self.checkupState == 0:
772 self.tempCheckTimeout = 20
773 if temp[self.checkExtruderNr] > 70:
774 self.checkupState = 1
775 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
776 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
777 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
779 self.startTemp = temp[self.checkExtruderNr]
780 self.checkupState = 2
781 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
782 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
783 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
784 elif self.checkupState == 1:
785 if temp[self.checkExtruderNr] < 60:
786 self.startTemp = temp[self.checkExtruderNr]
787 self.checkupState = 2
788 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
789 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
790 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
791 elif self.checkupState == 2:
792 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
793 if temp[self.checkExtruderNr] > self.startTemp + 40:
794 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
795 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
796 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
797 self.checkExtruderNr = 0
798 self.checkupState = 3
799 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
800 wx.CallAfter(self.endstopBitmap.Show, True)
801 wx.CallAfter(self.Layout)
802 self.comm.sendCommand('M119')
803 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
805 self.checkupState = 0
806 self.checkExtruderNr += 1
808 self.tempCheckTimeout -= 1
809 if self.tempCheckTimeout < 1:
810 self.checkupState = -1
811 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
812 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
813 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
814 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
815 elif self.checkupState >= 3 and self.checkupState < 10:
816 self.comm.sendCommand('M119')
817 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
819 def mcStateChange(self, state):
820 if self.comm is None:
822 if self.comm.isOperational():
823 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
824 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
825 elif self.comm.isError():
826 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
827 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
828 wx.CallAfter(self.endstopBitmap.Show, False)
829 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
830 wx.CallAfter(self.errorLogButton.Show, True)
831 wx.CallAfter(self.Layout)
833 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
835 def mcMessage(self, message):
836 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
837 for data in message.split(' '):
839 tag, value = data.split(':', 1)
841 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
843 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
845 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
847 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
849 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
851 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
853 tag, value = map(str.strip, message.split(':', 1))
855 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
857 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
859 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
861 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
863 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
865 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
866 if 'z_max' in message:
867 self.comm.sendCommand('M119')
869 if self.checkupState == 3:
870 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
871 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
872 self.checkupState = 5
873 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
874 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
876 self.checkupState = 4
877 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
878 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
879 elif self.checkupState == 4:
880 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
881 self.checkupState = 5
882 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
883 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
884 elif self.checkupState == 5:
885 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
886 self.checkupState = 6
887 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
888 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
889 elif self.checkupState == 6:
890 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
891 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
892 self.checkupState = 8
893 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
894 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
896 self.checkupState = 7
897 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
898 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
899 elif self.checkupState == 7:
900 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
901 self.checkupState = 8
902 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
903 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
904 elif self.checkupState == 8:
905 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
906 if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
907 self.checkupState = 10
909 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
910 wx.CallAfter(self.infoBox.SetReadyIndicator)
911 wx.CallAfter(self.endstopBitmap.Show, False)
912 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
913 wx.CallAfter(self.OnSkipClick, None)
915 self.checkupState = 9
916 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
917 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
918 elif self.checkupState == 9:
919 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
920 self.checkupState = 10
922 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
923 wx.CallAfter(self.infoBox.SetReadyIndicator)
924 wx.CallAfter(self.endstopBitmap.Show, False)
925 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
926 wx.CallAfter(self.OnSkipClick, None)
928 def mcProgress(self, lineNr):
931 def mcZChange(self, newZ):
935 class UltimakerCalibrationPage(InfoPage):
936 def __init__(self, parent):
937 super(UltimakerCalibrationPage, self).__init__(parent, _("Ultimaker Calibration"))
939 self.AddText("Your Ultimaker requires some calibration.")
940 self.AddText("This calibration is needed for a proper extrusion amount.")
942 self.AddText("The following values are needed:")
943 self.AddText("* Diameter of filament")
944 self.AddText("* Number of steps per mm of filament extrusion")
946 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
948 self.AddText("First we need the diameter of your filament:")
949 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
951 "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.")
952 self.AddText("Note: This value can be changed later at any time.")
955 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
958 class UltimakerCalibrateStepsPerEPage(InfoPage):
959 def __init__(self, parent):
960 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, _("Ultimaker Calibration"))
962 #if profile.getMachineSetting('steps_per_e') == '0':
963 # profile.putMachineSetting('steps_per_e', '865.888')
965 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
966 self.AddText(_("First remove any filament from your machine."))
967 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
968 self.AddText(_("We'll push the filament 100mm"))
969 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
970 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
971 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
972 self.AddText(_("This results in the following steps per E:"))
973 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
974 self.AddText(_("You can repeat these steps to get better calibration."))
977 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
978 self.heatButton = self.AddButton(_("Heatup for filament removal"))
980 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
981 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
982 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
984 def OnSaveLengthClick(self, e):
985 currentEValue = float(self.stepsPerEInput.GetValue())
986 realExtrudeLength = float(self.lengthInput.GetValue())
987 newEValue = currentEValue * 100 / realExtrudeLength
988 self.stepsPerEInput.SetValue(str(newEValue))
989 self.lengthInput.SetValue("100")
991 def OnExtrudeClick(self, e):
992 t = threading.Thread(target=self.OnExtrudeRun)
996 def OnExtrudeRun(self):
997 self.heatButton.Enable(False)
998 self.extrudeButton.Enable(False)
999 currentEValue = float(self.stepsPerEInput.GetValue())
1000 self.comm = machineCom.MachineCom()
1001 if not self.comm.isOpen():
1003 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
1004 'Printer error', wx.OK | wx.ICON_INFORMATION)
1005 self.heatButton.Enable(True)
1006 self.extrudeButton.Enable(True)
1009 line = self.comm.readline()
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('M302') #Disable cold extrusion protection
1018 self.sendGCommand("M92 E%f" % (currentEValue))
1019 self.sendGCommand("G92 E0")
1020 self.sendGCommand("G1 E100 F600")
1023 self.extrudeButton.Enable()
1024 self.heatButton.Enable()
1026 def OnHeatClick(self, e):
1027 t = threading.Thread(target=self.OnHeatRun)
1031 def OnHeatRun(self):
1032 self.heatButton.Enable(False)
1033 self.extrudeButton.Enable(False)
1034 self.comm = machineCom.MachineCom()
1035 if not self.comm.isOpen():
1037 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
1038 'Printer error', wx.OK | wx.ICON_INFORMATION)
1039 self.heatButton.Enable(True)
1040 self.extrudeButton.Enable(True)
1043 line = self.comm.readline()
1045 self.heatButton.Enable(True)
1046 self.extrudeButton.Enable(True)
1050 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
1053 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
1055 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
1056 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
1057 self.sendGCommand('M104 S0')
1060 self.heatButton.Enable(True)
1061 self.extrudeButton.Enable(True)
1063 def sendGCommand(self, cmd):
1064 self.comm.sendCommand(cmd) #Disable cold extrusion protection
1066 line = self.comm.readline()
1069 if line.startswith('ok'):
1072 def StoreData(self):
1073 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
1075 class Ultimaker2ReadyPage(InfoPage):
1076 def __init__(self, parent):
1077 super(Ultimaker2ReadyPage, self).__init__(parent, _("Ultimaker2"))
1078 self.AddText(_('Congratulations on your the purchase of your brand new Ultimaker2.'))
1079 self.AddText(_('Cura is now ready to be used with your Ultimaker2.'))
1082 class LulzbotMachineSelectPage(InfoPage):
1086 def __init__(self, parent):
1087 super(LulzbotMachineSelectPage, self).__init__(parent, _("Select your machine"))
1088 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1089 self.AddText(_("Select your printer :"))
1091 self.panel = self.AddPanel()
1093 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1094 self.LulzbotMini = self.AddImageButton(self.panel, 0, 0, _("LulzBot Mini"),
1095 'Lulzbot_mini.jpg', image_size, style=ImageButton.IB_GROUP)
1096 self.LulzbotMini.OnSelected(self.OnLulzbotMiniSelected)
1097 self.LulzbotMini.SetValue(True)
1098 self.LulzbotTaz5 = self.AddImageButton(self.panel, 0, 1, _("LulzBot TAZ 5"),
1099 'Lulzbot_TAZ5.jpg', image_size)
1100 self.LulzbotTaz5.OnSelected(self.OnLulzbotTazSelected)
1101 self.LulzbotTaz4 = self.AddImageButton(self.panel, 1, 0, _("LulzBot TAZ 4"),
1102 'Lulzbot_TAZ4.jpg', image_size)
1103 self.LulzbotTaz4.OnSelected(self.OnLulzbotTazSelected)
1104 self.OtherPrinters = self.AddImageButton(self.panel, 1, 1, _("Non-LulzBot Printers"),
1105 'Other_Printers.jpg', image_size)
1106 self.OtherPrinters.OnSelected(self.OnOthersSelected)
1108 def OnOthersSelected(self):
1109 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().machineSelectPage)
1111 def OnLulzbotMiniSelected(self):
1112 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotMiniToolheadPage)
1114 def OnLulzbotTazSelected(self):
1115 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTazHotendPage)
1117 def AllowNext(self):
1120 def AllowBack(self):
1123 def StoreData(self):
1124 if self.LulzbotTaz4.GetValue() or self.LulzbotTaz5.GetValue() or self.LulzbotMini.GetValue():
1125 if self.LulzbotTaz4.GetValue():
1126 profile.putMachineSetting('machine_width', '290')
1127 profile.putMachineSetting('machine_depth', '275')
1128 profile.putMachineSetting('machine_height', '250')
1129 profile.putProfileSetting('nozzle_size', '0.35')
1130 profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1131 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4')
1132 profile.putMachineSetting('serial_baud', '115200')
1133 elif self.LulzbotTaz5.GetValue():
1134 profile.putProfileSetting('nozzle_size', '0.35')
1135 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5')
1136 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1137 profile.putMachineSetting('machine_width', '290')
1138 profile.putMachineSetting('machine_depth', '275')
1139 profile.putMachineSetting('machine_height', '250')
1140 profile.putMachineSetting('serial_baud', '115200')
1142 profile.putMachineSetting('machine_width', '155')
1143 profile.putMachineSetting('machine_depth', '155')
1144 profile.putMachineSetting('machine_height', '163')
1145 profile.putProfileSetting('nozzle_size', '0.5')
1146 profile.putMachineSetting('serial_baud', '115200')
1147 profile.putMachineSetting('extruder_head_size_min_x', '40')
1148 profile.putMachineSetting('extruder_head_size_max_x', '75')
1149 profile.putMachineSetting('extruder_head_size_min_y', '25')
1150 profile.putMachineSetting('extruder_head_size_max_y', '55')
1151 profile.putMachineSetting('extruder_head_size_height', '17')
1153 profile.putMachineSetting('machine_center_is_zero', 'False')
1154 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
1155 profile.putMachineSetting('has_heated_bed', 'True')
1156 profile.putMachineSetting('extruder_head_size_min_x', '0.0')
1157 profile.putMachineSetting('extruder_head_size_min_y', '0.0')
1158 profile.putMachineSetting('extruder_head_size_max_x', '0.0')
1159 profile.putMachineSetting('extruder_head_size_max_y', '0.0')
1160 profile.putMachineSetting('extruder_head_size_height', '0.0')
1161 profile.putProfileSetting('retraction_enable', 'True')
1162 profile.putPreference('startMode', 'Simple')
1163 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
1164 profile.checkAndUpdateMachineName()
1166 class LulzbotReadyPage(InfoPage):
1167 def __init__(self, parent):
1168 super(LulzbotReadyPage, self).__init__(parent, _("LulzBot TAZ/Mini"))
1169 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1170 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
1172 self.AddText(_('For more information about using Cura with your LulzBot'))
1173 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
1176 class LulzbotToolheadSelectPage(InfoPage):
1177 url='http://lulzbot.com/toolhead-identification'
1179 def __init__(self, parent, title):
1180 super(LulzbotToolheadSelectPage, self).__init__(parent, title)
1182 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1184 self.AddText(_('Please select your currently installed Tool Head'))
1185 txt = self.AddText(_("WARNING :"))
1186 txt.SetForegroundColour(wx.RED)
1187 font = wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD)
1189 txt.SetMinSize(self.GetTextExtent(font, _("WARNING :")))
1191 self.AddText(_('Carefully select the right Tool Head. Flashing the firmware with the wrong Tool Head can damage your LulzBot.\nLearn more here:'))
1192 button = self.AddButton(self.url)
1193 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1197 def OnUrlClick(self, e):
1198 webbrowser.open(LulzbotMiniToolheadSelectPage.url)
1200 class LulzbotMiniToolheadSelectPage(LulzbotToolheadSelectPage):
1201 def __init__(self, parent):
1202 super(LulzbotMiniToolheadSelectPage, self).__init__(parent, _("LulzBot Mini Toolhead Selection"))
1204 self.panel = self.AddPanel()
1205 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1206 self.standard = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v2'),
1207 'Lulzbot_Toolhead_Mini_Standard.jpg', image_size,
1208 style=ImageButton.IB_GROUP)
1209 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v2'),
1210 'Lulzbot_Toolhead_Mini_Flexystruder.jpg', image_size)
1211 self.standard.SetValue(True)
1213 def StoreData(self):
1214 if self.standard.GetValue():
1215 profile.putMachineSetting('machine_name', 'LulzBot Mini')
1216 profile.putMachineSetting('machine_type', 'lulzbot_mini')
1218 profile.putMachineSetting('machine_name', 'LulzBot Mini (Flexy)')
1219 profile.putMachineSetting('machine_type', 'lulzbot_mini_flexy')
1222 class LulzbotTazToolheadSelectPage(LulzbotToolheadSelectPage):
1223 def __init__(self, parent):
1224 super(LulzbotTazToolheadSelectPage, self).__init__(parent, _("LulzBot TAZ Toolhead Selection"))
1226 self.panel = self.AddPanel()
1227 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1228 self.standard = self.AddImageButton(self.panel, 0, 0, _('Single Extruder v1'),
1229 'Lulzbot_Toolhead_TAZ_Single_v1.jpg', image_size,
1230 style=ImageButton.IB_GROUP)
1231 self.flexy = self.AddImageButton(self.panel, 0, 1, _('Flexystruder v1'),
1232 'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg', image_size)
1233 self.dually = self.AddImageButton(self.panel, 1, 0, _('Dual Extruder v1'),
1234 'Lulzbot_Toolhead_TAZ_Dually_v1.jpg', image_size)
1235 self.flexydually = self.AddImageButton(self.panel, 1, 1, _('FlexyDually v1'),
1236 'Lulzbot_Toolhead_TAZ_FlexyDually_v1.jpg', image_size)
1238 self.standard.SetValue(True)
1240 def SetVersion(self, version):
1241 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1242 self.standard.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Single_v%d.jpg' % version, image_size))
1243 self.standard.SetLabel(_('Single Extruder v%d' % version))
1244 self.flexy.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Flexystruder_v%d.jpg' % version, image_size))
1245 self.flexy.SetLabel(_('Flexystruder v%d' % version))
1246 self.dually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_Dually_v%d.jpg' % version, image_size))
1247 self.dually.SetLabel(_('Dual Extruder v%d' % version))
1248 self.flexydually.SetBitmap(self.GetBitmap('Lulzbot_Toolhead_TAZ_FlexyDually_v%d.jpg' % version, image_size))
1249 self.flexydually.SetLabel(_('FlexyDually v%d' % version))
1251 self.standard.OnSelected(None)
1252 self.flexy.OnSelected(None)
1253 self.dually.OnSelected(None)
1254 self.flexydually.OnSelected(None)
1255 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1257 self.standard.OnSelected(self.OnStandardV2)
1258 self.flexy.OnSelected(self.OnNonStandard)
1259 self.dually.OnSelected(self.OnNonStandard)
1260 self.flexydually.OnSelected(self.OnNonStandard)
1261 if self.standard.GetValue():
1262 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1264 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1266 def OnStandardV2(self):
1267 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotTaz5NozzleSelectPage)
1269 def OnNonStandard(self):
1270 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
1273 def StoreData(self):
1277 class LulzbotHotendSelectPage(LulzbotToolheadSelectPage):
1278 def __init__(self, parent):
1279 super(LulzbotHotendSelectPage, self).__init__(parent, _("LulzBot Toolhead Hotend Selection"))
1281 self.panel = self.AddPanel()
1282 image_size=(LulzbotMachineSelectPage.IMAGE_WIDTH, LulzbotMachineSelectPage.IMAGE_HEIGHT)
1283 self.v1 = self.AddImageButton(self.panel, 0, 0, _('v1 (Budaschnozzle Hotends)'),
1284 'Lulzbot_Toolhead_v1.jpg', image_size,
1285 style=ImageButton.IB_GROUP)
1286 self.v2 = self.AddImageButton(self.panel, 0, 1, _('v2 (Hexagon Hotends)'),
1287 'Lulzbot_Toolhead_v2.jpg', image_size)
1288 self.v1.OnSelected(self.OnV1Selected)
1289 self.v2.OnSelected(self.OnV2Selected)
1290 self.v1.SetValue(True)
1292 def OnV1Selected(self):
1293 self.GetParent().lulzbotTazToolheadPage.SetVersion(1)
1295 def OnV2Selected(self):
1296 self.GetParent().lulzbotTazToolheadPage.SetVersion(2)
1298 class LulzbotTaz5NozzleSelectPage(LulzbotToolheadSelectPage):
1299 url2='http://lulzbot.com/printer-identification'
1301 def __init__(self, parent):
1302 super(LulzbotTaz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ Standard V2 Nozzle Selection"))
1304 self.AddText(_('Please select your Hexagon hotend\'s nozzle size:'))
1305 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1306 self.Nozzle35Radio.SetValue(True)
1307 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1308 self.AddText(_(' '))
1311 self.AddText(_('If you are not sure which nozzle size you have'))
1312 self.AddText(_('please check this webpage: '))
1313 button = self.AddButton(LulzbotTaz5NozzleSelectPage.url2)
1314 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1316 def OnUrlClick(self, e):
1317 webbrowser.open(LulzbotTaz5NozzleSelectPage.url2)
1319 def StoreData(self):
1320 if self.Nozzle35Radio.GetValue():
1321 profile.putProfileSetting('nozzle_size', '0.35')
1322 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.35 nozzle)')
1323 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_035nozzle')
1326 profile.putProfileSetting('nozzle_size', '0.5')
1327 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.5 nozzle)')
1328 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_05nozzle')
1330 class ConfigWizard(wx.wizard.Wizard):
1331 def __init__(self, addNew = False):
1332 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1334 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1336 profile.setActiveMachine(profile.getMachineCount())
1338 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1339 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1340 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1342 self.machineSelectPage = MachineSelectPage(self)
1343 self.ultimakerSelectParts = SelectParts(self)
1344 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1345 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1346 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1347 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1348 self.bedLevelPage = bedLevelWizardMain(self)
1349 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1350 self.printrbotSelectType = PrintrbotPage(self)
1351 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1352 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1353 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1355 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1356 self.lulzbotReadyPage = LulzbotReadyPage(self)
1357 self.lulzbotMiniToolheadPage = LulzbotMiniToolheadSelectPage(self)
1358 self.lulzbotTazToolheadPage = LulzbotTazToolheadSelectPage(self)
1359 self.lulzbotTazHotendPage = LulzbotHotendSelectPage(self)
1360 self.lulzbotTaz5NozzleSelectPage = LulzbotTaz5NozzleSelectPage(self)
1361 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1363 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotMiniToolheadPage)
1364 wx.wizard.WizardPageSimple.Chain(self.lulzbotMiniToolheadPage, self.lulzbotReadyPage)
1365 wx.wizard.WizardPageSimple.Chain(self.lulzbotTazHotendPage, self.lulzbotTazToolheadPage)
1366 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1367 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1368 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1369 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1370 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1371 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1373 self.RunWizard(self.lulzbotMachineSelectPage)
1376 def OnPageChanging(self, e):
1377 e.GetPage().StoreData()
1379 def OnPageChanged(self, e):
1380 if e.GetPage().AllowNext():
1381 self.FindWindowById(wx.ID_FORWARD).Enable()
1383 self.FindWindowById(wx.ID_FORWARD).Disable()
1384 if e.GetPage().AllowBack():
1385 self.FindWindowById(wx.ID_BACKWARD).Enable()
1387 self.FindWindowById(wx.ID_BACKWARD).Disable()
1389 def OnCancel(self, e):
1390 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1391 profile.setActiveMachine(self._old_machine_index)
1392 profile.removeMachine(new_machine_index)
1394 class bedLevelWizardMain(InfoPage):
1395 def __init__(self, parent):
1396 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1398 self.AddText(_('This wizard will help you in leveling your printer bed'))
1400 self.AddText(_('It will do the following steps'))
1401 self.AddText(_('* Move the printer head to each corner'))
1402 self.AddText(_(' and let you adjust the height of the bed to the nozzle'))
1403 self.AddText(_('* Print a line around the bed to check if it is level'))
1406 self.connectButton = self.AddButton(_('Connect to printer'))
1409 self.infoBox = self.AddInfoBox()
1410 self.resumeButton = self.AddButton(_('Resume'))
1411 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1412 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1413 self.resumeButton.Enable(False)
1415 self.upButton.Enable(False)
1416 self.downButton.Enable(False)
1417 self.upButton2.Enable(False)
1418 self.downButton2.Enable(False)
1420 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1421 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1422 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1423 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1424 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1425 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1427 def OnConnect(self, e = None):
1428 if self.comm is not None:
1432 wx.CallAfter(self.OnConnect)
1434 self.connectButton.Enable(False)
1435 self.comm = machineCom.MachineCom(callbackObject=self)
1436 self.infoBox.SetBusy(_('Connecting to machine.'))
1437 self._wizardState = 0
1439 def OnBedUp(self, e):
1440 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1441 self.comm.sendCommand('G92 Z10')
1442 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1443 self.comm.sendCommand('M400')
1445 def OnBedDown(self, e):
1446 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1447 self.comm.sendCommand('G92 Z10')
1448 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1449 self.comm.sendCommand('M400')
1451 def OnBedUp2(self, e):
1452 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1453 self.comm.sendCommand('G92 Z10')
1454 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1455 self.comm.sendCommand('M400')
1457 def OnBedDown2(self, e):
1458 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1459 self.comm.sendCommand('G92 Z10')
1460 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1461 self.comm.sendCommand('M400')
1463 def AllowNext(self):
1464 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1465 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1468 def OnResume(self, e):
1469 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1470 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1471 if self._wizardState == -1:
1472 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1473 wx.CallAfter(self.upButton.Enable, False)
1474 wx.CallAfter(self.downButton.Enable, False)
1475 wx.CallAfter(self.upButton2.Enable, False)
1476 wx.CallAfter(self.downButton2.Enable, False)
1477 self.comm.sendCommand('M105')
1478 self.comm.sendCommand('G28')
1479 self._wizardState = 1
1480 elif self._wizardState == 2:
1481 if profile.getMachineSetting('has_heated_bed') == 'True':
1482 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1483 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1484 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1485 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1486 self.comm.sendCommand('M400')
1487 self._wizardState = 3
1489 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1490 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1491 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1492 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1493 self.comm.sendCommand('M400')
1494 self._wizardState = 3
1495 elif self._wizardState == 4:
1496 if profile.getMachineSetting('has_heated_bed') == 'True':
1497 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1498 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1499 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1500 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1501 self.comm.sendCommand('M400')
1502 self._wizardState = 7
1504 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1505 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1506 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1507 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1508 self.comm.sendCommand('M400')
1509 self._wizardState = 5
1510 elif self._wizardState == 6:
1511 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1512 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1513 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1514 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1515 self.comm.sendCommand('M400')
1516 self._wizardState = 7
1517 elif self._wizardState == 8:
1518 wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1519 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1520 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1521 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1522 self._wizardState = 9
1523 elif self._wizardState == 10:
1524 self._wizardState = 11
1525 wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1526 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1527 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1528 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1529 w = profile.getMachineSettingFloat('machine_width') - 10
1530 d = profile.getMachineSettingFloat('machine_depth')
1531 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1532 filamentArea = math.pi * filamentRadius * filamentRadius
1533 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1537 'G1 Z2 F%d' % (feedZ),
1539 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1540 'G1 Z0.3 F%d' % (feedZ)]
1542 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1544 for i in xrange(0, 3):
1545 dist = 5.0 + 0.4 * float(i)
1546 eValue += (d - 2.0*dist) * ePerMM
1547 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1548 eValue += (w - 2.0*dist) * ePerMM
1549 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1550 eValue += (d - 2.0*dist) * ePerMM
1551 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1552 eValue += (w - 2.0*dist) * ePerMM
1553 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1555 gcodeList.append('M400')
1556 self.comm.printGCode(gcodeList)
1557 self.resumeButton.Enable(False)
1559 def mcLog(self, message):
1560 print 'Log:', message
1562 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1563 if self._wizardState == 1:
1564 self._wizardState = 2
1565 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1566 wx.CallAfter(self.resumeButton.Enable, True)
1567 elif self._wizardState == 3:
1568 self._wizardState = 4
1569 if profile.getMachineSetting('has_heated_bed') == 'True':
1570 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1572 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1573 wx.CallAfter(self.resumeButton.Enable, True)
1574 elif self._wizardState == 5:
1575 self._wizardState = 6
1576 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1577 wx.CallAfter(self.resumeButton.Enable, True)
1578 elif self._wizardState == 7:
1579 self._wizardState = 8
1580 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1581 wx.CallAfter(self.resumeButton.Enable, True)
1582 elif self._wizardState == 9:
1583 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1584 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1586 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1587 wx.CallAfter(self.resumeButton.Enable, True)
1588 self._wizardState = 10
1590 def mcStateChange(self, state):
1591 if self.comm is None:
1593 if self.comm.isOperational():
1594 if self._wizardState == 0:
1595 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1596 wx.CallAfter(self.upButton.Enable, True)
1597 wx.CallAfter(self.downButton.Enable, True)
1598 wx.CallAfter(self.upButton2.Enable, True)
1599 wx.CallAfter(self.downButton2.Enable, True)
1600 wx.CallAfter(self.resumeButton.Enable, True)
1601 self._wizardState = -1
1602 elif self._wizardState == 11 and not self.comm.isPrinting():
1603 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1604 self.comm.sendCommand('G92 E0')
1605 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1606 self.comm.sendCommand('M104 S0')
1607 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1608 wx.CallAfter(self.infoBox.SetReadyIndicator)
1609 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1610 wx.CallAfter(self.connectButton.Enable, True)
1611 self._wizardState = 12
1612 elif self.comm.isError():
1613 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1615 def mcMessage(self, message):
1618 def mcProgress(self, lineNr):
1621 def mcZChange(self, newZ):
1624 class headOffsetCalibrationPage(InfoPage):
1625 def __init__(self, parent):
1626 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1628 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1631 self.connectButton = self.AddButton(_('Connect to printer'))
1634 self.infoBox = self.AddInfoBox()
1635 self.textEntry = self.AddTextCtrl('')
1636 self.textEntry.Enable(False)
1637 self.resumeButton = self.AddButton(_('Resume'))
1638 self.resumeButton.Enable(False)
1640 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1641 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1643 def AllowBack(self):
1646 def OnConnect(self, e = None):
1647 if self.comm is not None:
1651 wx.CallAfter(self.OnConnect)
1653 self.connectButton.Enable(False)
1654 self.comm = machineCom.MachineCom(callbackObject=self)
1655 self.infoBox.SetBusy(_('Connecting to machine.'))
1656 self._wizardState = 0
1658 def OnResume(self, e):
1659 if self._wizardState == 2:
1660 self._wizardState = 3
1661 wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1663 w = profile.getMachineSettingFloat('machine_width')
1664 d = profile.getMachineSettingFloat('machine_depth')
1666 gcode = gcodeGenerator.gcodeGenerator()
1667 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1668 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1675 gcode.addMove(w/2, 5)
1676 gcode.addMove(z=0.2)
1678 gcode.addExtrude(w/2, d-5.0)
1680 gcode.addMove(5, d/2)
1682 gcode.addExtrude(w-5.0, d/2)
1683 gcode.addRetract(15)
1686 gcode.addMove(w/2, 5)
1688 gcode.addExtrude(w/2, d-5.0)
1690 gcode.addMove(5, d/2)
1692 gcode.addExtrude(w-5.0, d/2)
1693 gcode.addRetract(15)
1698 gcode.addCmd('M400')
1700 self.comm.printGCode(gcode.list())
1701 self.resumeButton.Enable(False)
1702 elif self._wizardState == 4:
1704 float(self.textEntry.GetValue())
1707 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1708 self._wizardState = 5
1709 self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1710 self.textEntry.SetValue('0.0')
1711 self.textEntry.Enable(True)
1712 elif self._wizardState == 5:
1714 float(self.textEntry.GetValue())
1717 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1718 self._wizardState = 6
1719 self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1720 self.textEntry.SetValue('')
1721 self.textEntry.Enable(False)
1722 self.resumeButton.Enable(False)
1724 x = profile.getMachineSettingFloat('extruder_offset_x1')
1725 y = profile.getMachineSettingFloat('extruder_offset_y1')
1726 gcode = gcodeGenerator.gcodeGenerator()
1727 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1728 gcode.setPrintSpeed(25)
1731 gcode.addMove(50, 40, 0.2)
1733 for n in xrange(0, 10):
1734 gcode.addExtrude(50 + n * 10, 150)
1735 gcode.addExtrude(50 + n * 10 + 5, 150)
1736 gcode.addExtrude(50 + n * 10 + 5, 40)
1737 gcode.addExtrude(50 + n * 10 + 10, 40)
1738 gcode.addMove(40, 50)
1739 for n in xrange(0, 10):
1740 gcode.addExtrude(150, 50 + n * 10)
1741 gcode.addExtrude(150, 50 + n * 10 + 5)
1742 gcode.addExtrude(40, 50 + n * 10 + 5)
1743 gcode.addExtrude(40, 50 + n * 10 + 10)
1744 gcode.addRetract(15)
1747 gcode.addMove(50 - x, 30 - y, 0.2)
1749 for n in xrange(0, 10):
1750 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1751 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1752 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1753 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1754 gcode.addMove(30 - x, 50 - y, 0.2)
1755 for n in xrange(0, 10):
1756 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1757 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1758 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1759 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1760 gcode.addRetract(15)
1762 gcode.addCmd('M400')
1763 gcode.addCmd('M104 T0 S0')
1764 gcode.addCmd('M104 T1 S0')
1765 self.comm.printGCode(gcode.list())
1766 elif self._wizardState == 7:
1768 n = int(self.textEntry.GetValue()) - 1
1771 x = profile.getMachineSettingFloat('extruder_offset_x1')
1773 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1774 self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1775 self.textEntry.SetValue('10')
1776 self._wizardState = 8
1777 elif self._wizardState == 8:
1779 n = int(self.textEntry.GetValue()) - 1
1782 y = profile.getMachineSettingFloat('extruder_offset_y1')
1784 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1785 self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1786 self.infoBox.SetReadyIndicator()
1787 self._wizardState = 8
1789 self.resumeButton.Enable(False)
1791 def mcLog(self, message):
1792 print 'Log:', message
1794 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1795 if self._wizardState == 1:
1796 if temp[0] >= 210 and temp[1] >= 210:
1797 self._wizardState = 2
1798 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1799 wx.CallAfter(self.resumeButton.Enable, True)
1800 wx.CallAfter(self.resumeButton.SetFocus)
1802 def mcStateChange(self, state):
1803 if self.comm is None:
1805 if self.comm.isOperational():
1806 if self._wizardState == 0:
1807 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1808 self.comm.sendCommand('M105')
1809 self.comm.sendCommand('M104 S220 T0')
1810 self.comm.sendCommand('M104 S220 T1')
1811 self.comm.sendCommand('G28')
1812 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1813 self._wizardState = 1
1814 if not self.comm.isPrinting():
1815 if self._wizardState == 3:
1816 self._wizardState = 4
1817 wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1818 wx.CallAfter(self.textEntry.SetValue, '0.0')
1819 wx.CallAfter(self.textEntry.Enable, True)
1820 wx.CallAfter(self.resumeButton.Enable, True)
1821 wx.CallAfter(self.resumeButton.SetFocus)
1822 elif self._wizardState == 6:
1823 self._wizardState = 7
1824 wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1825 wx.CallAfter(self.textEntry.SetValue, '10')
1826 wx.CallAfter(self.textEntry.Enable, True)
1827 wx.CallAfter(self.resumeButton.Enable, True)
1828 wx.CallAfter(self.resumeButton.SetFocus)
1830 elif self.comm.isError():
1831 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1833 def mcMessage(self, message):
1836 def mcProgress(self, lineNr):
1839 def mcZChange(self, newZ):
1842 class bedLevelWizard(wx.wizard.Wizard):
1844 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1846 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1847 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1849 self.mainPage = bedLevelWizardMain(self)
1850 self.headOffsetCalibration = None
1852 self.RunWizard(self.mainPage)
1855 def OnPageChanging(self, e):
1856 e.GetPage().StoreData()
1858 def OnPageChanged(self, e):
1859 if e.GetPage().AllowNext():
1860 self.FindWindowById(wx.ID_FORWARD).Enable()
1862 self.FindWindowById(wx.ID_FORWARD).Disable()
1863 if e.GetPage().AllowBack():
1864 self.FindWindowById(wx.ID_BACKWARD).Enable()
1866 self.FindWindowById(wx.ID_BACKWARD).Disable()
1868 class headOffsetWizard(wx.wizard.Wizard):
1870 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1872 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1873 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1875 self.mainPage = headOffsetCalibrationPage(self)
1877 self.RunWizard(self.mainPage)
1880 def OnPageChanging(self, e):
1881 e.GetPage().StoreData()
1883 def OnPageChanged(self, e):
1884 if e.GetPage().AllowNext():
1885 self.FindWindowById(wx.ID_FORWARD).Enable()
1887 self.FindWindowById(wx.ID_FORWARD).Disable()
1888 if e.GetPage().AllowBack():
1889 self.FindWindowById(wx.ID_BACKWARD).Enable()
1891 self.FindWindowById(wx.ID_BACKWARD).Disable()