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 self.LulzbotMini = ImageButton(self.panel, _("LulzBot Mini"), self.GetBitmap('Lulzbot_mini.jpg'), style=ImageButton.IB_GROUP)
1094 self.LulzbotMini.OnSelected(self.OnLulzbotSelected)
1095 self.LulzbotMini.SetValue(True)
1096 self.LulzbotTaz5 = ImageButton(self.panel, _("LulzBot TAZ 5"), self.GetBitmap('Lulzbot_TAZ5.png'))
1097 self.LulzbotTaz5.OnSelected(self.OnLulzbotSelected)
1098 self.LulzbotTaz4 = ImageButton(self.panel, _("LulzBot TAZ 4"), self.GetBitmap('Lulzbot_TAZ4.jpg'))
1099 self.LulzbotTaz4.OnSelected(self.OnLulzbotSelected)
1100 self.OtherPrinters = ImageButton(self.panel, _("Non-LulzBot Printers"), self.GetBitmap('Other_Printers.jpg'))
1101 self.OtherPrinters.OnSelected(self.OnOthersSelected)
1103 self.panel.GetSizer().Add(self.LulzbotMini, pos=(0, 0))
1104 self.panel.GetSizer().Add(self.LulzbotTaz4, pos=(0, 1))
1105 self.panel.GetSizer().Add(self.LulzbotTaz5, pos=(1, 0))
1106 self.panel.GetSizer().Add(self.OtherPrinters, pos=(1, 1))
1108 def GetBitmap(self, filename):
1109 image = wx.Image(resources.getPathForImage(filename))
1110 image_scaled = image.Scale(LulzbotMachineSelectPage.IMAGE_WIDTH,
1111 LulzbotMachineSelectPage.IMAGE_HEIGHT, wx.IMAGE_QUALITY_HIGH)
1112 return wx.BitmapFromImage(image_scaled)
1114 def OnOthersSelected(self):
1115 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().machineSelectPage)
1117 def OnLulzbotSelected(self):
1118 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotToolheadPage)
1120 def AllowNext(self):
1123 def AllowBack(self):
1126 def StoreData(self):
1127 if self.LulzbotTaz4.GetValue() or self.LulzbotTaz5.GetValue() or self.LulzbotMini.GetValue():
1128 if self.LulzbotTaz4.GetValue():
1129 profile.putMachineSetting('machine_width', '290')
1130 profile.putMachineSetting('machine_depth', '275')
1131 profile.putMachineSetting('machine_height', '250')
1132 profile.putProfileSetting('nozzle_size', '0.35')
1133 profile.putMachineSetting('machine_name', 'LulzBot TAZ 4')
1134 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_4')
1135 profile.putMachineSetting('serial_baud', '115200')
1136 elif self.LulzbotTaz5.GetValue():
1137 profile.putMachineSetting('machine_width', '290')
1138 profile.putMachineSetting('machine_depth', '275')
1139 profile.putMachineSetting('machine_height', '250')
1140 profile.putMachineSetting('serial_baud', '115200')
1141 # Machine type and name are set in the nozzle select page
1143 profile.putMachineSetting('machine_width', '155')
1144 profile.putMachineSetting('machine_depth', '155')
1145 profile.putMachineSetting('machine_height', '163')
1146 profile.putProfileSetting('nozzle_size', '0.5')
1147 profile.putMachineSetting('machine_name', 'LulzBot Mini')
1148 profile.putMachineSetting('machine_type', 'lulzbot_mini')
1149 profile.putMachineSetting('serial_baud', '115200')
1150 profile.putMachineSetting('extruder_head_size_min_x', '40')
1151 profile.putMachineSetting('extruder_head_size_max_x', '75')
1152 profile.putMachineSetting('extruder_head_size_min_y', '25')
1153 profile.putMachineSetting('extruder_head_size_max_y', '55')
1154 profile.putMachineSetting('extruder_head_size_height', '17')
1156 profile.putMachineSetting('machine_center_is_zero', 'False')
1157 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
1158 profile.putMachineSetting('has_heated_bed', 'True')
1159 profile.putMachineSetting('extruder_head_size_min_x', '0.0')
1160 profile.putMachineSetting('extruder_head_size_min_y', '0.0')
1161 profile.putMachineSetting('extruder_head_size_max_x', '0.0')
1162 profile.putMachineSetting('extruder_head_size_max_y', '0.0')
1163 profile.putMachineSetting('extruder_head_size_height', '0.0')
1164 profile.putProfileSetting('retraction_enable', 'True')
1165 profile.putPreference('startMode', 'Simple')
1166 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
1167 profile.checkAndUpdateMachineName()
1169 class LulzbotReadyPage(InfoPage):
1170 def __init__(self, parent):
1171 super(LulzbotReadyPage, self).__init__(parent, _("LulzBot TAZ/Mini"))
1172 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1173 self.AddText(_('Cura is now ready to be used with your LulzBot 3D printer.'))
1175 self.AddText(_('For more information about using Cura with your LulzBot'))
1176 self.AddText(_('3D printer, please visit www.LulzBot.com/cura'))
1179 class LulzbotToolheadSelectPage(InfoPage):
1180 url='http://lulzbot.com/toolhead-identification'
1182 def __init__(self, parent):
1183 super(LulzbotToolheadSelectPage, self).__init__(parent, _("LulzBot Toolhead Selection"))
1185 self.mini_choices = [_('Standard'), _('Flexystruder')]
1186 self.taz_choices = [_('Standard v1'),
1187 _('Standard v2 0.35 mm nozzle'), _('Standard v2 0.5 mm nozzle'),
1188 _('Flexystruder v1'), _('Flexystruder v2'),
1189 _('Dually v1'), _('Dually v2'),
1190 _('FlexyDually v1'), _('FlexyDually v2')]
1191 self.description_map = {
1192 _('Standard'): _('This is the standard toolhead that comes with the Lulzbot Mini'),
1193 _('Flexystruder'): _('This is the Flexystruder for the Lulzbot Mini\nIt is used for printing Flexible materials'),
1194 _('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'),
1195 _('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'),
1196 _('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'),
1197 _('Flexystruder v1'): _('It\'s the flexy!'),
1198 _('Flexystruder v2'): _('It\'s the flexy v2!'),
1199 _('Dually v1'): _('It\'s the dualy v1!'),
1200 _('Dually v2'): _('It\'s the dual v2!'),
1201 _('FlexyDually v1'): _('It\'s the flexy dually v1!'),
1202 _('FlexyDually v2'): _('It\'s the flexy dual v2!')
1205 _('Standard'): 'Lulzbot_Toolhead_Mini_Standard.jpg',
1206 _('Flexystruder'): 'Lulzbot_logo.png',
1207 _('Standard v1'): 'Lulzbot_logo.png',
1208 _('Standard v2 0.35 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1209 _('Standard v2 0.5 mm nozzle'): 'Lulzbot_Toolhead_TAZ_Single_v2.jpg',
1210 _('Flexystruder v1'): 'Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg',
1211 _('Flexystruder v2'): 'Lulzbot_logo.png',
1212 _('Dually v1'): 'Lulzbot_Toolhead_TAZ_Dually_v1.jpg',
1213 _('Dually v2'): 'Lulzbot_logo.png',
1214 _('FlexyDually v1'): 'Lulzbot_logo.png',
1215 _('FlexyDually v2'): 'Lulzbot_logo.png'
1217 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1218 printer_name = profile.getMachineSetting('machine_type')
1219 self.Bind(wx.wizard.EVT_WIZARD_PAGE_SHOWN, self.OnPageShown)
1221 self.AddText(_('Please select your currently installed Tool Head'))
1222 txt = self.AddText(_('It is important to select the correct Tool head for your printer.\n' +
1223 'Flashing the wrong firmware on your printer can cause damage to your printer and to your toolhead\n' +
1224 'If you are not sure which toolhead you have, please refer to this webpage for more information: '))
1225 txt.SetForegroundColour(wx.RED)
1226 button = self.AddButton(self.url)
1227 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1230 #self.combo = self.AddCombo(_('Currently installed Toolhead'), [''])
1231 #self.combo.SetEditable(False)
1232 #self.combo.Bind(wx.EVT_COMBOBOX, self.OnToolheadSelected)
1233 #self.description = self.AddText('\n\n')
1234 #self.description.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD))
1235 #self.image = self.AddBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[self.mini_choices[0]])))
1236 self.panel = self.AddPanel()
1237 ib1 = ImageButton(self.panel, "Mini", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg')), "Some description", style=ImageButton.IB_GROUP)
1238 ib2 = ImageButton(self.panel, "TAZ 4 ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Single_v2.jpg')), "Some description 2")
1239 ib3 = ImageButton(self.panel, "TAZ 5 ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Flexystruder_v1.jpg')))
1240 ib4 = ImageButton(self.panel, "Others ", wx.Bitmap(resources.getPathForImage('Lulzbot_Toolhead_TAZ_Dually_v1.jpg')))
1241 self.panel.GetSizer().Add(ib1, pos=(0, 0), flag=wx.EXPAND)
1242 self.panel.GetSizer().Add(ib2, pos=(0, 1), flag=wx.EXPAND)
1243 self.panel.GetSizer().Add(ib3, pos=(1, 0), flag=wx.EXPAND)
1244 self.panel.GetSizer().Add(ib4, pos=(1, 1), flag=wx.EXPAND)
1247 def OnPageShown(self, e):
1248 printer_name = profile.getMachineSetting('machine_type')
1249 if printer_name == 'lulzbot_mini':
1250 choices = self.mini_choices
1253 choices = self.taz_choices
1254 if printer_name == 'lulzbot_TAZ_4':
1256 elif printer_name == 'lulzbot_TAZ_5':
1262 self.combo.AppendItems(choices)
1263 self.combo.SetValue(choices[default])
1264 self.OnToolheadSelected(e)
1266 def OnUrlClick(self, e):
1267 webbrowser.open(LulzbotToolheadSelectPage.url)
1269 def OnToolheadSelected(self, e):
1270 toolhead = self.combo.GetValue()
1271 if self.description_map.has_key(toolhead):
1272 self.image.SetBitmap(wx.Bitmap(resources.getPathForImage(self.image_map[toolhead])))
1273 self.description.SetLabel(self.description_map[toolhead])
1275 self.image.SetBitmap(wx.NullBitmap)
1276 self.description.SetLabel('\n\n')
1281 class Taz5NozzleSelectPage(InfoPage):
1282 url='http://lulzbot.com/printer-identification'
1284 def __init__(self, parent):
1285 super(Taz5NozzleSelectPage, self).__init__(parent, _("LulzBot TAZ5"))
1286 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Lulzbot_logo.png')))
1288 self.AddText(_(' '))
1289 self.AddText(_('Please select nozzle size:'))
1290 self.Nozzle35Radio = self.AddRadioButton("0.35 mm", style=wx.RB_GROUP)
1291 self.Nozzle35Radio.SetValue(True)
1292 self.Nozzle50Radio = self.AddRadioButton("0.5 mm")
1293 self.AddText(_(' '))
1296 self.AddText(_('If you are not sure which nozzle size you have'))
1297 self.AddText(_('please check this webpage: '))
1298 button = self.AddButton(Taz5NozzleSelectPage.url)
1299 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
1301 def OnUrlClick(self, e):
1302 webbrowser.open(Taz5NozzleSelectPage.url)
1304 def StoreData(self):
1305 if self.Nozzle35Radio.GetValue():
1306 profile.putProfileSetting('nozzle_size', '0.35')
1307 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.35 nozzle)')
1308 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5')
1311 profile.putProfileSetting('nozzle_size', '0.5')
1312 profile.putMachineSetting('machine_name', 'LulzBot TAZ 5 (0.5 nozzle)')
1313 profile.putMachineSetting('machine_type', 'lulzbot_TAZ_5_05nozzle')
1315 class ConfigWizard(wx.wizard.Wizard):
1316 def __init__(self, addNew = False):
1317 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
1319 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
1321 profile.setActiveMachine(profile.getMachineCount())
1323 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1324 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1325 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1327 self.machineSelectPage = MachineSelectPage(self)
1328 self.ultimakerSelectParts = SelectParts(self)
1329 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1330 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1331 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1332 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1333 self.bedLevelPage = bedLevelWizardMain(self)
1334 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1335 self.printrbotSelectType = PrintrbotPage(self)
1336 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1337 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1338 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1340 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1341 self.lulzbotReadyPage = LulzbotReadyPage(self)
1342 self.taz5NozzleSelectPage = Taz5NozzleSelectPage(self)
1343 self.lulzbotToolheadPage = LulzbotToolheadSelectPage(self)
1344 self.lulzbotMachineSelectPage = LulzbotMachineSelectPage(self)
1346 wx.wizard.WizardPageSimple.Chain(self.lulzbotMachineSelectPage, self.lulzbotToolheadPage)
1347 wx.wizard.WizardPageSimple.Chain(self.lulzbotToolheadPage, self.lulzbotReadyPage)
1348 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1349 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1350 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1351 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1352 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1353 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1355 self.RunWizard(self.lulzbotMachineSelectPage)
1358 def OnPageChanging(self, e):
1359 e.GetPage().StoreData()
1361 def OnPageChanged(self, e):
1362 if e.GetPage().AllowNext():
1363 self.FindWindowById(wx.ID_FORWARD).Enable()
1365 self.FindWindowById(wx.ID_FORWARD).Disable()
1366 if e.GetPage().AllowBack():
1367 self.FindWindowById(wx.ID_BACKWARD).Enable()
1369 self.FindWindowById(wx.ID_BACKWARD).Disable()
1371 def OnCancel(self, e):
1372 new_machine_index = int(profile.getPreferenceFloat('active_machine'))
1373 profile.setActiveMachine(self._old_machine_index)
1374 profile.removeMachine(new_machine_index)
1376 class bedLevelWizardMain(InfoPage):
1377 def __init__(self, parent):
1378 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1380 self.AddText(_('This wizard will help you in leveling your printer bed'))
1382 self.AddText(_('It will do the following steps'))
1383 self.AddText(_('* Move the printer head to each corner'))
1384 self.AddText(_(' and let you adjust the height of the bed to the nozzle'))
1385 self.AddText(_('* Print a line around the bed to check if it is level'))
1388 self.connectButton = self.AddButton(_('Connect to printer'))
1391 self.infoBox = self.AddInfoBox()
1392 self.resumeButton = self.AddButton(_('Resume'))
1393 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1394 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1395 self.resumeButton.Enable(False)
1397 self.upButton.Enable(False)
1398 self.downButton.Enable(False)
1399 self.upButton2.Enable(False)
1400 self.downButton2.Enable(False)
1402 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1403 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1404 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1405 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1406 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1407 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1409 def OnConnect(self, e = None):
1410 if self.comm is not None:
1414 wx.CallAfter(self.OnConnect)
1416 self.connectButton.Enable(False)
1417 self.comm = machineCom.MachineCom(callbackObject=self)
1418 self.infoBox.SetBusy(_('Connecting to machine.'))
1419 self._wizardState = 0
1421 def OnBedUp(self, e):
1422 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1423 self.comm.sendCommand('G92 Z10')
1424 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1425 self.comm.sendCommand('M400')
1427 def OnBedDown(self, e):
1428 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1429 self.comm.sendCommand('G92 Z10')
1430 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1431 self.comm.sendCommand('M400')
1433 def OnBedUp2(self, e):
1434 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1435 self.comm.sendCommand('G92 Z10')
1436 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1437 self.comm.sendCommand('M400')
1439 def OnBedDown2(self, e):
1440 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1441 self.comm.sendCommand('G92 Z10')
1442 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1443 self.comm.sendCommand('M400')
1445 def AllowNext(self):
1446 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1447 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1450 def OnResume(self, e):
1451 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1452 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1453 if self._wizardState == -1:
1454 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1455 wx.CallAfter(self.upButton.Enable, False)
1456 wx.CallAfter(self.downButton.Enable, False)
1457 wx.CallAfter(self.upButton2.Enable, False)
1458 wx.CallAfter(self.downButton2.Enable, False)
1459 self.comm.sendCommand('M105')
1460 self.comm.sendCommand('G28')
1461 self._wizardState = 1
1462 elif self._wizardState == 2:
1463 if profile.getMachineSetting('has_heated_bed') == 'True':
1464 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1465 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1466 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1467 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1468 self.comm.sendCommand('M400')
1469 self._wizardState = 3
1471 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1472 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1473 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1474 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1475 self.comm.sendCommand('M400')
1476 self._wizardState = 3
1477 elif self._wizardState == 4:
1478 if profile.getMachineSetting('has_heated_bed') == 'True':
1479 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1480 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1481 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1482 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1483 self.comm.sendCommand('M400')
1484 self._wizardState = 7
1486 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1487 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1488 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1489 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1490 self.comm.sendCommand('M400')
1491 self._wizardState = 5
1492 elif self._wizardState == 6:
1493 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1494 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1495 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1496 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1497 self.comm.sendCommand('M400')
1498 self._wizardState = 7
1499 elif self._wizardState == 8:
1500 wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1501 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1502 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1503 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1504 self._wizardState = 9
1505 elif self._wizardState == 10:
1506 self._wizardState = 11
1507 wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1508 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1509 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1510 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1511 w = profile.getMachineSettingFloat('machine_width') - 10
1512 d = profile.getMachineSettingFloat('machine_depth')
1513 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1514 filamentArea = math.pi * filamentRadius * filamentRadius
1515 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1519 'G1 Z2 F%d' % (feedZ),
1521 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1522 'G1 Z0.3 F%d' % (feedZ)]
1524 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1526 for i in xrange(0, 3):
1527 dist = 5.0 + 0.4 * float(i)
1528 eValue += (d - 2.0*dist) * ePerMM
1529 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1530 eValue += (w - 2.0*dist) * ePerMM
1531 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1532 eValue += (d - 2.0*dist) * ePerMM
1533 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1534 eValue += (w - 2.0*dist) * ePerMM
1535 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1537 gcodeList.append('M400')
1538 self.comm.printGCode(gcodeList)
1539 self.resumeButton.Enable(False)
1541 def mcLog(self, message):
1542 print 'Log:', message
1544 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1545 if self._wizardState == 1:
1546 self._wizardState = 2
1547 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1548 wx.CallAfter(self.resumeButton.Enable, True)
1549 elif self._wizardState == 3:
1550 self._wizardState = 4
1551 if profile.getMachineSetting('has_heated_bed') == 'True':
1552 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1554 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1555 wx.CallAfter(self.resumeButton.Enable, True)
1556 elif self._wizardState == 5:
1557 self._wizardState = 6
1558 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1559 wx.CallAfter(self.resumeButton.Enable, True)
1560 elif self._wizardState == 7:
1561 self._wizardState = 8
1562 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1563 wx.CallAfter(self.resumeButton.Enable, True)
1564 elif self._wizardState == 9:
1565 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1566 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1568 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1569 wx.CallAfter(self.resumeButton.Enable, True)
1570 self._wizardState = 10
1572 def mcStateChange(self, state):
1573 if self.comm is None:
1575 if self.comm.isOperational():
1576 if self._wizardState == 0:
1577 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1578 wx.CallAfter(self.upButton.Enable, True)
1579 wx.CallAfter(self.downButton.Enable, True)
1580 wx.CallAfter(self.upButton2.Enable, True)
1581 wx.CallAfter(self.downButton2.Enable, True)
1582 wx.CallAfter(self.resumeButton.Enable, True)
1583 self._wizardState = -1
1584 elif self._wizardState == 11 and not self.comm.isPrinting():
1585 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1586 self.comm.sendCommand('G92 E0')
1587 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1588 self.comm.sendCommand('M104 S0')
1589 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1590 wx.CallAfter(self.infoBox.SetReadyIndicator)
1591 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1592 wx.CallAfter(self.connectButton.Enable, True)
1593 self._wizardState = 12
1594 elif self.comm.isError():
1595 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1597 def mcMessage(self, message):
1600 def mcProgress(self, lineNr):
1603 def mcZChange(self, newZ):
1606 class headOffsetCalibrationPage(InfoPage):
1607 def __init__(self, parent):
1608 super(headOffsetCalibrationPage, self).__init__(parent, _("Printer head offset calibration"))
1610 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1613 self.connectButton = self.AddButton(_('Connect to printer'))
1616 self.infoBox = self.AddInfoBox()
1617 self.textEntry = self.AddTextCtrl('')
1618 self.textEntry.Enable(False)
1619 self.resumeButton = self.AddButton(_('Resume'))
1620 self.resumeButton.Enable(False)
1622 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1623 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1625 def AllowBack(self):
1628 def OnConnect(self, e = None):
1629 if self.comm is not None:
1633 wx.CallAfter(self.OnConnect)
1635 self.connectButton.Enable(False)
1636 self.comm = machineCom.MachineCom(callbackObject=self)
1637 self.infoBox.SetBusy(_('Connecting to machine.'))
1638 self._wizardState = 0
1640 def OnResume(self, e):
1641 if self._wizardState == 2:
1642 self._wizardState = 3
1643 wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1645 w = profile.getMachineSettingFloat('machine_width')
1646 d = profile.getMachineSettingFloat('machine_depth')
1648 gcode = gcodeGenerator.gcodeGenerator()
1649 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1650 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1657 gcode.addMove(w/2, 5)
1658 gcode.addMove(z=0.2)
1660 gcode.addExtrude(w/2, d-5.0)
1662 gcode.addMove(5, d/2)
1664 gcode.addExtrude(w-5.0, d/2)
1665 gcode.addRetract(15)
1668 gcode.addMove(w/2, 5)
1670 gcode.addExtrude(w/2, d-5.0)
1672 gcode.addMove(5, d/2)
1674 gcode.addExtrude(w-5.0, d/2)
1675 gcode.addRetract(15)
1680 gcode.addCmd('M400')
1682 self.comm.printGCode(gcode.list())
1683 self.resumeButton.Enable(False)
1684 elif self._wizardState == 4:
1686 float(self.textEntry.GetValue())
1689 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1690 self._wizardState = 5
1691 self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1692 self.textEntry.SetValue('0.0')
1693 self.textEntry.Enable(True)
1694 elif self._wizardState == 5:
1696 float(self.textEntry.GetValue())
1699 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1700 self._wizardState = 6
1701 self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1702 self.textEntry.SetValue('')
1703 self.textEntry.Enable(False)
1704 self.resumeButton.Enable(False)
1706 x = profile.getMachineSettingFloat('extruder_offset_x1')
1707 y = profile.getMachineSettingFloat('extruder_offset_y1')
1708 gcode = gcodeGenerator.gcodeGenerator()
1709 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1710 gcode.setPrintSpeed(25)
1713 gcode.addMove(50, 40, 0.2)
1715 for n in xrange(0, 10):
1716 gcode.addExtrude(50 + n * 10, 150)
1717 gcode.addExtrude(50 + n * 10 + 5, 150)
1718 gcode.addExtrude(50 + n * 10 + 5, 40)
1719 gcode.addExtrude(50 + n * 10 + 10, 40)
1720 gcode.addMove(40, 50)
1721 for n in xrange(0, 10):
1722 gcode.addExtrude(150, 50 + n * 10)
1723 gcode.addExtrude(150, 50 + n * 10 + 5)
1724 gcode.addExtrude(40, 50 + n * 10 + 5)
1725 gcode.addExtrude(40, 50 + n * 10 + 10)
1726 gcode.addRetract(15)
1729 gcode.addMove(50 - x, 30 - y, 0.2)
1731 for n in xrange(0, 10):
1732 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1733 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1734 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1735 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1736 gcode.addMove(30 - x, 50 - y, 0.2)
1737 for n in xrange(0, 10):
1738 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1739 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1740 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1741 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1742 gcode.addRetract(15)
1744 gcode.addCmd('M400')
1745 gcode.addCmd('M104 T0 S0')
1746 gcode.addCmd('M104 T1 S0')
1747 self.comm.printGCode(gcode.list())
1748 elif self._wizardState == 7:
1750 n = int(self.textEntry.GetValue()) - 1
1753 x = profile.getMachineSettingFloat('extruder_offset_x1')
1755 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1756 self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1757 self.textEntry.SetValue('10')
1758 self._wizardState = 8
1759 elif self._wizardState == 8:
1761 n = int(self.textEntry.GetValue()) - 1
1764 y = profile.getMachineSettingFloat('extruder_offset_y1')
1766 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1767 self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1768 self.infoBox.SetReadyIndicator()
1769 self._wizardState = 8
1771 self.resumeButton.Enable(False)
1773 def mcLog(self, message):
1774 print 'Log:', message
1776 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1777 if self._wizardState == 1:
1778 if temp[0] >= 210 and temp[1] >= 210:
1779 self._wizardState = 2
1780 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1781 wx.CallAfter(self.resumeButton.Enable, True)
1782 wx.CallAfter(self.resumeButton.SetFocus)
1784 def mcStateChange(self, state):
1785 if self.comm is None:
1787 if self.comm.isOperational():
1788 if self._wizardState == 0:
1789 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1790 self.comm.sendCommand('M105')
1791 self.comm.sendCommand('M104 S220 T0')
1792 self.comm.sendCommand('M104 S220 T1')
1793 self.comm.sendCommand('G28')
1794 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1795 self._wizardState = 1
1796 if not self.comm.isPrinting():
1797 if self._wizardState == 3:
1798 self._wizardState = 4
1799 wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1800 wx.CallAfter(self.textEntry.SetValue, '0.0')
1801 wx.CallAfter(self.textEntry.Enable, True)
1802 wx.CallAfter(self.resumeButton.Enable, True)
1803 wx.CallAfter(self.resumeButton.SetFocus)
1804 elif self._wizardState == 6:
1805 self._wizardState = 7
1806 wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1807 wx.CallAfter(self.textEntry.SetValue, '10')
1808 wx.CallAfter(self.textEntry.Enable, True)
1809 wx.CallAfter(self.resumeButton.Enable, True)
1810 wx.CallAfter(self.resumeButton.SetFocus)
1812 elif self.comm.isError():
1813 wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1815 def mcMessage(self, message):
1818 def mcProgress(self, lineNr):
1821 def mcZChange(self, newZ):
1824 class bedLevelWizard(wx.wizard.Wizard):
1826 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1828 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1829 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1831 self.mainPage = bedLevelWizardMain(self)
1832 self.headOffsetCalibration = None
1834 self.RunWizard(self.mainPage)
1837 def OnPageChanging(self, e):
1838 e.GetPage().StoreData()
1840 def OnPageChanged(self, e):
1841 if e.GetPage().AllowNext():
1842 self.FindWindowById(wx.ID_FORWARD).Enable()
1844 self.FindWindowById(wx.ID_FORWARD).Disable()
1845 if e.GetPage().AllowBack():
1846 self.FindWindowById(wx.ID_BACKWARD).Enable()
1848 self.FindWindowById(wx.ID_BACKWARD).Disable()
1850 class headOffsetWizard(wx.wizard.Wizard):
1852 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1854 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1855 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1857 self.mainPage = headOffsetCalibrationPage(self)
1859 self.RunWizard(self.mainPage)
1862 def OnPageChanging(self, e):
1863 e.GetPage().StoreData()
1865 def OnPageChanged(self, e):
1866 if e.GetPage().AllowNext():
1867 self.FindWindowById(wx.ID_FORWARD).Enable()
1869 self.FindWindowById(wx.ID_FORWARD).Disable()
1870 if e.GetPage().AllowBack():
1871 self.FindWindowById(wx.ID_BACKWARD).Enable()
1873 self.FindWindowById(wx.ID_BACKWARD).Disable()