1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
12 from Cura.gui import firmwareInstall
13 from Cura.gui import printWindow
14 from Cura.util import machineCom
15 from Cura.util import profile
16 from Cura.util import gcodeGenerator
17 from Cura.util import resources
20 class InfoBox(wx.Panel):
21 def __init__(self, parent):
22 super(InfoBox, self).__init__(parent)
23 self.SetBackgroundColour('#FFFF80')
25 self.sizer = wx.GridBagSizer(5, 5)
26 self.SetSizer(self.sizer)
28 self.attentionBitmap = wx.Bitmap(resources.getPathForImage('attention.png'))
29 self.errorBitmap = wx.Bitmap(resources.getPathForImage('error.png'))
30 self.readyBitmap = wx.Bitmap(resources.getPathForImage('ready.png'))
32 wx.Bitmap(resources.getPathForImage('busy-0.png')),
33 wx.Bitmap(resources.getPathForImage('busy-1.png')),
34 wx.Bitmap(resources.getPathForImage('busy-2.png')),
35 wx.Bitmap(resources.getPathForImage('busy-3.png'))
38 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
39 self.text = wx.StaticText(self, -1, '')
40 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
41 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
42 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
43 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
44 self.sizer.AddGrowableCol(1)
46 self.extraInfoButton.Show(False)
48 self.extraInfoUrl = ''
50 self.timer = wx.Timer(self)
51 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
52 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
55 def SetInfo(self, info):
56 self.SetBackgroundColour('#FFFF80')
57 self.text.SetLabel(info)
58 self.extraInfoButton.Show(False)
61 def SetError(self, info, extraInfoUrl):
62 self.extraInfoUrl = extraInfoUrl
63 self.SetBackgroundColour('#FF8080')
64 self.text.SetLabel(info)
65 self.extraInfoButton.Show(True)
67 self.SetErrorIndicator()
70 def SetAttention(self, info):
71 self.SetBackgroundColour('#FFFF80')
72 self.text.SetLabel(info)
73 self.extraInfoButton.Show(False)
74 self.SetAttentionIndicator()
78 def SetBusy(self, info):
80 self.SetBusyIndicator()
82 def SetBusyIndicator(self):
84 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
86 def doExtraInfo(self, e):
87 webbrowser.open(self.extraInfoUrl)
89 def doBusyUpdate(self, e):
90 if self.busyState is None:
93 if self.busyState >= len(self.busyBitmap):
95 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
97 def SetReadyIndicator(self):
99 self.bitmap.SetBitmap(self.readyBitmap)
101 def SetErrorIndicator(self):
102 self.busyState = None
103 self.bitmap.SetBitmap(self.errorBitmap)
105 def SetAttentionIndicator(self):
106 self.busyState = None
107 self.bitmap.SetBitmap(self.attentionBitmap)
110 class InfoPage(wx.wizard.WizardPageSimple):
111 def __init__(self, parent, title):
112 wx.wizard.WizardPageSimple.__init__(self, parent)
114 sizer = wx.GridBagSizer(5, 5)
118 title = wx.StaticText(self, -1, title)
119 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
120 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
121 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
122 sizer.AddGrowableCol(1)
126 def AddText(self, info):
127 text = wx.StaticText(self, -1, info)
128 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
132 def AddSeperator(self):
133 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
136 def AddHiddenSeperator(self):
139 def AddInfoBox(self):
140 infoBox = InfoBox(self)
141 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
145 def AddRadioButton(self, label, style=0):
146 radio = wx.RadioButton(self, -1, label, style=style)
147 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
151 def AddCheckbox(self, label, checked=False):
152 check = wx.CheckBox(self, -1)
153 text = wx.StaticText(self, -1, label)
154 check.SetValue(checked)
155 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
156 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
160 def AddButton(self, label):
161 button = wx.Button(self, -1, label)
162 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
166 def AddDualButton(self, label1, label2):
167 button1 = wx.Button(self, -1, label1)
168 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
169 button2 = wx.Button(self, -1, label2)
170 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
172 return button1, button2
174 def AddTextCtrl(self, value):
175 ret = wx.TextCtrl(self, -1, value)
176 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
180 def AddLabelTextCtrl(self, info, value):
181 text = wx.StaticText(self, -1, info)
182 ret = wx.TextCtrl(self, -1, value)
183 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
184 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
188 def AddTextCtrlButton(self, value, buttonText):
189 text = wx.TextCtrl(self, -1, value)
190 button = wx.Button(self, -1, buttonText)
191 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
192 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
196 def AddBitmap(self, bitmap):
197 bitmap = wx.StaticBitmap(self, -1, bitmap)
198 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
202 def AddCheckmark(self, label, bitmap):
203 check = wx.StaticBitmap(self, -1, bitmap)
204 text = wx.StaticText(self, -1, label)
205 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
206 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
220 class FirstInfoPage(InfoPage):
221 def __init__(self, parent, addNew):
223 super(FirstInfoPage, self).__init__(parent, _("Add new machine wizard"))
225 super(FirstInfoPage, self).__init__(parent, _("First time run wizard"))
226 self.AddText(_("Welcome, and thanks for trying Cura!"))
228 self.AddText(_("This wizard will help you in setting up Cura for your machine."))
229 # self.AddText(_("This wizard will help you with the following steps:"))
230 # self.AddText(_("* Configure Cura for your machine"))
231 # self.AddText(_("* Optionally upgrade your firmware"))
232 # self.AddText(_("* Optionally check if your machine is working safely"))
233 # self.AddText(_("* Optionally level your printer bed"))
235 #self.AddText('* Calibrate your machine')
236 #self.AddText('* Do your first print')
242 class OtherMachineSelectPage(InfoPage):
243 def __init__(self, parent):
244 super(OtherMachineSelectPage, self).__init__(parent, "Other machine information")
245 self.AddText(_("The following pre-defined machine profiles are available"))
246 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."))
248 machines = resources.getDefaultMachineProfiles()
250 for filename in machines:
251 name = os.path.splitext(os.path.basename(filename))[0]
252 item = self.AddRadioButton(name)
253 item.filename = filename
254 item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
255 self.options.append(item)
257 item = self.AddRadioButton('Custom...')
259 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
261 def OnProfileSelect(self, e):
262 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
264 def OnOtherSelect(self, e):
265 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
268 for option in self.options:
269 if option.GetValue():
270 profile.loadProfile(option.filename)
271 profile.loadMachineSettings(option.filename)
273 class OtherMachineInfoPage(InfoPage):
274 def __init__(self, parent):
275 super(OtherMachineInfoPage, self).__init__(parent, "Cura Ready!")
276 self.AddText(_("Cura is now ready to be used!"))
278 class CustomRepRapInfoPage(InfoPage):
279 def __init__(self, parent):
280 super(CustomRepRapInfoPage, self).__init__(parent, "Custom RepRap information")
281 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
282 self.AddText(_("Be sure to review the default profile before running it on your machine."))
283 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
285 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
287 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
288 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
289 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
290 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "55")
291 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
292 self.heatedBed = self.AddCheckbox(_("Heated bed"))
293 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
296 profile.putMachineSetting('machine_name', self.machineName.GetValue())
297 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
298 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
299 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
300 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
301 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
302 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
303 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
304 profile.putMachineSetting('extruder_head_size_min_x', '0')
305 profile.putMachineSetting('extruder_head_size_min_y', '0')
306 profile.putMachineSetting('extruder_head_size_max_x', '0')
307 profile.putMachineSetting('extruder_head_size_max_y', '0')
308 profile.putMachineSetting('extruder_head_size_height', '0')
309 profile.checkAndUpdateMachineName()
311 class MachineSelectPage(InfoPage):
312 def __init__(self, parent):
313 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
314 self.AddText(_("What kind of machine do you have:"))
316 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2", style=wx.RB_GROUP)
317 self.Ultimaker2Radio.SetValue(True)
318 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
319 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
320 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
321 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot)"))
322 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
324 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
325 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
326 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
327 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
328 self.SubmitUserStats.SetValue(True)
330 def OnUltimaker2Select(self, e):
331 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
333 def OnUltimakerSelect(self, e):
334 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
336 def OnOtherSelect(self, e):
337 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
340 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
344 profile.putProfileSetting('retraction_enable', 'True')
345 if self.Ultimaker2Radio.GetValue():
346 profile.putMachineSetting('machine_width', '230')
347 profile.putMachineSetting('machine_depth', '225')
348 profile.putMachineSetting('machine_height', '205')
349 profile.putMachineSetting('machine_name', 'ultimaker2')
350 profile.putMachineSetting('machine_type', 'ultimaker2')
351 profile.putMachineSetting('machine_center_is_zero', 'False')
352 profile.putMachineSetting('has_heated_bed', 'True')
353 profile.putMachineSetting('gcode_flavor', 'UltiGCode')
354 profile.putMachineSetting('extruder_head_size_min_x', '40.0')
355 profile.putMachineSetting('extruder_head_size_min_y', '10.0')
356 profile.putMachineSetting('extruder_head_size_max_x', '60.0')
357 profile.putMachineSetting('extruder_head_size_max_y', '30.0')
358 profile.putMachineSetting('extruder_head_size_height', '55.0')
359 profile.putProfileSetting('nozzle_size', '0.4')
360 profile.putProfileSetting('fan_full_height', '5.0')
361 profile.putMachineSetting('extruder_offset_x1', '18.0')
362 profile.putMachineSetting('extruder_offset_y1', '0.0')
363 elif self.UltimakerRadio.GetValue():
364 profile.putMachineSetting('machine_width', '205')
365 profile.putMachineSetting('machine_depth', '205')
366 profile.putMachineSetting('machine_height', '200')
367 profile.putMachineSetting('machine_name', 'ultimaker original')
368 profile.putMachineSetting('machine_type', 'ultimaker')
369 profile.putMachineSetting('machine_center_is_zero', 'False')
370 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
371 profile.putProfileSetting('nozzle_size', '0.4')
372 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
373 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
374 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
375 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
376 profile.putMachineSetting('extruder_head_size_height', '55.0')
378 profile.putMachineSetting('machine_width', '80')
379 profile.putMachineSetting('machine_depth', '80')
380 profile.putMachineSetting('machine_height', '60')
381 profile.putMachineSetting('machine_name', 'reprap')
382 profile.putMachineSetting('machine_type', 'reprap')
383 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
384 profile.putPreference('startMode', 'Normal')
385 profile.putProfileSetting('nozzle_size', '0.5')
386 profile.checkAndUpdateMachineName()
387 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
388 if self.SubmitUserStats.GetValue():
389 profile.putPreference('submit_slice_information', 'True')
391 profile.putPreference('submit_slice_information', 'False')
394 class SelectParts(InfoPage):
395 def __init__(self, parent):
396 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
397 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."))
399 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
400 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
401 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
403 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."))
404 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
405 self.springExtruder.SetValue(True)
408 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
409 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
410 if self.dualExtrusion.GetValue():
411 profile.putMachineSetting('extruder_amount', '2')
412 profile.putMachineSetting('machine_depth', '195')
414 profile.putMachineSetting('extruder_amount', '1')
415 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
416 profile.putProfileSetting('retraction_enable', 'True')
418 profile.putProfileSetting('retraction_enable', 'False')
421 class UltimakerFirmwareUpgradePage(InfoPage):
422 def __init__(self, parent):
423 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
424 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."))
425 self.AddHiddenSeperator()
426 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
427 self.AddHiddenSeperator()
428 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."))
429 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
430 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
431 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
432 self.AddHiddenSeperator()
433 self.AddText(_("Do not upgrade to this firmware if:"))
434 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
435 self.AddText(_("* Have other changes in the firmware"))
436 # button = self.AddButton('Goto this page for a custom firmware')
437 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
442 def OnUpgradeClick(self, e):
443 if firmwareInstall.InstallFirmware():
444 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
446 def OnSkipClick(self, e):
447 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
448 self.GetParent().ShowPage(self.GetNext())
450 def OnUrlClick(self, e):
451 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
453 class UltimakerCheckupPage(InfoPage):
454 def __init__(self, parent):
455 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
457 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
458 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
459 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
460 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
461 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
462 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
463 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
464 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
465 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
466 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
469 _("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."))
470 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
471 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
472 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
474 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
475 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
476 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
478 self.infoBox = self.AddInfoBox()
479 self.machineState = self.AddText("")
480 self.temperatureLabel = self.AddText("")
481 self.errorLogButton = self.AddButton(_("Show error log"))
482 self.errorLogButton.Show(False)
484 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
486 self.xMinStop = False
487 self.xMaxStop = False
488 self.yMinStop = False
489 self.yMaxStop = False
490 self.zMinStop = False
491 self.zMaxStop = False
493 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
496 if self.comm is not None:
500 self.endstopBitmap.Show(False)
503 def OnSkipClick(self, e):
504 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
505 self.GetParent().ShowPage(self.GetNext())
507 def OnCheckClick(self, e=None):
508 self.errorLogButton.Show(False)
509 if self.comm is not None:
513 wx.CallAfter(self.OnCheckClick)
515 self.infoBox.SetBusy(_("Connecting to machine."))
516 self.commState.SetBitmap(self.unknownBitmap)
517 self.tempState.SetBitmap(self.unknownBitmap)
518 self.stopState.SetBitmap(self.unknownBitmap)
519 self.checkupState = 0
520 self.checkExtruderNr = 0
521 self.comm = machineCom.MachineCom(callbackObject=self)
523 def OnErrorLog(self, e):
524 printWindow.LogWindow('\n'.join(self.comm.getLog()))
526 def mcLog(self, message):
529 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
530 if not self.comm.isOperational():
532 if self.checkupState == 0:
533 self.tempCheckTimeout = 20
534 if temp[self.checkExtruderNr] > 70:
535 self.checkupState = 1
536 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
537 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
538 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
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 == 1:
547 self.startTemp = temp[self.checkExtruderNr]
548 self.checkupState = 2
549 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
550 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
551 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
552 elif self.checkupState == 2:
553 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
554 if temp[self.checkExtruderNr] > self.startTemp + 40:
555 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
556 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
557 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
558 self.checkExtruderNr = 0
559 self.checkupState = 3
560 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
561 wx.CallAfter(self.endstopBitmap.Show, True)
562 wx.CallAfter(self.Layout)
563 self.comm.sendCommand('M119')
564 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
566 self.checkupState = 0
567 self.checkExtruderNr += 1
569 self.tempCheckTimeout -= 1
570 if self.tempCheckTimeout < 1:
571 self.checkupState = -1
572 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
573 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
574 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
575 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
576 elif self.checkupState >= 3 and self.checkupState < 10:
577 self.comm.sendCommand('M119')
578 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
580 def mcStateChange(self, state):
581 if self.comm is None:
583 if self.comm.isOperational():
584 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
585 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
586 elif self.comm.isError():
587 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
588 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
589 wx.CallAfter(self.endstopBitmap.Show, False)
590 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
591 wx.CallAfter(self.errorLogButton.Show, True)
592 wx.CallAfter(self.Layout)
594 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
596 def mcMessage(self, message):
597 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
598 for data in message.split(' '):
600 tag, value = data.split(':', 1)
602 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
604 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
606 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
608 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
610 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
612 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
614 tag, value = map(str.strip, message.split(':', 1))
616 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
618 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
620 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
622 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
624 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
626 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
627 if 'z_max' in message:
628 self.comm.sendCommand('M119')
630 if self.checkupState == 3:
631 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
632 self.checkupState = 4
633 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
634 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
635 elif self.checkupState == 4:
636 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
637 self.checkupState = 5
638 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
639 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
640 elif self.checkupState == 5:
641 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
642 self.checkupState = 6
643 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
644 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
645 elif self.checkupState == 6:
646 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
647 self.checkupState = 7
648 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
649 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
650 elif self.checkupState == 7:
651 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
652 self.checkupState = 8
653 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
654 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
655 elif self.checkupState == 8:
656 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
657 self.checkupState = 9
658 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
659 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
660 elif self.checkupState == 9:
661 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
662 self.checkupState = 10
664 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
665 wx.CallAfter(self.infoBox.SetReadyIndicator)
666 wx.CallAfter(self.endstopBitmap.Show, False)
667 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
668 wx.CallAfter(self.OnSkipClick, None)
670 def mcProgress(self, lineNr):
673 def mcZChange(self, newZ):
677 class UltimakerCalibrationPage(InfoPage):
678 def __init__(self, parent):
679 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
681 self.AddText("Your Ultimaker requires some calibration.")
682 self.AddText("This calibration is needed for a proper extrusion amount.")
684 self.AddText("The following values are needed:")
685 self.AddText("* Diameter of filament")
686 self.AddText("* Number of steps per mm of filament extrusion")
688 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
690 self.AddText("First we need the diameter of your filament:")
691 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
693 "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.")
694 self.AddText("Note: This value can be changed later at any time.")
697 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
700 class UltimakerCalibrateStepsPerEPage(InfoPage):
701 def __init__(self, parent):
702 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
704 #if profile.getMachineSetting('steps_per_e') == '0':
705 # profile.putMachineSetting('steps_per_e', '865.888')
707 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
708 self.AddText(_("First remove any filament from your machine."))
709 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
710 self.AddText(_("We'll push the filament 100mm"))
711 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
712 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
713 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
714 self.AddText(_("This results in the following steps per E:"))
715 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
716 self.AddText(_("You can repeat these steps to get better calibration."))
719 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
720 self.heatButton = self.AddButton(_("Heatup for filament removal"))
722 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
723 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
724 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
726 def OnSaveLengthClick(self, e):
727 currentEValue = float(self.stepsPerEInput.GetValue())
728 realExtrudeLength = float(self.lengthInput.GetValue())
729 newEValue = currentEValue * 100 / realExtrudeLength
730 self.stepsPerEInput.SetValue(str(newEValue))
731 self.lengthInput.SetValue("100")
733 def OnExtrudeClick(self, e):
734 t = threading.Thread(target=self.OnExtrudeRun)
738 def OnExtrudeRun(self):
739 self.heatButton.Enable(False)
740 self.extrudeButton.Enable(False)
741 currentEValue = float(self.stepsPerEInput.GetValue())
742 self.comm = machineCom.MachineCom()
743 if not self.comm.isOpen():
745 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
746 'Printer error', wx.OK | wx.ICON_INFORMATION)
747 self.heatButton.Enable(True)
748 self.extrudeButton.Enable(True)
751 line = self.comm.readline()
756 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
759 self.sendGCommand('M302') #Disable cold extrusion protection
760 self.sendGCommand("M92 E%f" % (currentEValue))
761 self.sendGCommand("G92 E0")
762 self.sendGCommand("G1 E100 F600")
765 self.extrudeButton.Enable()
766 self.heatButton.Enable()
768 def OnHeatClick(self, e):
769 t = threading.Thread(target=self.OnHeatRun)
774 self.heatButton.Enable(False)
775 self.extrudeButton.Enable(False)
776 self.comm = machineCom.MachineCom()
777 if not self.comm.isOpen():
779 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
780 'Printer error', wx.OK | wx.ICON_INFORMATION)
781 self.heatButton.Enable(True)
782 self.extrudeButton.Enable(True)
785 line = self.comm.readline()
787 self.heatButton.Enable(True)
788 self.extrudeButton.Enable(True)
792 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
795 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
797 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
798 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
799 self.sendGCommand('M104 S0')
802 self.heatButton.Enable(True)
803 self.extrudeButton.Enable(True)
805 def sendGCommand(self, cmd):
806 self.comm.sendCommand(cmd) #Disable cold extrusion protection
808 line = self.comm.readline()
811 if line.startswith('ok'):
815 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
817 class Ultimaker2ReadyPage(InfoPage):
818 def __init__(self, parent):
819 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
820 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
821 self.AddText('Cura is now ready to be used with your Ultimaker2.')
824 class configWizard(wx.wizard.Wizard):
825 def __init__(self, addNew = False):
826 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
828 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
829 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
831 self.firstInfoPage = FirstInfoPage(self, addNew)
832 self.machineSelectPage = MachineSelectPage(self)
833 self.ultimakerSelectParts = SelectParts(self)
834 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
835 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
836 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
837 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
838 self.bedLevelPage = bedLevelWizardMain(self)
839 self.headOffsetCalibration = headOffsetCalibrationPage(self)
840 self.otherMachineSelectPage = OtherMachineSelectPage(self)
841 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
842 self.otherMachineInfoPage = OtherMachineInfoPage(self)
844 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
846 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
847 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
848 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
849 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
850 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
851 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
852 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
853 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
855 self.FitToPage(self.firstInfoPage)
856 self.GetPageAreaSizer().Add(self.firstInfoPage)
858 self.RunWizard(self.firstInfoPage)
861 def OnPageChanging(self, e):
862 e.GetPage().StoreData()
864 def OnPageChanged(self, e):
865 if e.GetPage().AllowNext():
866 self.FindWindowById(wx.ID_FORWARD).Enable()
868 self.FindWindowById(wx.ID_FORWARD).Disable()
869 if e.GetPage().AllowBack():
870 self.FindWindowById(wx.ID_BACKWARD).Enable()
872 self.FindWindowById(wx.ID_BACKWARD).Disable()
874 class bedLevelWizardMain(InfoPage):
875 def __init__(self, parent):
876 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
878 self.AddText('This wizard will help you in leveling your printer bed')
880 self.AddText('It will do the following steps')
881 self.AddText('* Move the printer head to each corner')
882 self.AddText(' and let you adjust the height of the bed to the nozzle')
883 self.AddText('* Print a line around the bed to check if it is level')
886 self.connectButton = self.AddButton('Connect to printer')
889 self.infoBox = self.AddInfoBox()
890 self.resumeButton = self.AddButton('Resume')
891 self.resumeButton.Enable(False)
893 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
894 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
896 def OnConnect(self, e = None):
897 if self.comm is not None:
901 wx.CallAfter(self.OnConnect)
903 self.connectButton.Enable(False)
904 self.comm = machineCom.MachineCom(callbackObject=self)
905 self.infoBox.SetBusy('Connecting to machine.')
906 self._wizardState = 0
909 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
910 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
913 def OnResume(self, e):
914 feedZ = profile.getProfileSettingFloat('print_speed') * 60
915 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
916 if self._wizardState == 2:
917 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
918 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
919 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
920 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
921 self.comm.sendCommand('M400')
922 self._wizardState = 3
923 elif self._wizardState == 4:
924 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
925 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
926 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
927 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
928 self.comm.sendCommand('M400')
929 self._wizardState = 5
930 elif self._wizardState == 6:
931 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
932 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
933 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
934 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
935 self.comm.sendCommand('M400')
936 self._wizardState = 7
937 elif self._wizardState == 8:
938 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
939 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
940 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
941 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
942 self._wizardState = 9
943 elif self._wizardState == 10:
944 self._wizardState = 11
945 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
946 feedZ = profile.getProfileSettingFloat('print_speed') * 60
947 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
948 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
949 w = profile.getMachineSettingFloat('machine_width')
950 d = profile.getMachineSettingFloat('machine_depth')
951 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
952 filamentArea = math.pi * filamentRadius * filamentRadius
953 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
957 'G1 Z2 F%d' % (feedZ),
959 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
960 'G1 Z0.3 F%d' % (feedZ)]
962 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
964 for i in xrange(0, 3):
965 dist = 5.0 + 0.4 * float(i)
966 eValue += (d - 2.0*dist) * ePerMM
967 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
968 eValue += (w - 2.0*dist) * ePerMM
969 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
970 eValue += (d - 2.0*dist) * ePerMM
971 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
972 eValue += (w - 2.0*dist) * ePerMM
973 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
975 gcodeList.append('M400')
976 self.comm.printGCode(gcodeList)
977 self.resumeButton.Enable(False)
979 def mcLog(self, message):
980 print 'Log:', message
982 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
983 if self._wizardState == 1:
984 self._wizardState = 2
985 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
986 wx.CallAfter(self.resumeButton.Enable, True)
987 elif self._wizardState == 3:
988 self._wizardState = 4
989 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
990 wx.CallAfter(self.resumeButton.Enable, True)
991 elif self._wizardState == 5:
992 self._wizardState = 6
993 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
994 wx.CallAfter(self.resumeButton.Enable, True)
995 elif self._wizardState == 7:
996 self._wizardState = 8
997 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
998 wx.CallAfter(self.resumeButton.Enable, True)
999 elif self._wizardState == 9:
1000 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1001 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1003 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
1004 wx.CallAfter(self.resumeButton.Enable, True)
1005 self._wizardState = 10
1007 def mcStateChange(self, state):
1008 if self.comm is None:
1010 if self.comm.isOperational():
1011 if self._wizardState == 0:
1012 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
1013 self.comm.sendCommand('M105')
1014 self.comm.sendCommand('G28')
1015 self._wizardState = 1
1016 elif self._wizardState == 11 and not self.comm.isPrinting():
1017 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1018 self.comm.sendCommand('G92 E0')
1019 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1020 self.comm.sendCommand('M104 S0')
1021 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
1022 wx.CallAfter(self.infoBox.SetReadyIndicator)
1023 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1024 wx.CallAfter(self.connectButton.Enable, True)
1025 self._wizardState = 12
1026 elif self.comm.isError():
1027 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1029 def mcMessage(self, message):
1032 def mcProgress(self, lineNr):
1035 def mcZChange(self, newZ):
1038 class headOffsetCalibrationPage(InfoPage):
1039 def __init__(self, parent):
1040 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1042 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
1045 self.connectButton = self.AddButton('Connect to printer')
1048 self.infoBox = self.AddInfoBox()
1049 self.textEntry = self.AddTextCtrl('')
1050 self.textEntry.Enable(False)
1051 self.resumeButton = self.AddButton('Resume')
1052 self.resumeButton.Enable(False)
1054 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1055 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1057 def AllowBack(self):
1060 def OnConnect(self, e = None):
1061 if self.comm is not None:
1065 wx.CallAfter(self.OnConnect)
1067 self.connectButton.Enable(False)
1068 self.comm = machineCom.MachineCom(callbackObject=self)
1069 self.infoBox.SetBusy('Connecting to machine.')
1070 self._wizardState = 0
1072 def OnResume(self, e):
1073 if self._wizardState == 2:
1074 self._wizardState = 3
1075 wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1077 w = profile.getMachineSettingFloat('machine_width')
1078 d = profile.getMachineSettingFloat('machine_depth')
1080 gcode = gcodeGenerator.gcodeGenerator()
1081 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1082 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1089 gcode.addMove(w/2, 5)
1090 gcode.addMove(z=0.2)
1092 gcode.addExtrude(w/2, d-5.0)
1094 gcode.addMove(5, d/2)
1096 gcode.addExtrude(w-5.0, d/2)
1097 gcode.addRetract(15)
1100 gcode.addMove(w/2, 5)
1102 gcode.addExtrude(w/2, d-5.0)
1104 gcode.addMove(5, d/2)
1106 gcode.addExtrude(w-5.0, d/2)
1107 gcode.addRetract(15)
1112 gcode.addCmd('M400')
1114 self.comm.printGCode(gcode.list())
1115 self.resumeButton.Enable(False)
1116 elif self._wizardState == 4:
1118 float(self.textEntry.GetValue())
1121 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1122 self._wizardState = 5
1123 self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1124 self.textEntry.SetValue('0.0')
1125 self.textEntry.Enable(True)
1126 elif self._wizardState == 5:
1128 float(self.textEntry.GetValue())
1131 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1132 self._wizardState = 6
1133 self.infoBox.SetBusy('Printing the fine calibration lines.')
1134 self.textEntry.SetValue('')
1135 self.textEntry.Enable(False)
1136 self.resumeButton.Enable(False)
1138 x = profile.getMachineSettingFloat('extruder_offset_x1')
1139 y = profile.getMachineSettingFloat('extruder_offset_y1')
1140 gcode = gcodeGenerator.gcodeGenerator()
1141 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1142 gcode.setPrintSpeed(25)
1145 gcode.addMove(50, 40, 0.2)
1147 for n in xrange(0, 10):
1148 gcode.addExtrude(50 + n * 10, 150)
1149 gcode.addExtrude(50 + n * 10 + 5, 150)
1150 gcode.addExtrude(50 + n * 10 + 5, 40)
1151 gcode.addExtrude(50 + n * 10 + 10, 40)
1152 gcode.addMove(40, 50)
1153 for n in xrange(0, 10):
1154 gcode.addExtrude(150, 50 + n * 10)
1155 gcode.addExtrude(150, 50 + n * 10 + 5)
1156 gcode.addExtrude(40, 50 + n * 10 + 5)
1157 gcode.addExtrude(40, 50 + n * 10 + 10)
1158 gcode.addRetract(15)
1161 gcode.addMove(50 - x, 30 - y, 0.2)
1163 for n in xrange(0, 10):
1164 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1165 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1166 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1167 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1168 gcode.addMove(30 - x, 50 - y, 0.2)
1169 for n in xrange(0, 10):
1170 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1171 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1172 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1173 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1174 gcode.addRetract(15)
1176 gcode.addCmd('M400')
1177 gcode.addCmd('M104 T0 S0')
1178 gcode.addCmd('M104 T1 S0')
1179 self.comm.printGCode(gcode.list())
1180 elif self._wizardState == 7:
1182 n = int(self.textEntry.GetValue()) - 1
1185 x = profile.getMachineSettingFloat('extruder_offset_x1')
1187 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1188 self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1189 self.textEntry.SetValue('10')
1190 self._wizardState = 8
1191 elif self._wizardState == 8:
1193 n = int(self.textEntry.GetValue()) - 1
1196 y = profile.getMachineSettingFloat('extruder_offset_y1')
1198 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1199 self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1200 self.infoBox.SetReadyIndicator()
1201 self._wizardState = 8
1203 self.resumeButton.Enable(False)
1205 def mcLog(self, message):
1206 print 'Log:', message
1208 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1209 if self._wizardState == 1:
1210 if temp[0] >= 210 and temp[1] >= 210:
1211 self._wizardState = 2
1212 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1213 wx.CallAfter(self.resumeButton.Enable, True)
1214 wx.CallAfter(self.resumeButton.SetFocus)
1216 def mcStateChange(self, state):
1217 if self.comm is None:
1219 if self.comm.isOperational():
1220 if self._wizardState == 0:
1221 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1222 self.comm.sendCommand('M105')
1223 self.comm.sendCommand('M104 S220 T0')
1224 self.comm.sendCommand('M104 S220 T1')
1225 self.comm.sendCommand('G28')
1226 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1227 self._wizardState = 1
1228 if not self.comm.isPrinting():
1229 if self._wizardState == 3:
1230 self._wizardState = 4
1231 wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1232 wx.CallAfter(self.textEntry.SetValue, '0.0')
1233 wx.CallAfter(self.textEntry.Enable, True)
1234 wx.CallAfter(self.resumeButton.Enable, True)
1235 wx.CallAfter(self.resumeButton.SetFocus)
1236 elif self._wizardState == 6:
1237 self._wizardState = 7
1238 wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1239 wx.CallAfter(self.textEntry.SetValue, '10')
1240 wx.CallAfter(self.textEntry.Enable, True)
1241 wx.CallAfter(self.resumeButton.Enable, True)
1242 wx.CallAfter(self.resumeButton.SetFocus)
1244 elif self.comm.isError():
1245 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1247 def mcMessage(self, message):
1250 def mcProgress(self, lineNr):
1253 def mcZChange(self, newZ):
1256 class bedLevelWizard(wx.wizard.Wizard):
1258 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1260 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1261 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1263 self.mainPage = bedLevelWizardMain(self)
1264 self.headOffsetCalibration = None
1266 self.FitToPage(self.mainPage)
1267 self.GetPageAreaSizer().Add(self.mainPage)
1269 self.RunWizard(self.mainPage)
1272 def OnPageChanging(self, e):
1273 e.GetPage().StoreData()
1275 def OnPageChanged(self, e):
1276 if e.GetPage().AllowNext():
1277 self.FindWindowById(wx.ID_FORWARD).Enable()
1279 self.FindWindowById(wx.ID_FORWARD).Disable()
1280 if e.GetPage().AllowBack():
1281 self.FindWindowById(wx.ID_BACKWARD).Enable()
1283 self.FindWindowById(wx.ID_BACKWARD).Disable()
1285 class headOffsetWizard(wx.wizard.Wizard):
1287 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1289 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1290 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1292 self.mainPage = headOffsetCalibrationPage(self)
1294 self.FitToPage(self.mainPage)
1295 self.GetPageAreaSizer().Add(self.mainPage)
1297 self.RunWizard(self.mainPage)
1300 def OnPageChanging(self, e):
1301 e.GetPage().StoreData()
1303 def OnPageChanged(self, e):
1304 if e.GetPage().AllowNext():
1305 self.FindWindowById(wx.ID_FORWARD).Enable()
1307 self.FindWindowById(wx.ID_FORWARD).Disable()
1308 if e.GetPage().AllowBack():
1309 self.FindWindowById(wx.ID_BACKWARD).Enable()
1311 self.FindWindowById(wx.ID_BACKWARD).Disable()