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 != 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 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
470 def mcStateChange(self, state):
471 if self.comm is None:
473 if self.comm.isOperational():
474 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
475 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
476 elif self.comm.isError():
477 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
478 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
479 wx.CallAfter(self.endstopBitmap.Show, False)
480 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
481 wx.CallAfter(self.errorLogButton.Show, True)
482 wx.CallAfter(self.Layout)
484 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
486 def mcMessage(self, message):
487 if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message:
488 for data in message.split(' '):
490 tag, value = data.split(':', 2)
492 self.xMinStop = (value == 'H')
494 self.xMaxStop = (value == 'H')
496 self.yMinStop = (value == 'H')
498 self.yMaxStop = (value == 'H')
500 self.zMinStop = (value == 'H')
502 self.zMaxStop = (value == 'H')
503 self.comm.sendCommand('M119')
505 if self.checkupState == 3:
506 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
507 self.checkupState = 4
508 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
509 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
510 elif self.checkupState == 4:
511 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
512 self.checkupState = 5
513 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
514 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
515 elif self.checkupState == 5:
516 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
517 self.checkupState = 6
518 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
519 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
520 elif self.checkupState == 6:
521 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
522 self.checkupState = 7
523 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
524 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
525 elif self.checkupState == 7:
526 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
527 self.checkupState = 8
528 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
529 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
530 elif self.checkupState == 8:
531 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
532 self.checkupState = 9
533 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
534 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
535 elif self.checkupState == 9:
536 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
537 self.checkupState = 10
539 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
540 wx.CallAfter(self.infoBox.SetReadyIndicator)
541 wx.CallAfter(self.endstopBitmap.Show, False)
542 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
543 wx.CallAfter(self.OnSkipClick, None)
545 def mcProgress(self, lineNr):
548 def mcZChange(self, newZ):
552 class UltimakerCalibrationPage(InfoPage):
553 def __init__(self, parent):
554 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
556 self.AddText("Your Ultimaker requires some calibration.")
557 self.AddText("This calibration is needed for a proper extrusion amount.")
559 self.AddText("The following values are needed:")
560 self.AddText("* Diameter of filament")
561 self.AddText("* Number of steps per mm of filament extrusion")
563 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
565 self.AddText("First we need the diameter of your filament:")
566 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
568 "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.")
569 self.AddText("Note: This value can be changed later at any time.")
572 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
575 class UltimakerCalibrateStepsPerEPage(InfoPage):
576 def __init__(self, parent):
577 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
579 if profile.getPreference('steps_per_e') == '0':
580 profile.putPreference('steps_per_e', '865.888')
582 self.AddText("Calibrating the Steps Per E requires some manual actions.")
583 self.AddText("First remove any filament from your machine.")
584 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
585 self.AddText("We'll push the filament 100mm")
586 self.extrudeButton = self.AddButton("Extrude 100mm filament")
587 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
588 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
589 self.AddText("This results in the following steps per E:")
590 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
591 self.AddText("You can repeat these steps to get better calibration.")
594 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
595 self.heatButton = self.AddButton("Heatup for filament removal")
597 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
598 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
599 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
601 def OnSaveLengthClick(self, e):
602 currentEValue = float(self.stepsPerEInput.GetValue())
603 realExtrudeLength = float(self.lengthInput.GetValue())
604 newEValue = currentEValue * 100 / realExtrudeLength
605 self.stepsPerEInput.SetValue(str(newEValue))
606 self.lengthInput.SetValue("100")
608 def OnExtrudeClick(self, e):
609 threading.Thread(target=self.OnExtrudeRun).start()
611 def OnExtrudeRun(self):
612 self.heatButton.Enable(False)
613 self.extrudeButton.Enable(False)
614 currentEValue = float(self.stepsPerEInput.GetValue())
615 self.comm = machineCom.MachineCom()
616 if not self.comm.isOpen():
618 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
619 'Printer error', wx.OK | wx.ICON_INFORMATION)
620 self.heatButton.Enable(True)
621 self.extrudeButton.Enable(True)
624 line = self.comm.readline()
629 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
632 self.sendGCommand('M302') #Disable cold extrusion protection
633 self.sendGCommand("M92 E%f" % (currentEValue))
634 self.sendGCommand("G92 E0")
635 self.sendGCommand("G1 E100 F600")
638 self.extrudeButton.Enable()
639 self.heatButton.Enable()
641 def OnHeatClick(self, e):
642 threading.Thread(target=self.OnHeatRun).start()
645 self.heatButton.Enable(False)
646 self.extrudeButton.Enable(False)
647 self.comm = machineCom.MachineCom()
648 if not self.comm.isOpen():
650 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
651 'Printer error', wx.OK | wx.ICON_INFORMATION)
652 self.heatButton.Enable(True)
653 self.extrudeButton.Enable(True)
656 line = self.comm.readline()
658 self.heatButton.Enable(True)
659 self.extrudeButton.Enable(True)
663 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
666 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
668 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
669 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
670 self.sendGCommand('M104 S0')
673 self.heatButton.Enable(True)
674 self.extrudeButton.Enable(True)
676 def sendGCommand(self, cmd):
677 self.comm.sendCommand(cmd) #Disable cold extrusion protection
679 line = self.comm.readline()
682 if line.startswith('ok'):
686 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
689 class configWizard(wx.wizard.Wizard):
691 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
693 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
694 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
696 self.firstInfoPage = FirstInfoPage(self)
697 self.machineSelectPage = MachineSelectPage(self)
698 self.ultimakerSelectParts = SelectParts(self)
699 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
700 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
701 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
702 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
703 self.bedLevelPage = bedLevelWizardMain(self)
704 self.repRapInfoPage = RepRapInfoPage(self)
706 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
707 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
708 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
709 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
710 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
711 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
713 self.FitToPage(self.firstInfoPage)
714 self.GetPageAreaSizer().Add(self.firstInfoPage)
716 self.RunWizard(self.firstInfoPage)
719 def OnPageChanging(self, e):
720 e.GetPage().StoreData()
722 def OnPageChanged(self, e):
723 if e.GetPage().AllowNext():
724 self.FindWindowById(wx.ID_FORWARD).Enable()
726 self.FindWindowById(wx.ID_FORWARD).Disable()
727 self.FindWindowById(wx.ID_BACKWARD).Disable()
729 class bedLevelWizardMain(InfoPage):
730 def __init__(self, parent):
731 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
733 self.AddText('This wizard will help you in leveling your printer bed')
735 self.AddText('It will do the following steps')
736 self.AddText('* Move the printer head to each corner')
737 self.AddText(' and let you adjust the height of the bed to the nozzle')
738 self.AddText('* Print a line around the bed to check if it is level')
741 self.connectButton = self.AddButton('Connect to printer')
744 self.infoBox = self.AddInfoBox()
745 self.resumeButton = self.AddButton('Resume')
746 self.resumeButton.Enable(False)
748 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
749 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
751 def OnConnect(self, e = None):
752 if self.comm is not None:
756 wx.CallAfter(self.OnConnect)
758 self.connectButton.Enable(False)
759 self.comm = machineCom.MachineCom(callbackObject=self)
760 self.infoBox.SetBusy('Connecting to machine.')
761 self._wizardState = 0
766 def OnResume(self, e):
767 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
768 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
769 if self._wizardState == 2:
770 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
771 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
772 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
773 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
774 self.comm.sendCommand('M400')
775 self._wizardState = 3
776 elif self._wizardState == 4:
777 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
778 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
779 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 20, feedTravel))
780 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
781 self.comm.sendCommand('M400')
782 self._wizardState = 5
783 elif self._wizardState == 6:
784 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
785 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
786 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
787 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
788 self.comm.sendCommand('M400')
789 self._wizardState = 7
790 elif self._wizardState == 8:
791 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
792 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
793 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
794 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
795 self._wizardState = 9
796 self.resumeButton.Enable(False)
798 def mcLog(self, message):
799 print 'Log:', message
801 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
802 if self._wizardState == 1:
803 self._wizardState = 2
804 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
805 wx.CallAfter(self.resumeButton.Enable, True)
806 elif self._wizardState == 3:
807 self._wizardState = 4
808 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
809 wx.CallAfter(self.resumeButton.Enable, True)
810 elif self._wizardState == 5:
811 self._wizardState = 6
812 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
813 wx.CallAfter(self.resumeButton.Enable, True)
814 elif self._wizardState == 7:
815 self._wizardState = 8
816 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
817 wx.CallAfter(self.resumeButton.Enable, True)
818 elif self._wizardState == 9:
819 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
820 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
822 self._wizardState = 10
823 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
824 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
825 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
826 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
827 w = profile.getPreferenceFloat('machine_width')
828 d = profile.getPreferenceFloat('machine_depth')
829 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
830 filamentArea = math.pi * filamentRadius * filamentRadius
831 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
835 'G1 Z2 F%d' % (feedZ),
837 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
838 'G1 Z0.3 F%d' % (feedZ)]
840 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
842 for i in xrange(0, 3):
844 eValue += (d - 2*dist) * ePerMM
845 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
846 eValue += (w - 2*dist) * ePerMM
847 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
848 eValue += (d - 2*dist) * ePerMM
849 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
850 eValue += (w - 2*dist) * ePerMM
851 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
853 gcodeList.append('M400')
854 self.comm.printGCode(gcodeList)
856 def mcStateChange(self, state):
857 if self.comm is None:
859 if self.comm.isOperational():
860 if self._wizardState == 0:
861 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
862 self.comm.sendCommand('M105')
863 self.comm.sendCommand('G28')
864 self._wizardState = 1
865 elif self._wizardState == 10 and not self.comm.isPrinting():
866 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
867 self.comm.sendCommand('G92 E0')
868 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
869 self.comm.sendCommand('M104 S0')
870 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
871 wx.CallAfter(self.infoBox.SetReadyIndicator)
872 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
873 wx.CallAfter(self.connectButton.Enable, True)
874 self._wizardState = 11
875 elif self.comm.isError():
876 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
878 def mcMessage(self, message):
881 def mcProgress(self, lineNr):
884 def mcZChange(self, newZ):
887 class bedLevelWizard(wx.wizard.Wizard):
889 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
891 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
892 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
894 self.mainPage = bedLevelWizardMain(self)
896 self.FitToPage(self.mainPage)
897 self.GetPageAreaSizer().Add(self.mainPage)
899 self.RunWizard(self.mainPage)
902 def OnPageChanging(self, e):
903 e.GetPage().StoreData()
905 def OnPageChanged(self, e):
906 if e.GetPage().AllowNext():
907 self.FindWindowById(wx.ID_FORWARD).Enable()
909 self.FindWindowById(wx.ID_FORWARD).Disable()
910 self.FindWindowById(wx.ID_BACKWARD).Disable()