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 bedLevelFirstPage(InfoPage):
723 def __init__(self, parent):
724 super(bedLevelFirstPage, self).__init__(parent, "Bed leveling wizard")
725 self.AddText('This wizard will help you in leveling your printer bed')
727 self.AddText('It will do the following steps')
728 self.AddText('* Move the printer head to each corner')
729 self.AddText(' and let you adjust the height of the bed to the nozzle')
730 self.AddText('* Print a line around the bed to check if it is level')
732 class bedLevelWizardMain(InfoPage):
733 def __init__(self, parent):
734 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
736 self.connectButton = self.AddButton('Connect to printer')
739 self.infoBox = self.AddInfoBox()
740 self.resumeButton = self.AddButton('Resume')
741 self.resumeButton.Enable(False)
743 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
744 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
746 def OnConnect(self, e = None):
747 if self.comm is not None:
751 wx.CallAfter(self.OnConnect)
753 self.connectButton.Enable(False)
754 self.comm = machineCom.MachineCom(callbackObject=self)
755 self.infoBox.SetBusy('Connecting to machine.')
756 self._wizardState = 0
761 def OnResume(self, e):
762 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
763 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
764 if self._wizardState == 2:
765 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
766 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
767 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
768 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
769 self.comm.sendCommand('M400')
770 self._wizardState = 3
771 elif self._wizardState == 4:
772 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
773 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
774 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 20, feedTravel))
775 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
776 self.comm.sendCommand('M400')
777 self._wizardState = 5
778 elif self._wizardState == 6:
779 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
780 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
781 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
782 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
783 self.comm.sendCommand('M400')
784 self._wizardState = 7
785 elif self._wizardState == 8:
786 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
787 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
788 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
789 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
790 self._wizardState = 9
791 self.resumeButton.Enable(False)
793 def mcLog(self, message):
794 print 'Log:', message
796 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
797 if self._wizardState == 1:
798 self._wizardState = 2
799 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
800 wx.CallAfter(self.resumeButton.Enable, True)
801 elif self._wizardState == 3:
802 self._wizardState = 4
803 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
804 wx.CallAfter(self.resumeButton.Enable, True)
805 elif self._wizardState == 5:
806 self._wizardState = 6
807 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
808 wx.CallAfter(self.resumeButton.Enable, True)
809 elif self._wizardState == 7:
810 self._wizardState = 8
811 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
812 wx.CallAfter(self.resumeButton.Enable, True)
813 elif self._wizardState == 9:
814 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
815 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
817 self._wizardState = 10
818 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
819 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
820 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
821 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
822 w = profile.getPreferenceFloat('machine_width')
823 d = profile.getPreferenceFloat('machine_depth')
824 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
825 filamentArea = math.pi * filamentRadius * filamentRadius
826 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
830 'G1 Z2 F%d' % (feedZ),
832 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
833 'G1 Z0.3 F%d' % (feedZ)]
834 eValue += (d - 10) * ePerMM
835 gcodeList.append('G1 X%d Y%d E%f F%d' % (5, d - 5, eValue, feedPrint))
836 eValue += (w - 10) * ePerMM
837 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - 5, d - 5, eValue, feedPrint))
838 eValue += (d - 10) * ePerMM
839 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - 5, 5, eValue, feedPrint))
840 eValue += (w - 10) * ePerMM
841 gcodeList.append('G1 X%d Y%d E%f F%d' % (5, 5, eValue, feedPrint))
843 gcodeList.append('G1 X%d Y%d E%f F%d' % (5.4, 5.4, eValue, feedTravel))
844 eValue += (d - 10.8) * ePerMM
845 gcodeList.append('G1 X%d Y%d E%f F%d' % (5.4, d - 5.4, eValue, feedPrint))
846 eValue += (w - 10.8) * ePerMM
847 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - 5.4, d - 5.4, eValue, feedPrint))
848 eValue += (d - 10.8) * ePerMM
849 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - 5.4, 5.4, eValue, feedPrint))
850 eValue += (w - 10.8) * ePerMM
851 gcodeList.append('G1 X%d Y%d E%f F%d' % (5.4, 5.4, eValue, feedPrint))
852 self.comm.printGCode(gcodeList)
854 def mcStateChange(self, state):
855 if self.comm is None:
857 if self.comm.isOperational():
858 if self._wizardState == 0:
859 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
860 self.comm.sendCommand('G28')
861 self._wizardState = 1
862 elif self._wizardState == 10 and not self.comm.isPrinting():
863 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
864 self.comm.sendCommand('M104 S0')
865 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe two squares on the bed should slightly touch each other.')
866 wx.CallAfter(self.infoBox.SetReadyIndicator)
867 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
868 self._wizardState = 11
869 elif self.comm.isError():
870 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
872 def mcMessage(self, message):
875 def mcProgress(self, lineNr):
878 def mcZChange(self, newZ):
881 class bedLevelWizard(wx.wizard.Wizard):
883 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
885 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
886 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
888 self.firstInfoPage = bedLevelFirstPage(self)
889 self.mainPage = bedLevelWizardMain(self)
891 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.mainPage)
893 self.FitToPage(self.firstInfoPage)
894 self.GetPageAreaSizer().Add(self.firstInfoPage)
896 self.RunWizard(self.firstInfoPage)
899 def OnPageChanging(self, e):
900 e.GetPage().StoreData()
902 def OnPageChanged(self, e):
903 if e.GetPage().AllowNext():
904 self.FindWindowById(wx.ID_FORWARD).Enable()
906 self.FindWindowById(wx.ID_FORWARD).Disable()
907 self.FindWindowById(wx.ID_BACKWARD).Disable()