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')
246 profile.putPreference('machine_width', self.machineWidth.GetValue())
247 profile.putPreference('machine_depth', self.machineDepth.GetValue())
248 profile.putPreference('machine_height', self.machineHeight.GetValue())
249 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
250 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
251 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
254 class MachineSelectPage(InfoPage):
255 def __init__(self, parent):
256 super(MachineSelectPage, self).__init__(parent, "Select your machine")
257 self.AddText('What kind of machine do you have:')
259 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
260 self.UltimakerRadio.SetValue(True)
261 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
262 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
263 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
265 def OnUltimakerSelect(self, e):
266 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
268 def OnOtherSelect(self, e):
269 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
272 if self.UltimakerRadio.GetValue():
273 profile.putPreference('machine_width', '205')
274 profile.putPreference('machine_depth', '205')
275 profile.putPreference('machine_height', '200')
276 profile.putPreference('machine_type', 'ultimaker')
277 profile.putProfileSetting('nozzle_size', '0.4')
279 profile.putPreference('machine_width', '80')
280 profile.putPreference('machine_depth', '80')
281 profile.putPreference('machine_height', '60')
282 profile.putPreference('machine_type', 'reprap')
283 profile.putPreference('startMode', 'Normal')
284 profile.putProfileSetting('nozzle_size', '0.5')
285 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
288 class SelectParts(InfoPage):
289 def __init__(self, parent):
290 super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
291 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.')
293 self.springExtruder = self.AddCheckbox('Extruder drive upgrade')
294 self.heatedBed = self.AddCheckbox('Heated printer bed (self build)')
295 self.dualExtrusion = self.AddCheckbox('Dual extrusion (experimental)')
297 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 reliablity.')
298 self.AddText('This upgrade can be bought from the Ultimaker webshop shop\nor found on thingiverse as thing:26094')
299 self.springExtruder.SetValue(True)
302 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
303 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
304 if self.dualExtrusion.GetValue():
305 profile.putPreference('extruder_amount', '2')
306 if profile.getPreference('ultimaker_extruder_upgrade') == 'True':
307 profile.putProfileSetting('retraction_enable', 'True')
310 class FirmwareUpgradePage(InfoPage):
311 def __init__(self, parent):
312 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
314 '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.')
315 self.AddHiddenSeperator()
317 'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
318 self.AddHiddenSeperator()
320 '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.')
321 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
322 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
323 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
324 self.AddHiddenSeperator()
325 self.AddText('Do not upgrade to this firmware if:')
326 self.AddText('* You have an older machine based on ATMega1280')
327 self.AddText('* Have other changes in the firmware')
328 button = self.AddButton('Goto this page for a custom firmware')
329 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
334 def OnUpgradeClick(self, e):
335 if firmwareInstall.InstallFirmware():
336 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
338 def OnSkipClick(self, e):
339 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
341 def OnUrlClick(self, e):
342 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
345 class UltimakerCheckupPage(InfoPage):
346 def __init__(self, parent):
347 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
349 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
350 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
351 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
352 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
353 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
354 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
355 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
356 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
357 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
358 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
361 '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.')
362 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
363 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
364 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
366 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
367 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
368 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
370 self.infoBox = self.AddInfoBox()
371 self.machineState = self.AddText('')
372 self.temperatureLabel = self.AddText('')
373 self.errorLogButton = self.AddButton('Show error log')
374 self.errorLogButton.Show(False)
376 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
378 self.xMinStop = False
379 self.xMaxStop = False
380 self.yMinStop = False
381 self.yMaxStop = False
382 self.zMinStop = False
383 self.zMaxStop = False
385 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
388 if self.comm != None:
392 self.endstopBitmap.Show(False)
395 def OnSkipClick(self, e):
396 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
398 def OnCheckClick(self, e=None):
399 self.errorLogButton.Show(False)
400 if self.comm is not None:
404 wx.CallAfter(self.OnCheckClick)
406 self.infoBox.SetBusy('Connecting to machine.')
407 self.commState.SetBitmap(self.unknownBitmap)
408 self.tempState.SetBitmap(self.unknownBitmap)
409 self.stopState.SetBitmap(self.unknownBitmap)
410 self.checkupState = 0
411 self.comm = machineCom.MachineCom(callbackObject=self)
413 def OnErrorLog(self, e):
414 printWindow.LogWindow('\n'.join(self.comm.getLog()))
416 def mcLog(self, message):
419 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
420 if not self.comm.isOperational():
422 if self.checkupState == 0:
423 self.tempCheckTimeout = 20
425 self.checkupState = 1
426 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
427 self.comm.sendCommand('M104 S0')
428 self.comm.sendCommand('M104 S0')
430 self.startTemp = temp
431 self.checkupState = 2
432 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
433 self.comm.sendCommand('M104 S200')
434 self.comm.sendCommand('M104 S200')
435 elif self.checkupState == 1:
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 == 2:
443 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
444 if temp > self.startTemp + 40:
445 self.checkupState = 3
446 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
447 wx.CallAfter(self.endstopBitmap.Show, True)
448 wx.CallAfter(self.Layout)
449 self.comm.sendCommand('M104 S0')
450 self.comm.sendCommand('M104 S0')
451 self.comm.sendCommand('M119')
452 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
454 self.tempCheckTimeout -= 1
455 if self.tempCheckTimeout < 1:
456 self.checkupState = -1
457 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
458 wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
459 self.comm.sendCommand('M104 S0')
460 self.comm.sendCommand('M104 S0')
461 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
463 def mcStateChange(self, state):
464 if self.comm is None:
466 if self.comm.isOperational():
467 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
468 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
469 elif self.comm.isError():
470 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
471 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
472 wx.CallAfter(self.endstopBitmap.Show, False)
473 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
474 wx.CallAfter(self.errorLogButton.Show, True)
475 wx.CallAfter(self.Layout)
477 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
479 def mcMessage(self, message):
480 if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message:
481 for data in message.split(' '):
483 tag, value = data.split(':', 2)
485 self.xMinStop = (value == 'H')
487 self.xMaxStop = (value == 'H')
489 self.yMinStop = (value == 'H')
491 self.yMaxStop = (value == 'H')
493 self.zMinStop = (value == 'H')
495 self.zMaxStop = (value == 'H')
496 self.comm.sendCommand('M119')
498 if self.checkupState == 3:
499 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
500 self.checkupState = 4
501 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
502 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
503 elif self.checkupState == 4:
504 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
505 self.checkupState = 5
506 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
507 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
508 elif self.checkupState == 5:
509 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
510 self.checkupState = 6
511 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
512 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
513 elif self.checkupState == 6:
514 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
515 self.checkupState = 7
516 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
517 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
518 elif self.checkupState == 7:
519 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
520 self.checkupState = 8
521 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
522 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
523 elif self.checkupState == 8:
524 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
525 self.checkupState = 9
526 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
527 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
528 elif self.checkupState == 9:
529 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
530 self.checkupState = 10
532 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
533 wx.CallAfter(self.infoBox.SetReadyIndicator)
534 wx.CallAfter(self.endstopBitmap.Show, False)
535 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
536 wx.CallAfter(self.OnSkipClick, None)
538 def mcProgress(self, lineNr):
541 def mcZChange(self, newZ):
545 class UltimakerCalibrationPage(InfoPage):
546 def __init__(self, parent):
547 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
549 self.AddText("Your Ultimaker requires some calibration.")
550 self.AddText("This calibration is needed for a proper extrusion amount.")
552 self.AddText("The following values are needed:")
553 self.AddText("* Diameter of filament")
554 self.AddText("* Number of steps per mm of filament extrusion")
556 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
558 self.AddText("First we need the diameter of your filament:")
559 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
561 "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.")
562 self.AddText("Note: This value can be changed later at any time.")
565 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
568 class UltimakerCalibrateStepsPerEPage(InfoPage):
569 def __init__(self, parent):
570 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
572 if profile.getPreference('steps_per_e') == '0':
573 profile.putPreference('steps_per_e', '865.888')
575 self.AddText("Calibrating the Steps Per E requires some manual actions.")
576 self.AddText("First remove any filament from your machine.")
577 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
578 self.AddText("We'll push the filament 100mm")
579 self.extrudeButton = self.AddButton("Extrude 100mm filament")
580 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
581 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
582 self.AddText("This results in the following steps per E:")
583 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
584 self.AddText("You can repeat these steps to get better calibration.")
587 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
588 self.heatButton = self.AddButton("Heatup for filament removal")
590 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
591 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
592 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
594 def OnSaveLengthClick(self, e):
595 currentEValue = float(self.stepsPerEInput.GetValue())
596 realExtrudeLength = float(self.lengthInput.GetValue())
597 newEValue = currentEValue * 100 / realExtrudeLength
598 self.stepsPerEInput.SetValue(str(newEValue))
599 self.lengthInput.SetValue("100")
601 def OnExtrudeClick(self, e):
602 threading.Thread(target=self.OnExtrudeRun).start()
604 def OnExtrudeRun(self):
605 self.heatButton.Enable(False)
606 self.extrudeButton.Enable(False)
607 currentEValue = float(self.stepsPerEInput.GetValue())
608 self.comm = machineCom.MachineCom()
609 if not self.comm.isOpen():
611 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
612 'Printer error', wx.OK | wx.ICON_INFORMATION)
613 self.heatButton.Enable(True)
614 self.extrudeButton.Enable(True)
617 line = self.comm.readline()
622 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
625 self.sendGCommand('M302') #Disable cold extrusion protection
626 self.sendGCommand("M92 E%f" % (currentEValue))
627 self.sendGCommand("G92 E0")
628 self.sendGCommand("G1 E100 F600")
631 self.extrudeButton.Enable()
632 self.heatButton.Enable()
634 def OnHeatClick(self, e):
635 threading.Thread(target=self.OnHeatRun).start()
638 self.heatButton.Enable(False)
639 self.extrudeButton.Enable(False)
640 self.comm = machineCom.MachineCom()
641 if not self.comm.isOpen():
643 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
644 'Printer error', wx.OK | wx.ICON_INFORMATION)
645 self.heatButton.Enable(True)
646 self.extrudeButton.Enable(True)
649 line = self.comm.readline()
651 self.heatButton.Enable(True)
652 self.extrudeButton.Enable(True)
656 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
659 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
661 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
662 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
663 self.sendGCommand('M104 S0')
666 self.heatButton.Enable(True)
667 self.extrudeButton.Enable(True)
669 def sendGCommand(self, cmd):
670 self.comm.sendCommand(cmd) #Disable cold extrusion protection
672 line = self.comm.readline()
675 if line.startswith('ok'):
679 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
682 class configWizard(wx.wizard.Wizard):
684 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
686 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
687 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
689 self.firstInfoPage = FirstInfoPage(self)
690 self.machineSelectPage = MachineSelectPage(self)
691 self.ultimakerSelectParts = SelectParts(self)
692 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
693 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
694 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
695 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
696 self.bedLevelPage = bedLevelWizardMain(self)
697 self.repRapInfoPage = RepRapInfoPage(self)
699 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
700 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
701 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
702 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
703 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
704 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
706 self.FitToPage(self.firstInfoPage)
707 self.GetPageAreaSizer().Add(self.firstInfoPage)
709 self.RunWizard(self.firstInfoPage)
712 def OnPageChanging(self, e):
713 e.GetPage().StoreData()
715 def OnPageChanged(self, e):
716 if e.GetPage().AllowNext():
717 self.FindWindowById(wx.ID_FORWARD).Enable()
719 self.FindWindowById(wx.ID_FORWARD).Disable()
720 self.FindWindowById(wx.ID_BACKWARD).Disable()
722 class bedLevelWizardMain(InfoPage):
723 def __init__(self, parent):
724 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
726 self.AddText('This wizard will help you in leveling your printer bed')
728 self.AddText('It will do the following steps')
729 self.AddText('* Move the printer head to each corner')
730 self.AddText(' and let you adjust the height of the bed to the nozzle')
731 self.AddText('* Print a line around the bed to check if it is level')
734 self.connectButton = self.AddButton('Connect to printer')
737 self.infoBox = self.AddInfoBox()
738 self.resumeButton = self.AddButton('Resume')
739 self.resumeButton.Enable(False)
741 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
742 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
744 def OnConnect(self, e = None):
745 if self.comm is not None:
749 wx.CallAfter(self.OnConnect)
751 self.connectButton.Enable(False)
752 self.comm = machineCom.MachineCom(callbackObject=self)
753 self.infoBox.SetBusy('Connecting to machine.')
754 self._wizardState = 0
759 def OnResume(self, e):
760 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
761 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
762 if self._wizardState == 2:
763 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
764 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
765 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
766 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
767 self.comm.sendCommand('M400')
768 self._wizardState = 3
769 elif self._wizardState == 4:
770 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
771 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
772 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 20, feedTravel))
773 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
774 self.comm.sendCommand('M400')
775 self._wizardState = 5
776 elif self._wizardState == 6:
777 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front 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'), 20, feedTravel))
780 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
781 self.comm.sendCommand('M400')
782 self._wizardState = 7
783 elif self._wizardState == 8:
784 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
785 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
786 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
787 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
788 self._wizardState = 9
789 self.resumeButton.Enable(False)
791 def mcLog(self, message):
792 print 'Log:', message
794 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
795 if self._wizardState == 1:
796 self._wizardState = 2
797 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
798 wx.CallAfter(self.resumeButton.Enable, True)
799 elif self._wizardState == 3:
800 self._wizardState = 4
801 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
802 wx.CallAfter(self.resumeButton.Enable, True)
803 elif self._wizardState == 5:
804 self._wizardState = 6
805 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
806 wx.CallAfter(self.resumeButton.Enable, True)
807 elif self._wizardState == 7:
808 self._wizardState = 8
809 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
810 wx.CallAfter(self.resumeButton.Enable, True)
811 elif self._wizardState == 9:
812 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
813 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
815 self._wizardState = 10
816 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
817 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
818 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
819 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
820 w = profile.getPreferenceFloat('machine_width')
821 d = profile.getPreferenceFloat('machine_depth')
822 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
823 filamentArea = math.pi * filamentRadius * filamentRadius
824 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
828 'G1 Z2 F%d' % (feedZ),
830 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
831 'G1 Z0.3 F%d' % (feedZ)]
833 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
835 for i in xrange(0, 3):
837 eValue += (d - 2*dist) * ePerMM
838 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
839 eValue += (w - 2*dist) * ePerMM
840 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
841 eValue += (d - 2*dist) * ePerMM
842 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
843 eValue += (w - 2*dist) * ePerMM
844 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
846 gcodeList.append('M400')
847 self.comm.printGCode(gcodeList)
849 def mcStateChange(self, state):
850 if self.comm is None:
852 if self.comm.isOperational():
853 if self._wizardState == 0:
854 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
855 self.comm.sendCommand('G28')
856 self._wizardState = 1
857 elif self._wizardState == 10 and not self.comm.isPrinting():
858 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
859 self.comm.sendCommand('G92 E0')
860 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
861 self.comm.sendCommand('M104 S0')
862 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
863 wx.CallAfter(self.infoBox.SetReadyIndicator)
864 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
865 wx.CallAfter(self.connectButton.Enable, True)
866 self._wizardState = 11
867 elif self.comm.isError():
868 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
870 def mcMessage(self, message):
873 def mcProgress(self, lineNr):
876 def mcZChange(self, newZ):
879 class bedLevelWizard(wx.wizard.Wizard):
881 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
883 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
884 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
886 self.mainPage = bedLevelWizardMain(self)
888 self.FitToPage(self.mainPage)
889 self.GetPageAreaSizer().Add(self.mainPage)
891 self.RunWizard(self.mainPage)
894 def OnPageChanging(self, e):
895 e.GetPage().StoreData()
897 def OnPageChanged(self, e):
898 if e.GetPage().AllowNext():
899 self.FindWindowById(wx.ID_FORWARD).Enable()
901 self.FindWindowById(wx.ID_FORWARD).Disable()
902 self.FindWindowById(wx.ID_BACKWARD).Disable()