2 from __future__ import absolute_import
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.resources import getPathForImage
18 class InfoBox(wx.Panel):
19 def __init__(self, parent):
20 super(InfoBox, self).__init__(parent)
21 self.SetBackgroundColour('#FFFF80')
23 self.sizer = wx.GridBagSizer(5, 5)
24 self.SetSizer(self.sizer)
26 self.attentionBitmap = wx.Bitmap(getPathForImage('attention.png'))
27 self.errorBitmap = wx.Bitmap(getPathForImage('error.png'))
28 self.readyBitmap = wx.Bitmap(getPathForImage('ready.png'))
30 wx.Bitmap(getPathForImage('busy-0.png')),
31 wx.Bitmap(getPathForImage('busy-1.png')),
32 wx.Bitmap(getPathForImage('busy-2.png')),
33 wx.Bitmap(getPathForImage('busy-3.png'))
36 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
37 self.text = wx.StaticText(self, -1, '')
38 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
39 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
40 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
41 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
42 self.sizer.AddGrowableCol(1)
44 self.extraInfoButton.Show(False)
46 self.extraInfoUrl = ''
48 self.timer = wx.Timer(self)
49 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
50 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
53 def SetInfo(self, info):
54 self.SetBackgroundColour('#FFFF80')
55 self.text.SetLabel(info)
56 self.extraInfoButton.Show(False)
59 def SetError(self, info, extraInfoUrl):
60 self.extraInfoUrl = extraInfoUrl
61 self.SetBackgroundColour('#FF8080')
62 self.text.SetLabel(info)
63 self.extraInfoButton.Show(True)
65 self.SetErrorIndicator()
68 def SetAttention(self, info):
69 self.SetBackgroundColour('#FFFF80')
70 self.text.SetLabel(info)
71 self.extraInfoButton.Show(False)
72 self.SetAttentionIndicator()
76 def SetBusy(self, info):
78 self.SetBusyIndicator()
80 def SetBusyIndicator(self):
82 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
84 def doExtraInfo(self, e):
85 webbrowser.open(self.extraInfoUrl)
87 def doBusyUpdate(self, e):
88 if self.busyState is None:
91 if self.busyState >= len(self.busyBitmap):
93 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
95 def SetReadyIndicator(self):
97 self.bitmap.SetBitmap(self.readyBitmap)
99 def SetErrorIndicator(self):
100 self.busyState = None
101 self.bitmap.SetBitmap(self.errorBitmap)
103 def SetAttentionIndicator(self):
104 self.busyState = None
105 self.bitmap.SetBitmap(self.attentionBitmap)
108 class InfoPage(wx.wizard.WizardPageSimple):
109 def __init__(self, parent, title):
110 wx.wizard.WizardPageSimple.__init__(self, parent)
112 sizer = wx.GridBagSizer(5, 5)
116 title = wx.StaticText(self, -1, title)
117 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
118 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
119 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
120 sizer.AddGrowableCol(1)
124 def AddText(self, info):
125 text = wx.StaticText(self, -1, info)
126 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
130 def AddSeperator(self):
131 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
134 def AddHiddenSeperator(self):
137 def AddInfoBox(self):
138 infoBox = InfoBox(self)
139 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
143 def AddRadioButton(self, label, style=0):
144 radio = wx.RadioButton(self, -1, label, style=style)
145 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
149 def AddCheckbox(self, label, checked=False):
150 check = wx.CheckBox(self, -1)
151 text = wx.StaticText(self, -1, label)
152 check.SetValue(checked)
153 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
154 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
158 def AddButton(self, label):
159 button = wx.Button(self, -1, label)
160 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
164 def AddDualButton(self, label1, label2):
165 button1 = wx.Button(self, -1, label1)
166 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
167 button2 = wx.Button(self, -1, label2)
168 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
170 return button1, button2
172 def AddTextCtrl(self, value):
173 ret = wx.TextCtrl(self, -1, value)
174 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
178 def AddLabelTextCtrl(self, info, value):
179 text = wx.StaticText(self, -1, info)
180 ret = wx.TextCtrl(self, -1, value)
181 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
182 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
186 def AddTextCtrlButton(self, value, buttonText):
187 text = wx.TextCtrl(self, -1, value)
188 button = wx.Button(self, -1, buttonText)
189 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
190 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
194 def AddBitmap(self, bitmap):
195 bitmap = wx.StaticBitmap(self, -1, bitmap)
196 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
200 def AddCheckmark(self, label, bitmap):
201 check = wx.StaticBitmap(self, -1, bitmap)
202 text = wx.StaticText(self, -1, label)
203 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
204 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
215 class FirstInfoPage(InfoPage):
216 def __init__(self, parent):
217 super(FirstInfoPage, self).__init__(parent, "First time run wizard")
218 self.AddText('Welcome, and thanks for trying Cura!')
220 self.AddText('This wizard will help you with the following steps:')
221 self.AddText('* Configure Cura for your machine')
222 self.AddText('* Upgrade your firmware')
223 self.AddText('* Check if your machine is working safely')
224 self.AddText('* Level your printer bed')
226 #self.AddText('* Calibrate your machine')
227 #self.AddText('* Do your first print')
230 class RepRapInfoPage(InfoPage):
231 def __init__(self, parent):
232 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
234 'RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.')
235 self.AddText('If you like a default profile for your machine added,\nthen make an issue on github.')
237 self.AddText('You will have to manually install Marlin or Sprinter firmware.')
239 self.machineWidth = self.AddLabelTextCtrl('Machine width (mm)', '80')
240 self.machineDepth = self.AddLabelTextCtrl('Machine depth (mm)', '80')
241 self.machineHeight = self.AddLabelTextCtrl('Machine height (mm)', '60')
242 self.nozzleSize = self.AddLabelTextCtrl('Nozzle size (mm)', '0.5')
243 self.heatedBed = self.AddCheckbox('Heated bed')
244 self.HomeAtCenter = self.AddCheckbox('Bed center is 0,0,0 (RoStock)')
247 profile.putPreference('machine_width', self.machineWidth.GetValue())
248 profile.putPreference('machine_depth', self.machineDepth.GetValue())
249 profile.putPreference('machine_height', self.machineHeight.GetValue())
250 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
251 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
252 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
253 profile.putPreference('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
256 class MachineSelectPage(InfoPage):
257 def __init__(self, parent):
258 super(MachineSelectPage, self).__init__(parent, "Select your machine")
259 self.AddText('What kind of machine do you have:')
261 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
262 self.UltimakerRadio.SetValue(True)
263 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
264 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
265 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
267 def OnUltimakerSelect(self, e):
268 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
270 def OnOtherSelect(self, e):
271 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
274 if self.UltimakerRadio.GetValue():
275 profile.putPreference('machine_width', '205')
276 profile.putPreference('machine_depth', '205')
277 profile.putPreference('machine_height', '200')
278 profile.putPreference('machine_type', 'ultimaker')
279 profile.putPreference('machine_center_is_zero', 'False')
280 profile.putProfileSetting('nozzle_size', '0.4')
282 profile.putPreference('machine_width', '80')
283 profile.putPreference('machine_depth', '80')
284 profile.putPreference('machine_height', '60')
285 profile.putPreference('machine_type', 'reprap')
286 profile.putPreference('startMode', 'Normal')
287 profile.putProfileSetting('nozzle_size', '0.5')
288 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
291 class SelectParts(InfoPage):
292 def __init__(self, parent):
293 super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
294 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.')
296 self.springExtruder = self.AddCheckbox('Extruder drive upgrade')
297 self.heatedBed = self.AddCheckbox('Heated printer bed (self built)')
298 self.dualExtrusion = self.AddCheckbox('Dual extrusion (experimental)')
300 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.')
301 self.AddText('This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094')
302 self.springExtruder.SetValue(True)
305 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
306 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
307 if self.dualExtrusion.GetValue():
308 profile.putPreference('extruder_amount', '2')
310 profile.putPreference('extruder_amount', '1')
311 if profile.getPreference('ultimaker_extruder_upgrade') == 'True':
312 profile.putProfileSetting('retraction_enable', 'True')
314 profile.putProfileSetting('retraction_enable', 'False')
317 class FirmwareUpgradePage(InfoPage):
318 def __init__(self, parent):
319 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
321 '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.')
322 self.AddHiddenSeperator()
324 'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
325 self.AddHiddenSeperator()
327 '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.')
328 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
329 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
330 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
331 self.AddHiddenSeperator()
332 self.AddText('Do not upgrade to this firmware if:')
333 self.AddText('* You have an older machine based on ATMega1280')
334 self.AddText('* Have other changes in the firmware')
335 button = self.AddButton('Goto this page for a custom firmware')
336 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
341 def OnUpgradeClick(self, e):
342 if firmwareInstall.InstallFirmware():
343 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
345 def OnSkipClick(self, e):
346 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
348 def OnUrlClick(self, e):
349 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
352 class UltimakerCheckupPage(InfoPage):
353 def __init__(self, parent):
354 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
356 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
357 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
358 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
359 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
360 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
361 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
362 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
363 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
364 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
365 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
368 '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.')
369 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
370 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
371 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
373 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
374 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
375 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
377 self.infoBox = self.AddInfoBox()
378 self.machineState = self.AddText('')
379 self.temperatureLabel = self.AddText('')
380 self.errorLogButton = self.AddButton('Show error log')
381 self.errorLogButton.Show(False)
383 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
385 self.xMinStop = False
386 self.xMaxStop = False
387 self.yMinStop = False
388 self.yMaxStop = False
389 self.zMinStop = False
390 self.zMaxStop = False
392 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
395 if self.comm is not None:
399 self.endstopBitmap.Show(False)
402 def OnSkipClick(self, e):
403 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
405 def OnCheckClick(self, e=None):
406 self.errorLogButton.Show(False)
407 if self.comm is not None:
411 wx.CallAfter(self.OnCheckClick)
413 self.infoBox.SetBusy('Connecting to machine.')
414 self.commState.SetBitmap(self.unknownBitmap)
415 self.tempState.SetBitmap(self.unknownBitmap)
416 self.stopState.SetBitmap(self.unknownBitmap)
417 self.checkupState = 0
418 self.comm = machineCom.MachineCom(callbackObject=self)
420 def OnErrorLog(self, e):
421 printWindow.LogWindow('\n'.join(self.comm.getLog()))
423 def mcLog(self, message):
426 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
427 if not self.comm.isOperational():
429 if self.checkupState == 0:
430 self.tempCheckTimeout = 20
432 self.checkupState = 1
433 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
434 self.comm.sendCommand('M104 S0')
435 self.comm.sendCommand('M104 S0')
437 self.startTemp = temp
438 self.checkupState = 2
439 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
440 self.comm.sendCommand('M104 S200')
441 self.comm.sendCommand('M104 S200')
442 elif self.checkupState == 1:
444 self.startTemp = temp
445 self.checkupState = 2
446 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
447 self.comm.sendCommand('M104 S200')
448 self.comm.sendCommand('M104 S200')
449 elif self.checkupState == 2:
450 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
451 if temp > self.startTemp + 40:
452 self.checkupState = 3
453 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
454 wx.CallAfter(self.endstopBitmap.Show, True)
455 wx.CallAfter(self.Layout)
456 self.comm.sendCommand('M104 S0')
457 self.comm.sendCommand('M104 S0')
458 self.comm.sendCommand('M119')
459 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
461 self.tempCheckTimeout -= 1
462 if self.tempCheckTimeout < 1:
463 self.checkupState = -1
464 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
465 wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
466 self.comm.sendCommand('M104 S0')
467 self.comm.sendCommand('M104 S0')
468 elif self.checkupState >= 3 and self.checkupState < 10:
469 self.comm.sendCommand('M119')
470 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
472 def mcStateChange(self, state):
473 if self.comm is None:
475 if self.comm.isOperational():
476 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
477 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
478 elif self.comm.isError():
479 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
480 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
481 wx.CallAfter(self.endstopBitmap.Show, False)
482 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
483 wx.CallAfter(self.errorLogButton.Show, True)
484 wx.CallAfter(self.Layout)
486 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
488 def mcMessage(self, message):
489 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
490 for data in message.split(' '):
492 tag, value = data.split(':', 1)
494 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
496 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
498 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
500 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
502 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
504 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
506 tag, value = map(str.strip, message.split(':', 1))
508 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
510 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
512 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
514 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
516 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
518 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
519 if 'z_max' in message:
520 self.comm.sendCommand('M119')
522 if self.checkupState == 3:
523 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
524 self.checkupState = 4
525 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
526 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
527 elif self.checkupState == 4:
528 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
529 self.checkupState = 5
530 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
531 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
532 elif self.checkupState == 5:
533 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
534 self.checkupState = 6
535 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
536 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
537 elif self.checkupState == 6:
538 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
539 self.checkupState = 7
540 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
541 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
542 elif self.checkupState == 7:
543 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
544 self.checkupState = 8
545 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
546 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
547 elif self.checkupState == 8:
548 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
549 self.checkupState = 9
550 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
551 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
552 elif self.checkupState == 9:
553 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
554 self.checkupState = 10
556 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
557 wx.CallAfter(self.infoBox.SetReadyIndicator)
558 wx.CallAfter(self.endstopBitmap.Show, False)
559 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
560 wx.CallAfter(self.OnSkipClick, None)
562 def mcProgress(self, lineNr):
565 def mcZChange(self, newZ):
569 class UltimakerCalibrationPage(InfoPage):
570 def __init__(self, parent):
571 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
573 self.AddText("Your Ultimaker requires some calibration.")
574 self.AddText("This calibration is needed for a proper extrusion amount.")
576 self.AddText("The following values are needed:")
577 self.AddText("* Diameter of filament")
578 self.AddText("* Number of steps per mm of filament extrusion")
580 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
582 self.AddText("First we need the diameter of your filament:")
583 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
585 "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.")
586 self.AddText("Note: This value can be changed later at any time.")
589 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
592 class UltimakerCalibrateStepsPerEPage(InfoPage):
593 def __init__(self, parent):
594 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
596 if profile.getPreference('steps_per_e') == '0':
597 profile.putPreference('steps_per_e', '865.888')
599 self.AddText("Calibrating the Steps Per E requires some manual actions.")
600 self.AddText("First remove any filament from your machine.")
601 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
602 self.AddText("We'll push the filament 100mm")
603 self.extrudeButton = self.AddButton("Extrude 100mm filament")
604 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
605 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
606 self.AddText("This results in the following steps per E:")
607 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
608 self.AddText("You can repeat these steps to get better calibration.")
611 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
612 self.heatButton = self.AddButton("Heatup for filament removal")
614 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
615 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
616 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
618 def OnSaveLengthClick(self, e):
619 currentEValue = float(self.stepsPerEInput.GetValue())
620 realExtrudeLength = float(self.lengthInput.GetValue())
621 newEValue = currentEValue * 100 / realExtrudeLength
622 self.stepsPerEInput.SetValue(str(newEValue))
623 self.lengthInput.SetValue("100")
625 def OnExtrudeClick(self, e):
626 threading.Thread(target=self.OnExtrudeRun).start()
628 def OnExtrudeRun(self):
629 self.heatButton.Enable(False)
630 self.extrudeButton.Enable(False)
631 currentEValue = float(self.stepsPerEInput.GetValue())
632 self.comm = machineCom.MachineCom()
633 if not self.comm.isOpen():
635 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
636 'Printer error', wx.OK | wx.ICON_INFORMATION)
637 self.heatButton.Enable(True)
638 self.extrudeButton.Enable(True)
641 line = self.comm.readline()
646 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
649 self.sendGCommand('M302') #Disable cold extrusion protection
650 self.sendGCommand("M92 E%f" % (currentEValue))
651 self.sendGCommand("G92 E0")
652 self.sendGCommand("G1 E100 F600")
655 self.extrudeButton.Enable()
656 self.heatButton.Enable()
658 def OnHeatClick(self, e):
659 threading.Thread(target=self.OnHeatRun).start()
662 self.heatButton.Enable(False)
663 self.extrudeButton.Enable(False)
664 self.comm = machineCom.MachineCom()
665 if not self.comm.isOpen():
667 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
668 'Printer error', wx.OK | wx.ICON_INFORMATION)
669 self.heatButton.Enable(True)
670 self.extrudeButton.Enable(True)
673 line = self.comm.readline()
675 self.heatButton.Enable(True)
676 self.extrudeButton.Enable(True)
680 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
683 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
685 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
686 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
687 self.sendGCommand('M104 S0')
690 self.heatButton.Enable(True)
691 self.extrudeButton.Enable(True)
693 def sendGCommand(self, cmd):
694 self.comm.sendCommand(cmd) #Disable cold extrusion protection
696 line = self.comm.readline()
699 if line.startswith('ok'):
703 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
706 class configWizard(wx.wizard.Wizard):
708 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
710 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
711 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
713 self.firstInfoPage = FirstInfoPage(self)
714 self.machineSelectPage = MachineSelectPage(self)
715 self.ultimakerSelectParts = SelectParts(self)
716 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
717 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
718 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
719 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
720 self.bedLevelPage = bedLevelWizardMain(self)
721 self.repRapInfoPage = RepRapInfoPage(self)
723 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
724 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
725 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
726 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
727 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
728 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
730 self.FitToPage(self.firstInfoPage)
731 self.GetPageAreaSizer().Add(self.firstInfoPage)
733 self.RunWizard(self.firstInfoPage)
736 def OnPageChanging(self, e):
737 e.GetPage().StoreData()
739 def OnPageChanged(self, e):
740 if e.GetPage().AllowNext():
741 self.FindWindowById(wx.ID_FORWARD).Enable()
743 self.FindWindowById(wx.ID_FORWARD).Disable()
744 self.FindWindowById(wx.ID_BACKWARD).Disable()
746 class bedLevelWizardMain(InfoPage):
747 def __init__(self, parent):
748 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
750 self.AddText('This wizard will help you in leveling your printer bed')
752 self.AddText('It will do the following steps')
753 self.AddText('* Move the printer head to each corner')
754 self.AddText(' and let you adjust the height of the bed to the nozzle')
755 self.AddText('* Print a line around the bed to check if it is level')
758 self.connectButton = self.AddButton('Connect to printer')
761 self.infoBox = self.AddInfoBox()
762 self.resumeButton = self.AddButton('Resume')
763 self.resumeButton.Enable(False)
765 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
766 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
768 def OnConnect(self, e = None):
769 if self.comm is not None:
773 wx.CallAfter(self.OnConnect)
775 self.connectButton.Enable(False)
776 self.comm = machineCom.MachineCom(callbackObject=self)
777 self.infoBox.SetBusy('Connecting to machine.')
778 self._wizardState = 0
783 def OnResume(self, e):
784 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
785 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
786 if self._wizardState == 2:
787 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
788 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
789 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
790 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
791 self.comm.sendCommand('M400')
792 self._wizardState = 3
793 elif self._wizardState == 4:
794 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
795 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
796 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 25, feedTravel))
797 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
798 self.comm.sendCommand('M400')
799 self._wizardState = 5
800 elif self._wizardState == 6:
801 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
802 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
803 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
804 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
805 self.comm.sendCommand('M400')
806 self._wizardState = 7
807 elif self._wizardState == 8:
808 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
809 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
810 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
811 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
812 self._wizardState = 9
813 self.resumeButton.Enable(False)
815 def mcLog(self, message):
816 print 'Log:', message
818 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
819 if self._wizardState == 1:
820 self._wizardState = 2
821 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
822 wx.CallAfter(self.resumeButton.Enable, True)
823 elif self._wizardState == 3:
824 self._wizardState = 4
825 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
826 wx.CallAfter(self.resumeButton.Enable, True)
827 elif self._wizardState == 5:
828 self._wizardState = 6
829 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
830 wx.CallAfter(self.resumeButton.Enable, True)
831 elif self._wizardState == 7:
832 self._wizardState = 8
833 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
834 wx.CallAfter(self.resumeButton.Enable, True)
835 elif self._wizardState == 9:
836 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
837 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
839 self._wizardState = 10
840 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
841 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
842 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
843 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
844 w = profile.getPreferenceFloat('machine_width')
845 d = profile.getPreferenceFloat('machine_depth')
846 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
847 filamentArea = math.pi * filamentRadius * filamentRadius
848 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
852 'G1 Z2 F%d' % (feedZ),
854 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
855 'G1 Z0.3 F%d' % (feedZ)]
857 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
859 for i in xrange(0, 3):
861 eValue += (d - 2*dist) * ePerMM
862 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
863 eValue += (w - 2*dist) * ePerMM
864 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
865 eValue += (d - 2*dist) * ePerMM
866 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
867 eValue += (w - 2*dist) * ePerMM
868 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
870 gcodeList.append('M400')
871 self.comm.printGCode(gcodeList)
873 def mcStateChange(self, state):
874 if self.comm is None:
876 if self.comm.isOperational():
877 if self._wizardState == 0:
878 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
879 self.comm.sendCommand('M105')
880 self.comm.sendCommand('G28')
881 self._wizardState = 1
882 elif self._wizardState == 10 and not self.comm.isPrinting():
883 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
884 self.comm.sendCommand('G92 E0')
885 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
886 self.comm.sendCommand('M104 S0')
887 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
888 wx.CallAfter(self.infoBox.SetReadyIndicator)
889 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
890 wx.CallAfter(self.connectButton.Enable, True)
891 self._wizardState = 11
892 elif self.comm.isError():
893 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
895 def mcMessage(self, message):
898 def mcProgress(self, lineNr):
901 def mcZChange(self, newZ):
904 class bedLevelWizard(wx.wizard.Wizard):
906 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
908 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
909 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
911 self.mainPage = bedLevelWizardMain(self)
913 self.FitToPage(self.mainPage)
914 self.GetPageAreaSizer().Add(self.mainPage)
916 self.RunWizard(self.mainPage)
919 def OnPageChanging(self, e):
920 e.GetPage().StoreData()
922 def OnPageChanged(self, e):
923 if e.GetPage().AllowNext():
924 self.FindWindowById(wx.ID_FORWARD).Enable()
926 self.FindWindowById(wx.ID_FORWARD).Disable()
927 self.FindWindowById(wx.ID_BACKWARD).Disable()