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")
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 elif self.UltimakerRadio.GetValue():
357 profile.putMachineSetting('machine_width', '205')
358 profile.putMachineSetting('machine_depth', '205')
359 profile.putMachineSetting('machine_height', '200')
360 profile.putMachineSetting('machine_name', 'ultimaker')
361 profile.putMachineSetting('machine_type', 'ultimaker')
362 profile.putMachineSetting('machine_center_is_zero', 'False')
363 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
364 profile.putProfileSetting('nozzle_size', '0.4')
365 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
366 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
367 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
368 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
369 profile.putMachineSetting('extruder_head_size_height', '60.0')
371 profile.putMachineSetting('machine_width', '80')
372 profile.putMachineSetting('machine_depth', '80')
373 profile.putMachineSetting('machine_height', '60')
374 profile.putMachineSetting('machine_name', 'reprap')
375 profile.putMachineSetting('machine_type', 'reprap')
376 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
377 profile.putPreference('startMode', 'Normal')
378 profile.putProfileSetting('nozzle_size', '0.5')
379 profile.checkAndUpdateMachineName()
380 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
381 if self.SubmitUserStats.GetValue():
382 profile.putPreference('submit_slice_information', 'True')
384 profile.putPreference('submit_slice_information', 'False')
387 class SelectParts(InfoPage):
388 def __init__(self, parent):
389 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
390 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."))
392 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
393 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
394 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
396 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."))
397 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
398 self.springExtruder.SetValue(True)
401 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
402 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
403 if self.dualExtrusion.GetValue():
404 profile.putMachineSetting('extruder_amount', '2')
405 profile.putMachineSetting('machine_depth', '195')
407 profile.putMachineSetting('extruder_amount', '1')
408 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
409 profile.putProfileSetting('retraction_enable', 'True')
411 profile.putProfileSetting('retraction_enable', 'False')
414 class UltimakerFirmwareUpgradePage(InfoPage):
415 def __init__(self, parent):
416 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
417 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."))
418 self.AddHiddenSeperator()
419 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
420 self.AddHiddenSeperator()
421 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."))
422 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
423 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
424 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
425 self.AddHiddenSeperator()
426 self.AddText(_("Do not upgrade to this firmware if:"))
427 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
428 self.AddText(_("* Have other changes in the firmware"))
429 # button = self.AddButton('Goto this page for a custom firmware')
430 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
435 def OnUpgradeClick(self, e):
436 if firmwareInstall.InstallFirmware():
437 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
439 def OnSkipClick(self, e):
440 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
441 self.GetParent().ShowPage(self.GetNext())
443 def OnUrlClick(self, e):
444 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
446 class UltimakerCheckupPage(InfoPage):
447 def __init__(self, parent):
448 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
450 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
451 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
452 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
453 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
454 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
455 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
456 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
457 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
458 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
459 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
462 _("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."))
463 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
464 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
465 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
467 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
468 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
469 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
471 self.infoBox = self.AddInfoBox()
472 self.machineState = self.AddText("")
473 self.temperatureLabel = self.AddText("")
474 self.errorLogButton = self.AddButton(_("Show error log"))
475 self.errorLogButton.Show(False)
477 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
479 self.xMinStop = False
480 self.xMaxStop = False
481 self.yMinStop = False
482 self.yMaxStop = False
483 self.zMinStop = False
484 self.zMaxStop = False
486 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
489 if self.comm is not None:
493 self.endstopBitmap.Show(False)
496 def OnSkipClick(self, e):
497 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
498 self.GetParent().ShowPage(self.GetNext())
500 def OnCheckClick(self, e=None):
501 self.errorLogButton.Show(False)
502 if self.comm is not None:
506 wx.CallAfter(self.OnCheckClick)
508 self.infoBox.SetBusy(_("Connecting to machine."))
509 self.commState.SetBitmap(self.unknownBitmap)
510 self.tempState.SetBitmap(self.unknownBitmap)
511 self.stopState.SetBitmap(self.unknownBitmap)
512 self.checkupState = 0
513 self.checkExtruderNr = 0
514 self.comm = machineCom.MachineCom(callbackObject=self)
516 def OnErrorLog(self, e):
517 printWindow.LogWindow('\n'.join(self.comm.getLog()))
519 def mcLog(self, message):
522 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
523 if not self.comm.isOperational():
525 if self.checkupState == 0:
526 self.tempCheckTimeout = 20
527 if temp[self.checkExtruderNr] > 70:
528 self.checkupState = 1
529 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
530 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
531 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
533 self.startTemp = temp[self.checkExtruderNr]
534 self.checkupState = 2
535 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
536 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
537 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
538 elif self.checkupState == 1:
540 self.startTemp = temp[self.checkExtruderNr]
541 self.checkupState = 2
542 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
543 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
544 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
545 elif self.checkupState == 2:
546 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
547 if temp[self.checkExtruderNr] > self.startTemp + 40:
548 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
549 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
550 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
551 self.checkExtruderNr = 0
552 self.checkupState = 3
553 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
554 wx.CallAfter(self.endstopBitmap.Show, True)
555 wx.CallAfter(self.Layout)
556 self.comm.sendCommand('M119')
557 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
559 self.checkupState = 0
560 self.checkExtruderNr += 1
562 self.tempCheckTimeout -= 1
563 if self.tempCheckTimeout < 1:
564 self.checkupState = -1
565 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
566 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
567 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
568 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
569 elif self.checkupState >= 3 and self.checkupState < 10:
570 self.comm.sendCommand('M119')
571 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
573 def mcStateChange(self, state):
574 if self.comm is None:
576 if self.comm.isOperational():
577 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
578 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
579 elif self.comm.isError():
580 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
581 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
582 wx.CallAfter(self.endstopBitmap.Show, False)
583 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
584 wx.CallAfter(self.errorLogButton.Show, True)
585 wx.CallAfter(self.Layout)
587 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
589 def mcMessage(self, message):
590 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
591 for data in message.split(' '):
593 tag, value = data.split(':', 1)
595 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
597 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
599 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
601 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
603 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
605 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
607 tag, value = map(str.strip, message.split(':', 1))
609 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
611 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
613 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
615 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
617 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
619 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
620 if 'z_max' in message:
621 self.comm.sendCommand('M119')
623 if self.checkupState == 3:
624 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
625 self.checkupState = 4
626 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
627 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
628 elif self.checkupState == 4:
629 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
630 self.checkupState = 5
631 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
632 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
633 elif self.checkupState == 5:
634 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
635 self.checkupState = 6
636 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
637 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
638 elif self.checkupState == 6:
639 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
640 self.checkupState = 7
641 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
642 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
643 elif self.checkupState == 7:
644 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
645 self.checkupState = 8
646 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
647 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
648 elif self.checkupState == 8:
649 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
650 self.checkupState = 9
651 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
652 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
653 elif self.checkupState == 9:
654 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
655 self.checkupState = 10
657 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
658 wx.CallAfter(self.infoBox.SetReadyIndicator)
659 wx.CallAfter(self.endstopBitmap.Show, False)
660 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
661 wx.CallAfter(self.OnSkipClick, None)
663 def mcProgress(self, lineNr):
666 def mcZChange(self, newZ):
670 class UltimakerCalibrationPage(InfoPage):
671 def __init__(self, parent):
672 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
674 self.AddText("Your Ultimaker requires some calibration.")
675 self.AddText("This calibration is needed for a proper extrusion amount.")
677 self.AddText("The following values are needed:")
678 self.AddText("* Diameter of filament")
679 self.AddText("* Number of steps per mm of filament extrusion")
681 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
683 self.AddText("First we need the diameter of your filament:")
684 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
686 "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.")
687 self.AddText("Note: This value can be changed later at any time.")
690 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
693 class UltimakerCalibrateStepsPerEPage(InfoPage):
694 def __init__(self, parent):
695 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
697 #if profile.getMachineSetting('steps_per_e') == '0':
698 # profile.putMachineSetting('steps_per_e', '865.888')
700 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
701 self.AddText(_("First remove any filament from your machine."))
702 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
703 self.AddText(_("We'll push the filament 100mm"))
704 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
705 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
706 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
707 self.AddText(_("This results in the following steps per E:"))
708 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
709 self.AddText(_("You can repeat these steps to get better calibration."))
712 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
713 self.heatButton = self.AddButton(_("Heatup for filament removal"))
715 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
716 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
717 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
719 def OnSaveLengthClick(self, e):
720 currentEValue = float(self.stepsPerEInput.GetValue())
721 realExtrudeLength = float(self.lengthInput.GetValue())
722 newEValue = currentEValue * 100 / realExtrudeLength
723 self.stepsPerEInput.SetValue(str(newEValue))
724 self.lengthInput.SetValue("100")
726 def OnExtrudeClick(self, e):
727 threading.Thread(target=self.OnExtrudeRun).start()
729 def OnExtrudeRun(self):
730 self.heatButton.Enable(False)
731 self.extrudeButton.Enable(False)
732 currentEValue = float(self.stepsPerEInput.GetValue())
733 self.comm = machineCom.MachineCom()
734 if not self.comm.isOpen():
736 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
737 'Printer error', wx.OK | wx.ICON_INFORMATION)
738 self.heatButton.Enable(True)
739 self.extrudeButton.Enable(True)
742 line = self.comm.readline()
747 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
750 self.sendGCommand('M302') #Disable cold extrusion protection
751 self.sendGCommand("M92 E%f" % (currentEValue))
752 self.sendGCommand("G92 E0")
753 self.sendGCommand("G1 E100 F600")
756 self.extrudeButton.Enable()
757 self.heatButton.Enable()
759 def OnHeatClick(self, e):
760 threading.Thread(target=self.OnHeatRun).start()
763 self.heatButton.Enable(False)
764 self.extrudeButton.Enable(False)
765 self.comm = machineCom.MachineCom()
766 if not self.comm.isOpen():
768 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
769 'Printer error', wx.OK | wx.ICON_INFORMATION)
770 self.heatButton.Enable(True)
771 self.extrudeButton.Enable(True)
774 line = self.comm.readline()
776 self.heatButton.Enable(True)
777 self.extrudeButton.Enable(True)
781 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
784 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
786 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
787 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
788 self.sendGCommand('M104 S0')
791 self.heatButton.Enable(True)
792 self.extrudeButton.Enable(True)
794 def sendGCommand(self, cmd):
795 self.comm.sendCommand(cmd) #Disable cold extrusion protection
797 line = self.comm.readline()
800 if line.startswith('ok'):
804 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
806 class Ultimaker2ReadyPage(InfoPage):
807 def __init__(self, parent):
808 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
809 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
810 self.AddText('Cura is now ready to be used with your Ultimaker2.')
813 class configWizard(wx.wizard.Wizard):
814 def __init__(self, addNew = False):
815 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
817 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
818 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
820 self.firstInfoPage = FirstInfoPage(self, addNew)
821 self.machineSelectPage = MachineSelectPage(self)
822 self.ultimakerSelectParts = SelectParts(self)
823 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
824 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
825 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
826 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
827 self.bedLevelPage = bedLevelWizardMain(self)
828 self.headOffsetCalibration = headOffsetCalibrationPage(self)
829 self.otherMachineSelectPage = OtherMachineSelectPage(self)
830 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
831 self.otherMachineInfoPage = OtherMachineInfoPage(self)
833 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
835 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
836 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
837 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
838 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
839 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
840 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
841 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
842 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
844 self.FitToPage(self.firstInfoPage)
845 self.GetPageAreaSizer().Add(self.firstInfoPage)
847 self.RunWizard(self.firstInfoPage)
850 def OnPageChanging(self, e):
851 e.GetPage().StoreData()
853 def OnPageChanged(self, e):
854 if e.GetPage().AllowNext():
855 self.FindWindowById(wx.ID_FORWARD).Enable()
857 self.FindWindowById(wx.ID_FORWARD).Disable()
858 self.FindWindowById(wx.ID_BACKWARD).Disable()
860 class bedLevelWizardMain(InfoPage):
861 def __init__(self, parent):
862 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
864 self.AddText('This wizard will help you in leveling your printer bed')
866 self.AddText('It will do the following steps')
867 self.AddText('* Move the printer head to each corner')
868 self.AddText(' and let you adjust the height of the bed to the nozzle')
869 self.AddText('* Print a line around the bed to check if it is level')
872 self.connectButton = self.AddButton('Connect to printer')
875 self.infoBox = self.AddInfoBox()
876 self.resumeButton = self.AddButton('Resume')
877 self.resumeButton.Enable(False)
879 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
880 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
882 def OnConnect(self, e = None):
883 if self.comm is not None:
887 wx.CallAfter(self.OnConnect)
889 self.connectButton.Enable(False)
890 self.comm = machineCom.MachineCom(callbackObject=self)
891 self.infoBox.SetBusy('Connecting to machine.')
892 self._wizardState = 0
895 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
896 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
899 def OnResume(self, e):
900 feedZ = profile.getProfileSettingFloat('print_speed') * 60
901 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
902 if self._wizardState == 2:
903 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
904 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
905 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
906 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
907 self.comm.sendCommand('M400')
908 self._wizardState = 3
909 elif self._wizardState == 4:
910 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
911 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
912 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
913 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
914 self.comm.sendCommand('M400')
915 self._wizardState = 5
916 elif self._wizardState == 6:
917 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
918 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
919 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
920 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
921 self.comm.sendCommand('M400')
922 self._wizardState = 7
923 elif self._wizardState == 8:
924 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
925 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
926 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
927 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
928 self._wizardState = 9
929 elif self._wizardState == 10:
930 self._wizardState = 11
931 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
932 feedZ = profile.getProfileSettingFloat('print_speed') * 60
933 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
934 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
935 w = profile.getMachineSettingFloat('machine_width')
936 d = profile.getMachineSettingFloat('machine_depth')
937 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
938 filamentArea = math.pi * filamentRadius * filamentRadius
939 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
943 'G1 Z2 F%d' % (feedZ),
945 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
946 'G1 Z0.3 F%d' % (feedZ)]
948 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
950 for i in xrange(0, 3):
951 dist = 5.0 + 0.4 * float(i)
952 eValue += (d - 2.0*dist) * ePerMM
953 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
954 eValue += (w - 2.0*dist) * ePerMM
955 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
956 eValue += (d - 2.0*dist) * ePerMM
957 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
958 eValue += (w - 2.0*dist) * ePerMM
959 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
961 gcodeList.append('M400')
962 self.comm.printGCode(gcodeList)
963 self.resumeButton.Enable(False)
965 def mcLog(self, message):
966 print 'Log:', message
968 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
969 if self._wizardState == 1:
970 self._wizardState = 2
971 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
972 wx.CallAfter(self.resumeButton.Enable, True)
973 elif self._wizardState == 3:
974 self._wizardState = 4
975 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
976 wx.CallAfter(self.resumeButton.Enable, True)
977 elif self._wizardState == 5:
978 self._wizardState = 6
979 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
980 wx.CallAfter(self.resumeButton.Enable, True)
981 elif self._wizardState == 7:
982 self._wizardState = 8
983 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
984 wx.CallAfter(self.resumeButton.Enable, True)
985 elif self._wizardState == 9:
986 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
987 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
989 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
990 wx.CallAfter(self.resumeButton.Enable, True)
991 self._wizardState = 10
993 def mcStateChange(self, state):
994 if self.comm is None:
996 if self.comm.isOperational():
997 if self._wizardState == 0:
998 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
999 self.comm.sendCommand('M105')
1000 self.comm.sendCommand('G28')
1001 self._wizardState = 1
1002 elif self._wizardState == 11 and not self.comm.isPrinting():
1003 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1004 self.comm.sendCommand('G92 E0')
1005 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1006 self.comm.sendCommand('M104 S0')
1007 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
1008 wx.CallAfter(self.infoBox.SetReadyIndicator)
1009 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1010 wx.CallAfter(self.connectButton.Enable, True)
1011 self._wizardState = 12
1012 elif self.comm.isError():
1013 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1015 def mcMessage(self, message):
1018 def mcProgress(self, lineNr):
1021 def mcZChange(self, newZ):
1024 class headOffsetCalibrationPage(InfoPage):
1025 def __init__(self, parent):
1026 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1028 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
1031 self.connectButton = self.AddButton('Connect to printer')
1034 self.infoBox = self.AddInfoBox()
1035 self.textEntry = self.AddTextCtrl('')
1036 self.textEntry.Enable(False)
1037 self.resumeButton = self.AddButton('Resume')
1038 self.resumeButton.Enable(False)
1040 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1041 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1043 def OnConnect(self, e = None):
1044 if self.comm is not None:
1048 wx.CallAfter(self.OnConnect)
1050 self.connectButton.Enable(False)
1051 self.comm = machineCom.MachineCom(callbackObject=self)
1052 self.infoBox.SetBusy('Connecting to machine.')
1053 self._wizardState = 0
1055 def OnResume(self, e):
1056 if self._wizardState == 2:
1057 self._wizardState = 3
1058 wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1060 w = profile.getMachineSettingFloat('machine_width')
1061 d = profile.getMachineSettingFloat('machine_depth')
1063 gcode = gcodeGenerator.gcodeGenerator()
1064 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1065 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1072 gcode.addMove(w/2, 5)
1073 gcode.addMove(z=0.2)
1075 gcode.addExtrude(w/2, d-5.0)
1077 gcode.addMove(5, d/2)
1079 gcode.addExtrude(w-5.0, d/2)
1080 gcode.addRetract(15)
1083 gcode.addMove(w/2, 5)
1085 gcode.addExtrude(w/2, d-5.0)
1087 gcode.addMove(5, d/2)
1089 gcode.addExtrude(w-5.0, d/2)
1090 gcode.addRetract(15)
1095 gcode.addCmd('M400')
1097 self.comm.printGCode(gcode.list())
1098 self.resumeButton.Enable(False)
1099 elif self._wizardState == 4:
1101 float(self.textEntry.GetValue())
1104 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1105 self._wizardState = 5
1106 self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1107 self.textEntry.SetValue('0.0')
1108 self.textEntry.Enable(True)
1109 elif self._wizardState == 5:
1111 float(self.textEntry.GetValue())
1114 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1115 self._wizardState = 6
1116 self.infoBox.SetBusy('Printing the fine calibration lines.')
1117 self.textEntry.SetValue('')
1118 self.textEntry.Enable(False)
1119 self.resumeButton.Enable(False)
1121 x = profile.getMachineSettingFloat('extruder_offset_x1')
1122 y = profile.getMachineSettingFloat('extruder_offset_y1')
1123 gcode = gcodeGenerator.gcodeGenerator()
1124 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1125 gcode.setPrintSpeed(25)
1128 gcode.addMove(50, 40, 0.2)
1130 for n in xrange(0, 10):
1131 gcode.addExtrude(50 + n * 10, 150)
1132 gcode.addExtrude(50 + n * 10 + 5, 150)
1133 gcode.addExtrude(50 + n * 10 + 5, 40)
1134 gcode.addExtrude(50 + n * 10 + 10, 40)
1135 gcode.addMove(40, 50)
1136 for n in xrange(0, 10):
1137 gcode.addExtrude(150, 50 + n * 10)
1138 gcode.addExtrude(150, 50 + n * 10 + 5)
1139 gcode.addExtrude(40, 50 + n * 10 + 5)
1140 gcode.addExtrude(40, 50 + n * 10 + 10)
1141 gcode.addRetract(15)
1144 gcode.addMove(50 - x, 30 - y, 0.2)
1146 for n in xrange(0, 10):
1147 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1148 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1149 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1150 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1151 gcode.addMove(30 - x, 50 - y, 0.2)
1152 for n in xrange(0, 10):
1153 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1154 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1155 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1156 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1157 gcode.addRetract(15)
1159 gcode.addCmd('M400')
1160 gcode.addCmd('M104 T0 S0')
1161 gcode.addCmd('M104 T1 S0')
1162 self.comm.printGCode(gcode.list())
1163 elif self._wizardState == 7:
1165 n = int(self.textEntry.GetValue()) - 1
1168 x = profile.getMachineSettingFloat('extruder_offset_x1')
1170 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1171 self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1172 self.textEntry.SetValue('10')
1173 self._wizardState = 8
1174 elif self._wizardState == 8:
1176 n = int(self.textEntry.GetValue()) - 1
1179 y = profile.getMachineSettingFloat('extruder_offset_y1')
1181 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1182 self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1183 self.infoBox.SetReadyIndicator()
1184 self._wizardState = 8
1186 self.resumeButton.Enable(False)
1188 def mcLog(self, message):
1189 print 'Log:', message
1191 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1192 if self._wizardState == 1:
1193 if temp[0] >= 210 and temp[1] >= 210:
1194 self._wizardState = 2
1195 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1196 wx.CallAfter(self.resumeButton.Enable, True)
1197 wx.CallAfter(self.resumeButton.SetFocus)
1199 def mcStateChange(self, state):
1200 if self.comm is None:
1202 if self.comm.isOperational():
1203 if self._wizardState == 0:
1204 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1205 self.comm.sendCommand('M105')
1206 self.comm.sendCommand('M104 S220 T0')
1207 self.comm.sendCommand('M104 S220 T1')
1208 self.comm.sendCommand('G28')
1209 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1210 self._wizardState = 1
1211 if not self.comm.isPrinting():
1212 if self._wizardState == 3:
1213 self._wizardState = 4
1214 wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1215 wx.CallAfter(self.textEntry.SetValue, '0.0')
1216 wx.CallAfter(self.textEntry.Enable, True)
1217 wx.CallAfter(self.resumeButton.Enable, True)
1218 wx.CallAfter(self.resumeButton.SetFocus)
1219 elif self._wizardState == 6:
1220 self._wizardState = 7
1221 wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1222 wx.CallAfter(self.textEntry.SetValue, '10')
1223 wx.CallAfter(self.textEntry.Enable, True)
1224 wx.CallAfter(self.resumeButton.Enable, True)
1225 wx.CallAfter(self.resumeButton.SetFocus)
1227 elif self.comm.isError():
1228 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1230 def mcMessage(self, message):
1233 def mcProgress(self, lineNr):
1236 def mcZChange(self, newZ):
1239 class bedLevelWizard(wx.wizard.Wizard):
1241 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1243 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1244 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1246 self.mainPage = bedLevelWizardMain(self)
1247 self.headOffsetCalibration = None
1249 self.FitToPage(self.mainPage)
1250 self.GetPageAreaSizer().Add(self.mainPage)
1252 self.RunWizard(self.mainPage)
1255 def OnPageChanging(self, e):
1256 e.GetPage().StoreData()
1258 def OnPageChanged(self, e):
1259 if e.GetPage().AllowNext():
1260 self.FindWindowById(wx.ID_FORWARD).Enable()
1262 self.FindWindowById(wx.ID_FORWARD).Disable()
1263 self.FindWindowById(wx.ID_BACKWARD).Disable()
1265 class headOffsetWizard(wx.wizard.Wizard):
1267 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1269 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1270 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1272 self.mainPage = headOffsetCalibrationPage(self)
1274 self.FitToPage(self.mainPage)
1275 self.GetPageAreaSizer().Add(self.mainPage)
1277 self.RunWizard(self.mainPage)
1280 def OnPageChanging(self, e):
1281 e.GetPage().StoreData()
1283 def OnPageChanged(self, e):
1284 if e.GetPage().AllowNext():
1285 self.FindWindowById(wx.ID_FORWARD).Enable()
1287 self.FindWindowById(wx.ID_FORWARD).Disable()
1288 self.FindWindowById(wx.ID_BACKWARD).Disable()