1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
13 from Cura.gui import firmwareInstall
14 from Cura.gui import printWindow
15 from Cura.util import machineCom
16 from Cura.util import profile
17 from Cura.util import gcodeGenerator
18 from Cura.util import resources
21 class InfoBox(wx.Panel):
22 def __init__(self, parent):
23 super(InfoBox, self).__init__(parent)
24 self.SetBackgroundColour('#FFFF80')
26 self.sizer = wx.GridBagSizer(5, 5)
27 self.SetSizer(self.sizer)
29 self.attentionBitmap = wx.Bitmap(resources.getPathForImage('attention.png'))
30 self.errorBitmap = wx.Bitmap(resources.getPathForImage('error.png'))
31 self.readyBitmap = wx.Bitmap(resources.getPathForImage('ready.png'))
33 wx.Bitmap(resources.getPathForImage('busy-0.png')),
34 wx.Bitmap(resources.getPathForImage('busy-1.png')),
35 wx.Bitmap(resources.getPathForImage('busy-2.png')),
36 wx.Bitmap(resources.getPathForImage('busy-3.png'))
39 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
40 self.text = wx.StaticText(self, -1, '')
41 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
42 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
43 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
44 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
45 self.sizer.AddGrowableCol(1)
47 self.extraInfoButton.Show(False)
49 self.extraInfoUrl = ''
51 self.timer = wx.Timer(self)
52 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
53 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
56 def SetInfo(self, info):
57 self.SetBackgroundColour('#FFFF80')
58 self.text.SetLabel(info)
59 self.extraInfoButton.Show(False)
62 def SetError(self, info, extraInfoUrl):
63 self.extraInfoUrl = extraInfoUrl
64 self.SetBackgroundColour('#FF8080')
65 self.text.SetLabel(info)
66 self.extraInfoButton.Show(True)
68 self.SetErrorIndicator()
71 def SetAttention(self, info):
72 self.SetBackgroundColour('#FFFF80')
73 self.text.SetLabel(info)
74 self.extraInfoButton.Show(False)
75 self.SetAttentionIndicator()
79 def SetBusy(self, info):
81 self.SetBusyIndicator()
83 def SetBusyIndicator(self):
85 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
87 def doExtraInfo(self, e):
88 webbrowser.open(self.extraInfoUrl)
90 def doBusyUpdate(self, e):
91 if self.busyState is None:
94 if self.busyState >= len(self.busyBitmap):
96 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
98 def SetReadyIndicator(self):
100 self.bitmap.SetBitmap(self.readyBitmap)
102 def SetErrorIndicator(self):
103 self.busyState = None
104 self.bitmap.SetBitmap(self.errorBitmap)
106 def SetAttentionIndicator(self):
107 self.busyState = None
108 self.bitmap.SetBitmap(self.attentionBitmap)
111 class InfoPage(wx.wizard.WizardPageSimple):
112 def __init__(self, parent, title):
113 wx.wizard.WizardPageSimple.__init__(self, parent)
115 sizer = wx.GridBagSizer(5, 5)
119 title = wx.StaticText(self, -1, title)
120 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
121 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
122 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
123 sizer.AddGrowableCol(1)
127 def AddText(self, info):
128 text = wx.StaticText(self, -1, info)
129 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
133 def AddSeperator(self):
134 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
137 def AddHiddenSeperator(self):
140 def AddInfoBox(self):
141 infoBox = InfoBox(self)
142 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
146 def AddRadioButton(self, label, style=0):
147 radio = wx.RadioButton(self, -1, label, style=style)
148 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
152 def AddCheckbox(self, label, checked=False):
153 check = wx.CheckBox(self, -1)
154 text = wx.StaticText(self, -1, label)
155 check.SetValue(checked)
156 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
157 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
161 def AddButton(self, label):
162 button = wx.Button(self, -1, label)
163 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
167 def AddDualButton(self, label1, label2):
168 button1 = wx.Button(self, -1, label1)
169 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
170 button2 = wx.Button(self, -1, label2)
171 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
173 return button1, button2
175 def AddTextCtrl(self, value):
176 ret = wx.TextCtrl(self, -1, value)
177 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
181 def AddLabelTextCtrl(self, info, value):
182 text = wx.StaticText(self, -1, info)
183 ret = wx.TextCtrl(self, -1, value)
184 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
185 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
189 def AddTextCtrlButton(self, value, buttonText):
190 text = wx.TextCtrl(self, -1, value)
191 button = wx.Button(self, -1, buttonText)
192 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
193 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
197 def AddBitmap(self, bitmap):
198 bitmap = wx.StaticBitmap(self, -1, bitmap)
199 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
203 def AddCheckmark(self, label, bitmap):
204 check = wx.StaticBitmap(self, -1, bitmap)
205 text = wx.StaticText(self, -1, label)
206 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
207 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
218 class FirstInfoPage(InfoPage):
219 def __init__(self, parent, addNew):
221 super(FirstInfoPage, self).__init__(parent, _("Add new machine wizard"))
223 super(FirstInfoPage, self).__init__(parent, _("First time run wizard"))
224 self.AddText(_("Welcome, and thanks for trying Cura!"))
226 self.AddText(_("This wizard will help you in setting up Cura for your machine."))
227 # self.AddText(_("This wizard will help you with the following steps:"))
228 # self.AddText(_("* Configure Cura for your machine"))
229 # self.AddText(_("* Optionally upgrade your firmware"))
230 # self.AddText(_("* Optionally check if your machine is working safely"))
231 # self.AddText(_("* Optionally level your printer bed"))
233 #self.AddText('* Calibrate your machine')
234 #self.AddText('* Do your first print')
237 class OtherMachineSelectPage(InfoPage):
238 def __init__(self, parent):
239 super(OtherMachineSelectPage, self).__init__(parent, "Other machine information")
240 self.AddText(_("The following pre-defined machine profiles are available"))
241 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."))
243 machines = resources.getDefaultMachineProfiles()
245 for filename in machines:
246 name = os.path.splitext(os.path.basename(filename))[0]
247 item = self.AddRadioButton(name)
248 item.filename = filename
249 item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
250 self.options.append(item)
252 item = self.AddRadioButton('Custom...')
254 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
256 def OnProfileSelect(self, e):
257 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
259 def OnOtherSelect(self, e):
260 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
263 for option in self.options:
264 if option.GetValue():
265 profile.loadProfile(option.filename)
266 profile.loadMachineSettings(option.filename)
268 class OtherMachineInfoPage(InfoPage):
269 def __init__(self, parent):
270 super(OtherMachineInfoPage, self).__init__(parent, "Cura Ready!")
271 self.AddText(_("Cura is now ready to be used!"))
273 class CustomRepRapInfoPage(InfoPage):
274 def __init__(self, parent):
275 super(CustomRepRapInfoPage, self).__init__(parent, "Custom RepRap information")
276 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
277 self.AddText(_("Be sure to review the default profile before running it on your machine."))
278 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
280 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
282 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
283 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
284 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
285 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "60")
286 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
287 self.heatedBed = self.AddCheckbox(_("Heated bed"))
288 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
291 profile.putMachineSetting('machine_name', self.machineName.GetValue())
292 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
293 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
294 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
295 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
296 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
297 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
298 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
299 profile.putMachineSetting('extruder_head_size_min_x', '0')
300 profile.putMachineSetting('extruder_head_size_min_y', '0')
301 profile.putMachineSetting('extruder_head_size_max_x', '0')
302 profile.putMachineSetting('extruder_head_size_max_y', '0')
303 profile.putMachineSetting('extruder_head_size_height', '0')
304 profile.checkAndUpdateMachineName()
306 class MachineSelectPage(InfoPage):
307 def __init__(self, parent):
308 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
309 self.AddText(_("What kind of machine do you have:"))
311 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2", style=wx.RB_GROUP)
312 self.Ultimaker2Radio.SetValue(True)
313 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
314 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
315 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
316 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot)"))
317 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
319 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
320 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
321 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
322 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
323 self.SubmitUserStats.SetValue(True)
325 def OnUltimaker2Select(self, e):
326 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
328 def OnUltimakerSelect(self, e):
329 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
331 def OnOtherSelect(self, e):
332 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
335 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
339 profile.putProfileSetting('retraction_enable', 'True')
340 if self.Ultimaker2Radio.GetValue():
341 profile.putMachineSetting('machine_width', '230')
342 profile.putMachineSetting('machine_depth', '225')
343 profile.putMachineSetting('machine_height', '205')
344 profile.putMachineSetting('machine_name', 'ultimaker2')
345 profile.putMachineSetting('machine_type', 'ultimaker2')
346 profile.putMachineSetting('machine_center_is_zero', 'False')
347 profile.putMachineSetting('has_heated_bed', 'True')
348 profile.putMachineSetting('gcode_flavor', 'UltiGCode')
349 profile.putMachineSetting('extruder_head_size_min_x', '40.0')
350 profile.putMachineSetting('extruder_head_size_min_y', '10.0')
351 profile.putMachineSetting('extruder_head_size_max_x', '60.0')
352 profile.putMachineSetting('extruder_head_size_max_y', '30.0')
353 profile.putMachineSetting('extruder_head_size_height', '55.0')
354 profile.putProfileSetting('nozzle_size', '0.4')
355 profile.putProfileSetting('fan_full_height', '5.0')
356 profile.putMachineSetting('extruder_offset_x1', '18.0')
357 profile.putMachineSetting('extruder_offset_y1', '0.0')
358 elif self.UltimakerRadio.GetValue():
359 profile.putMachineSetting('machine_width', '205')
360 profile.putMachineSetting('machine_depth', '205')
361 profile.putMachineSetting('machine_height', '200')
362 profile.putMachineSetting('machine_name', 'ultimaker original')
363 profile.putMachineSetting('machine_type', 'ultimaker')
364 profile.putMachineSetting('machine_center_is_zero', 'False')
365 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
366 profile.putProfileSetting('nozzle_size', '0.4')
367 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
368 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
369 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
370 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
371 profile.putMachineSetting('extruder_head_size_height', '60.0')
373 profile.putMachineSetting('machine_width', '80')
374 profile.putMachineSetting('machine_depth', '80')
375 profile.putMachineSetting('machine_height', '60')
376 profile.putMachineSetting('machine_name', 'reprap')
377 profile.putMachineSetting('machine_type', 'reprap')
378 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
379 profile.putPreference('startMode', 'Normal')
380 profile.putProfileSetting('nozzle_size', '0.5')
381 profile.checkAndUpdateMachineName()
382 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
383 if self.SubmitUserStats.GetValue():
384 profile.putPreference('submit_slice_information', 'True')
386 profile.putPreference('submit_slice_information', 'False')
389 class SelectParts(InfoPage):
390 def __init__(self, parent):
391 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
392 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."))
394 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
395 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
396 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
398 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."))
399 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
400 self.springExtruder.SetValue(True)
403 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
404 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
405 if self.dualExtrusion.GetValue():
406 profile.putMachineSetting('extruder_amount', '2')
407 profile.putMachineSetting('machine_depth', '195')
409 profile.putMachineSetting('extruder_amount', '1')
410 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
411 profile.putProfileSetting('retraction_enable', 'True')
413 profile.putProfileSetting('retraction_enable', 'False')
416 class UltimakerFirmwareUpgradePage(InfoPage):
417 def __init__(self, parent):
418 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
419 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."))
420 self.AddHiddenSeperator()
421 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
422 self.AddHiddenSeperator()
423 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."))
424 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
425 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
426 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
427 self.AddHiddenSeperator()
428 self.AddText(_("Do not upgrade to this firmware if:"))
429 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
430 self.AddText(_("* Have other changes in the firmware"))
431 # button = self.AddButton('Goto this page for a custom firmware')
432 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
437 def OnUpgradeClick(self, e):
438 if firmwareInstall.InstallFirmware():
439 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
441 def OnSkipClick(self, e):
442 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
443 self.GetParent().ShowPage(self.GetNext())
445 def OnUrlClick(self, e):
446 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
448 class UltimakerCheckupPage(InfoPage):
449 def __init__(self, parent):
450 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
452 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
453 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
454 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
455 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
456 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
457 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
458 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
459 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
460 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
461 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
464 _("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."))
465 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
466 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
467 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
469 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
470 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
471 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
473 self.infoBox = self.AddInfoBox()
474 self.machineState = self.AddText("")
475 self.temperatureLabel = self.AddText("")
476 self.errorLogButton = self.AddButton(_("Show error log"))
477 self.errorLogButton.Show(False)
479 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
481 self.xMinStop = False
482 self.xMaxStop = False
483 self.yMinStop = False
484 self.yMaxStop = False
485 self.zMinStop = False
486 self.zMaxStop = False
488 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
491 if self.comm is not None:
495 self.endstopBitmap.Show(False)
498 def OnSkipClick(self, e):
499 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
500 self.GetParent().ShowPage(self.GetNext())
502 def OnCheckClick(self, e=None):
503 self.errorLogButton.Show(False)
504 if self.comm is not None:
508 wx.CallAfter(self.OnCheckClick)
510 self.infoBox.SetBusy(_("Connecting to machine."))
511 self.commState.SetBitmap(self.unknownBitmap)
512 self.tempState.SetBitmap(self.unknownBitmap)
513 self.stopState.SetBitmap(self.unknownBitmap)
514 self.checkupState = 0
515 self.checkExtruderNr = 0
516 self.comm = machineCom.MachineCom(callbackObject=self)
518 def OnErrorLog(self, e):
519 printWindow.LogWindow('\n'.join(self.comm.getLog()))
521 def mcLog(self, message):
524 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
525 if not self.comm.isOperational():
527 if self.checkupState == 0:
528 self.tempCheckTimeout = 20
529 if temp[self.checkExtruderNr] > 70:
530 self.checkupState = 1
531 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
532 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
533 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
535 self.startTemp = temp[self.checkExtruderNr]
536 self.checkupState = 2
537 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
538 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
539 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
540 elif self.checkupState == 1:
542 self.startTemp = temp[self.checkExtruderNr]
543 self.checkupState = 2
544 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
545 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
546 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
547 elif self.checkupState == 2:
548 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
549 if temp[self.checkExtruderNr] > self.startTemp + 40:
550 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
551 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
552 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
553 self.checkExtruderNr = 0
554 self.checkupState = 3
555 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
556 wx.CallAfter(self.endstopBitmap.Show, True)
557 wx.CallAfter(self.Layout)
558 self.comm.sendCommand('M119')
559 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
561 self.checkupState = 0
562 self.checkExtruderNr += 1
564 self.tempCheckTimeout -= 1
565 if self.tempCheckTimeout < 1:
566 self.checkupState = -1
567 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
568 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
569 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
570 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
571 elif self.checkupState >= 3 and self.checkupState < 10:
572 self.comm.sendCommand('M119')
573 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
575 def mcStateChange(self, state):
576 if self.comm is None:
578 if self.comm.isOperational():
579 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
580 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
581 elif self.comm.isError():
582 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
583 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
584 wx.CallAfter(self.endstopBitmap.Show, False)
585 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
586 wx.CallAfter(self.errorLogButton.Show, True)
587 wx.CallAfter(self.Layout)
589 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
591 def mcMessage(self, message):
592 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
593 for data in message.split(' '):
595 tag, value = data.split(':', 1)
597 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
599 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
601 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
603 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
605 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
607 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
609 tag, value = map(str.strip, message.split(':', 1))
611 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
613 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
615 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
617 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
619 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
621 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
622 if 'z_max' in message:
623 self.comm.sendCommand('M119')
625 if self.checkupState == 3:
626 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
627 self.checkupState = 4
628 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
629 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
630 elif self.checkupState == 4:
631 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
632 self.checkupState = 5
633 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
634 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
635 elif self.checkupState == 5:
636 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
637 self.checkupState = 6
638 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
639 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
640 elif self.checkupState == 6:
641 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
642 self.checkupState = 7
643 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
644 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
645 elif self.checkupState == 7:
646 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
647 self.checkupState = 8
648 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
649 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
650 elif self.checkupState == 8:
651 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
652 self.checkupState = 9
653 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
654 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
655 elif self.checkupState == 9:
656 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
657 self.checkupState = 10
659 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
660 wx.CallAfter(self.infoBox.SetReadyIndicator)
661 wx.CallAfter(self.endstopBitmap.Show, False)
662 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
663 wx.CallAfter(self.OnSkipClick, None)
665 def mcProgress(self, lineNr):
668 def mcZChange(self, newZ):
672 class UltimakerCalibrationPage(InfoPage):
673 def __init__(self, parent):
674 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
676 self.AddText("Your Ultimaker requires some calibration.")
677 self.AddText("This calibration is needed for a proper extrusion amount.")
679 self.AddText("The following values are needed:")
680 self.AddText("* Diameter of filament")
681 self.AddText("* Number of steps per mm of filament extrusion")
683 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
685 self.AddText("First we need the diameter of your filament:")
686 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
688 "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.")
689 self.AddText("Note: This value can be changed later at any time.")
692 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
695 class UltimakerCalibrateStepsPerEPage(InfoPage):
696 def __init__(self, parent):
697 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
699 #if profile.getMachineSetting('steps_per_e') == '0':
700 # profile.putMachineSetting('steps_per_e', '865.888')
702 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
703 self.AddText(_("First remove any filament from your machine."))
704 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
705 self.AddText(_("We'll push the filament 100mm"))
706 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
707 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
708 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
709 self.AddText(_("This results in the following steps per E:"))
710 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
711 self.AddText(_("You can repeat these steps to get better calibration."))
714 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
715 self.heatButton = self.AddButton(_("Heatup for filament removal"))
717 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
718 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
719 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
721 def OnSaveLengthClick(self, e):
722 currentEValue = float(self.stepsPerEInput.GetValue())
723 realExtrudeLength = float(self.lengthInput.GetValue())
724 newEValue = currentEValue * 100 / realExtrudeLength
725 self.stepsPerEInput.SetValue(str(newEValue))
726 self.lengthInput.SetValue("100")
728 def OnExtrudeClick(self, e):
729 threading.Thread(target=self.OnExtrudeRun).start()
731 def OnExtrudeRun(self):
732 self.heatButton.Enable(False)
733 self.extrudeButton.Enable(False)
734 currentEValue = float(self.stepsPerEInput.GetValue())
735 self.comm = machineCom.MachineCom()
736 if not self.comm.isOpen():
738 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
739 'Printer error', wx.OK | wx.ICON_INFORMATION)
740 self.heatButton.Enable(True)
741 self.extrudeButton.Enable(True)
744 line = self.comm.readline()
749 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
752 self.sendGCommand('M302') #Disable cold extrusion protection
753 self.sendGCommand("M92 E%f" % (currentEValue))
754 self.sendGCommand("G92 E0")
755 self.sendGCommand("G1 E100 F600")
758 self.extrudeButton.Enable()
759 self.heatButton.Enable()
761 def OnHeatClick(self, e):
762 threading.Thread(target=self.OnHeatRun).start()
765 self.heatButton.Enable(False)
766 self.extrudeButton.Enable(False)
767 self.comm = machineCom.MachineCom()
768 if not self.comm.isOpen():
770 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
771 'Printer error', wx.OK | wx.ICON_INFORMATION)
772 self.heatButton.Enable(True)
773 self.extrudeButton.Enable(True)
776 line = self.comm.readline()
778 self.heatButton.Enable(True)
779 self.extrudeButton.Enable(True)
783 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
786 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
788 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
789 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
790 self.sendGCommand('M104 S0')
793 self.heatButton.Enable(True)
794 self.extrudeButton.Enable(True)
796 def sendGCommand(self, cmd):
797 self.comm.sendCommand(cmd) #Disable cold extrusion protection
799 line = self.comm.readline()
802 if line.startswith('ok'):
806 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
808 class Ultimaker2ReadyPage(InfoPage):
809 def __init__(self, parent):
810 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
811 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
812 self.AddText('Cura is now ready to be used with your Ultimaker2.')
815 class configWizard(wx.wizard.Wizard):
816 def __init__(self, addNew = False):
817 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
819 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
820 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
822 self.firstInfoPage = FirstInfoPage(self, addNew)
823 self.machineSelectPage = MachineSelectPage(self)
824 self.ultimakerSelectParts = SelectParts(self)
825 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
826 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
827 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
828 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
829 self.bedLevelPage = bedLevelWizardMain(self)
830 self.headOffsetCalibration = headOffsetCalibrationPage(self)
831 self.otherMachineSelectPage = OtherMachineSelectPage(self)
832 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
833 self.otherMachineInfoPage = OtherMachineInfoPage(self)
835 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
837 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
838 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
839 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
840 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
841 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
842 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
843 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
844 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
846 self.FitToPage(self.firstInfoPage)
847 self.GetPageAreaSizer().Add(self.firstInfoPage)
849 self.RunWizard(self.firstInfoPage)
852 def OnPageChanging(self, e):
853 e.GetPage().StoreData()
855 def OnPageChanged(self, e):
856 if e.GetPage().AllowNext():
857 self.FindWindowById(wx.ID_FORWARD).Enable()
859 self.FindWindowById(wx.ID_FORWARD).Disable()
860 self.FindWindowById(wx.ID_BACKWARD).Disable()
862 class bedLevelWizardMain(InfoPage):
863 def __init__(self, parent):
864 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
866 self.AddText('This wizard will help you in leveling your printer bed')
868 self.AddText('It will do the following steps')
869 self.AddText('* Move the printer head to each corner')
870 self.AddText(' and let you adjust the height of the bed to the nozzle')
871 self.AddText('* Print a line around the bed to check if it is level')
874 self.connectButton = self.AddButton('Connect to printer')
877 self.infoBox = self.AddInfoBox()
878 self.resumeButton = self.AddButton('Resume')
879 self.resumeButton.Enable(False)
881 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
882 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
884 def OnConnect(self, e = None):
885 if self.comm is not None:
889 wx.CallAfter(self.OnConnect)
891 self.connectButton.Enable(False)
892 self.comm = machineCom.MachineCom(callbackObject=self)
893 self.infoBox.SetBusy('Connecting to machine.')
894 self._wizardState = 0
897 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
898 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
901 def OnResume(self, e):
902 feedZ = profile.getProfileSettingFloat('print_speed') * 60
903 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
904 if self._wizardState == 2:
905 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
906 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
907 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
908 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
909 self.comm.sendCommand('M400')
910 self._wizardState = 3
911 elif self._wizardState == 4:
912 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
913 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
914 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
915 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
916 self.comm.sendCommand('M400')
917 self._wizardState = 5
918 elif self._wizardState == 6:
919 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
920 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
921 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
922 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
923 self.comm.sendCommand('M400')
924 self._wizardState = 7
925 elif self._wizardState == 8:
926 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
927 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
928 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
929 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
930 self._wizardState = 9
931 elif self._wizardState == 10:
932 self._wizardState = 11
933 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
934 feedZ = profile.getProfileSettingFloat('print_speed') * 60
935 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
936 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
937 w = profile.getMachineSettingFloat('machine_width')
938 d = profile.getMachineSettingFloat('machine_depth')
939 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
940 filamentArea = math.pi * filamentRadius * filamentRadius
941 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
945 'G1 Z2 F%d' % (feedZ),
947 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
948 'G1 Z0.3 F%d' % (feedZ)]
950 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
952 for i in xrange(0, 3):
953 dist = 5.0 + 0.4 * float(i)
954 eValue += (d - 2.0*dist) * ePerMM
955 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
956 eValue += (w - 2.0*dist) * ePerMM
957 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
958 eValue += (d - 2.0*dist) * ePerMM
959 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
960 eValue += (w - 2.0*dist) * ePerMM
961 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
963 gcodeList.append('M400')
964 self.comm.printGCode(gcodeList)
965 self.resumeButton.Enable(False)
967 def mcLog(self, message):
968 print 'Log:', message
970 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
971 if self._wizardState == 1:
972 self._wizardState = 2
973 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
974 wx.CallAfter(self.resumeButton.Enable, True)
975 elif self._wizardState == 3:
976 self._wizardState = 4
977 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
978 wx.CallAfter(self.resumeButton.Enable, True)
979 elif self._wizardState == 5:
980 self._wizardState = 6
981 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
982 wx.CallAfter(self.resumeButton.Enable, True)
983 elif self._wizardState == 7:
984 self._wizardState = 8
985 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
986 wx.CallAfter(self.resumeButton.Enable, True)
987 elif self._wizardState == 9:
988 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
989 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
991 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
992 wx.CallAfter(self.resumeButton.Enable, True)
993 self._wizardState = 10
995 def mcStateChange(self, state):
996 if self.comm is None:
998 if self.comm.isOperational():
999 if self._wizardState == 0:
1000 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
1001 self.comm.sendCommand('M105')
1002 self.comm.sendCommand('G28')
1003 self._wizardState = 1
1004 elif self._wizardState == 11 and not self.comm.isPrinting():
1005 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1006 self.comm.sendCommand('G92 E0')
1007 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1008 self.comm.sendCommand('M104 S0')
1009 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
1010 wx.CallAfter(self.infoBox.SetReadyIndicator)
1011 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1012 wx.CallAfter(self.connectButton.Enable, True)
1013 self._wizardState = 12
1014 elif self.comm.isError():
1015 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1017 def mcMessage(self, message):
1020 def mcProgress(self, lineNr):
1023 def mcZChange(self, newZ):
1026 class headOffsetCalibrationPage(InfoPage):
1027 def __init__(self, parent):
1028 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1030 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
1033 self.connectButton = self.AddButton('Connect to printer')
1036 self.infoBox = self.AddInfoBox()
1037 self.textEntry = self.AddTextCtrl('')
1038 self.textEntry.Enable(False)
1039 self.resumeButton = self.AddButton('Resume')
1040 self.resumeButton.Enable(False)
1042 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1043 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1045 def OnConnect(self, e = None):
1046 if self.comm is not None:
1050 wx.CallAfter(self.OnConnect)
1052 self.connectButton.Enable(False)
1053 self.comm = machineCom.MachineCom(callbackObject=self)
1054 self.infoBox.SetBusy('Connecting to machine.')
1055 self._wizardState = 0
1057 def OnResume(self, e):
1058 if self._wizardState == 2:
1059 self._wizardState = 3
1060 wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1062 w = profile.getMachineSettingFloat('machine_width')
1063 d = profile.getMachineSettingFloat('machine_depth')
1065 gcode = gcodeGenerator.gcodeGenerator()
1066 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1067 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1074 gcode.addMove(w/2, 5)
1075 gcode.addMove(z=0.2)
1077 gcode.addExtrude(w/2, d-5.0)
1079 gcode.addMove(5, d/2)
1081 gcode.addExtrude(w-5.0, d/2)
1082 gcode.addRetract(15)
1085 gcode.addMove(w/2, 5)
1087 gcode.addExtrude(w/2, d-5.0)
1089 gcode.addMove(5, d/2)
1091 gcode.addExtrude(w-5.0, d/2)
1092 gcode.addRetract(15)
1097 gcode.addCmd('M400')
1099 self.comm.printGCode(gcode.list())
1100 self.resumeButton.Enable(False)
1101 elif self._wizardState == 4:
1103 float(self.textEntry.GetValue())
1106 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1107 self._wizardState = 5
1108 self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1109 self.textEntry.SetValue('0.0')
1110 self.textEntry.Enable(True)
1111 elif self._wizardState == 5:
1113 float(self.textEntry.GetValue())
1116 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1117 self._wizardState = 6
1118 self.infoBox.SetBusy('Printing the fine calibration lines.')
1119 self.textEntry.SetValue('')
1120 self.textEntry.Enable(False)
1121 self.resumeButton.Enable(False)
1123 x = profile.getMachineSettingFloat('extruder_offset_x1')
1124 y = profile.getMachineSettingFloat('extruder_offset_y1')
1125 gcode = gcodeGenerator.gcodeGenerator()
1126 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1127 gcode.setPrintSpeed(25)
1130 gcode.addMove(50, 40, 0.2)
1132 for n in xrange(0, 10):
1133 gcode.addExtrude(50 + n * 10, 150)
1134 gcode.addExtrude(50 + n * 10 + 5, 150)
1135 gcode.addExtrude(50 + n * 10 + 5, 40)
1136 gcode.addExtrude(50 + n * 10 + 10, 40)
1137 gcode.addMove(40, 50)
1138 for n in xrange(0, 10):
1139 gcode.addExtrude(150, 50 + n * 10)
1140 gcode.addExtrude(150, 50 + n * 10 + 5)
1141 gcode.addExtrude(40, 50 + n * 10 + 5)
1142 gcode.addExtrude(40, 50 + n * 10 + 10)
1143 gcode.addRetract(15)
1146 gcode.addMove(50 - x, 30 - y, 0.2)
1148 for n in xrange(0, 10):
1149 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1150 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1151 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1152 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1153 gcode.addMove(30 - x, 50 - y, 0.2)
1154 for n in xrange(0, 10):
1155 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1156 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1157 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1158 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1159 gcode.addRetract(15)
1161 gcode.addCmd('M400')
1162 gcode.addCmd('M104 T0 S0')
1163 gcode.addCmd('M104 T1 S0')
1164 self.comm.printGCode(gcode.list())
1165 elif self._wizardState == 7:
1167 n = int(self.textEntry.GetValue()) - 1
1170 x = profile.getMachineSettingFloat('extruder_offset_x1')
1172 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1173 self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1174 self.textEntry.SetValue('10')
1175 self._wizardState = 8
1176 elif self._wizardState == 8:
1178 n = int(self.textEntry.GetValue()) - 1
1181 y = profile.getMachineSettingFloat('extruder_offset_y1')
1183 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1184 self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1185 self.infoBox.SetReadyIndicator()
1186 self._wizardState = 8
1188 self.resumeButton.Enable(False)
1190 def mcLog(self, message):
1191 print 'Log:', message
1193 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1194 if self._wizardState == 1:
1195 if temp[0] >= 210 and temp[1] >= 210:
1196 self._wizardState = 2
1197 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1198 wx.CallAfter(self.resumeButton.Enable, True)
1199 wx.CallAfter(self.resumeButton.SetFocus)
1201 def mcStateChange(self, state):
1202 if self.comm is None:
1204 if self.comm.isOperational():
1205 if self._wizardState == 0:
1206 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1207 self.comm.sendCommand('M105')
1208 self.comm.sendCommand('M104 S220 T0')
1209 self.comm.sendCommand('M104 S220 T1')
1210 self.comm.sendCommand('G28')
1211 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1212 self._wizardState = 1
1213 if not self.comm.isPrinting():
1214 if self._wizardState == 3:
1215 self._wizardState = 4
1216 wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1217 wx.CallAfter(self.textEntry.SetValue, '0.0')
1218 wx.CallAfter(self.textEntry.Enable, True)
1219 wx.CallAfter(self.resumeButton.Enable, True)
1220 wx.CallAfter(self.resumeButton.SetFocus)
1221 elif self._wizardState == 6:
1222 self._wizardState = 7
1223 wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1224 wx.CallAfter(self.textEntry.SetValue, '10')
1225 wx.CallAfter(self.textEntry.Enable, True)
1226 wx.CallAfter(self.resumeButton.Enable, True)
1227 wx.CallAfter(self.resumeButton.SetFocus)
1229 elif self.comm.isError():
1230 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1232 def mcMessage(self, message):
1235 def mcProgress(self, lineNr):
1238 def mcZChange(self, newZ):
1241 class bedLevelWizard(wx.wizard.Wizard):
1243 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1245 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1246 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1248 self.mainPage = bedLevelWizardMain(self)
1249 self.headOffsetCalibration = None
1251 self.FitToPage(self.mainPage)
1252 self.GetPageAreaSizer().Add(self.mainPage)
1254 self.RunWizard(self.mainPage)
1257 def OnPageChanging(self, e):
1258 e.GetPage().StoreData()
1260 def OnPageChanged(self, e):
1261 if e.GetPage().AllowNext():
1262 self.FindWindowById(wx.ID_FORWARD).Enable()
1264 self.FindWindowById(wx.ID_FORWARD).Disable()
1265 self.FindWindowById(wx.ID_BACKWARD).Disable()
1267 class headOffsetWizard(wx.wizard.Wizard):
1269 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1271 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1272 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1274 self.mainPage = headOffsetCalibrationPage(self)
1276 self.FitToPage(self.mainPage)
1277 self.GetPageAreaSizer().Add(self.mainPage)
1279 self.RunWizard(self.mainPage)
1282 def OnPageChanging(self, e):
1283 e.GetPage().StoreData()
1285 def OnPageChanged(self, e):
1286 if e.GetPage().AllowNext():
1287 self.FindWindowById(wx.ID_FORWARD).Enable()
1289 self.FindWindowById(wx.ID_FORWARD).Disable()
1290 self.FindWindowById(wx.ID_BACKWARD).Disable()