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 self.AddText('The collection of anonymous usage information helps with the continued improvement of Cura.')
268 self.AddText('This does NOT submit your models online nor gathers any privacy related information.')
269 self.SubmitUserStats = self.AddCheckbox('Submit anonymous usage information:')
270 self.AddText('For full details see: http://wiki.ultimaker.com/Cura:stats')
271 self.SubmitUserStats.SetValue(True)
273 def OnUltimakerSelect(self, e):
274 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
276 def OnOtherSelect(self, e):
277 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
280 if self.UltimakerRadio.GetValue():
281 profile.putPreference('machine_width', '205')
282 profile.putPreference('machine_depth', '205')
283 profile.putPreference('machine_height', '200')
284 profile.putPreference('machine_type', 'ultimaker')
285 profile.putPreference('machine_center_is_zero', 'False')
286 profile.putProfileSetting('nozzle_size', '0.4')
288 profile.putPreference('machine_width', '80')
289 profile.putPreference('machine_depth', '80')
290 profile.putPreference('machine_height', '60')
291 profile.putPreference('machine_type', 'reprap')
292 profile.putPreference('startMode', 'Normal')
293 profile.putProfileSetting('nozzle_size', '0.5')
294 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
295 if self.SubmitUserStats.GetValue():
296 profile.putPreference('submit_slice_information', 'True')
298 profile.putPreference('submit_slice_information', 'False')
300 class SelectParts(InfoPage):
301 def __init__(self, parent):
302 super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
303 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.')
305 self.springExtruder = self.AddCheckbox('Extruder drive upgrade')
306 self.heatedBed = self.AddCheckbox('Heated printer bed (self built)')
307 self.dualExtrusion = self.AddCheckbox('Dual extrusion (experimental)')
309 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.')
310 self.AddText('This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094')
311 self.springExtruder.SetValue(True)
314 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
315 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
316 if self.dualExtrusion.GetValue():
317 profile.putPreference('extruder_amount', '2')
319 profile.putPreference('extruder_amount', '1')
320 if profile.getPreference('ultimaker_extruder_upgrade') == 'True':
321 profile.putProfileSetting('retraction_enable', 'True')
323 profile.putProfileSetting('retraction_enable', 'False')
326 class FirmwareUpgradePage(InfoPage):
327 def __init__(self, parent):
328 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
330 '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.')
331 self.AddHiddenSeperator()
333 'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
334 self.AddHiddenSeperator()
336 '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.')
337 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
338 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
339 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
340 self.AddHiddenSeperator()
341 self.AddText('Do not upgrade to this firmware if:')
342 self.AddText('* You have an older machine based on ATMega1280')
343 self.AddText('* Have other changes in the firmware')
344 button = self.AddButton('Goto this page for a custom firmware')
345 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
350 def OnUpgradeClick(self, e):
351 if firmwareInstall.InstallFirmware():
352 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
354 def OnSkipClick(self, e):
355 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
357 def OnUrlClick(self, e):
358 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
361 class UltimakerCheckupPage(InfoPage):
362 def __init__(self, parent):
363 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
365 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
366 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
367 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
368 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
369 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
370 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
371 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
372 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
373 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
374 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
377 '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.')
378 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
379 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
380 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
382 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
383 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
384 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
386 self.infoBox = self.AddInfoBox()
387 self.machineState = self.AddText('')
388 self.temperatureLabel = self.AddText('')
389 self.errorLogButton = self.AddButton('Show error log')
390 self.errorLogButton.Show(False)
392 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
394 self.xMinStop = False
395 self.xMaxStop = False
396 self.yMinStop = False
397 self.yMaxStop = False
398 self.zMinStop = False
399 self.zMaxStop = False
401 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
404 if self.comm is not None:
408 self.endstopBitmap.Show(False)
411 def OnSkipClick(self, e):
412 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
414 def OnCheckClick(self, e=None):
415 self.errorLogButton.Show(False)
416 if self.comm is not None:
420 wx.CallAfter(self.OnCheckClick)
422 self.infoBox.SetBusy('Connecting to machine.')
423 self.commState.SetBitmap(self.unknownBitmap)
424 self.tempState.SetBitmap(self.unknownBitmap)
425 self.stopState.SetBitmap(self.unknownBitmap)
426 self.checkupState = 0
427 self.comm = machineCom.MachineCom(callbackObject=self)
429 def OnErrorLog(self, e):
430 printWindow.LogWindow('\n'.join(self.comm.getLog()))
432 def mcLog(self, message):
435 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
436 if not self.comm.isOperational():
438 if self.checkupState == 0:
439 self.tempCheckTimeout = 20
441 self.checkupState = 1
442 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
443 self.comm.sendCommand('M104 S0')
444 self.comm.sendCommand('M104 S0')
446 self.startTemp = temp
447 self.checkupState = 2
448 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
449 self.comm.sendCommand('M104 S200')
450 self.comm.sendCommand('M104 S200')
451 elif self.checkupState == 1:
453 self.startTemp = temp
454 self.checkupState = 2
455 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
456 self.comm.sendCommand('M104 S200')
457 self.comm.sendCommand('M104 S200')
458 elif self.checkupState == 2:
459 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
460 if temp > self.startTemp + 40:
461 self.checkupState = 3
462 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
463 wx.CallAfter(self.endstopBitmap.Show, True)
464 wx.CallAfter(self.Layout)
465 self.comm.sendCommand('M104 S0')
466 self.comm.sendCommand('M104 S0')
467 self.comm.sendCommand('M119')
468 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
470 self.tempCheckTimeout -= 1
471 if self.tempCheckTimeout < 1:
472 self.checkupState = -1
473 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
474 wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
475 self.comm.sendCommand('M104 S0')
476 self.comm.sendCommand('M104 S0')
477 elif self.checkupState >= 3 and self.checkupState < 10:
478 self.comm.sendCommand('M119')
479 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
481 def mcStateChange(self, state):
482 if self.comm is None:
484 if self.comm.isOperational():
485 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
486 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
487 elif self.comm.isError():
488 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
489 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
490 wx.CallAfter(self.endstopBitmap.Show, False)
491 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
492 wx.CallAfter(self.errorLogButton.Show, True)
493 wx.CallAfter(self.Layout)
495 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
497 def mcMessage(self, message):
498 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
499 for data in message.split(' '):
501 tag, value = data.split(':', 1)
503 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
505 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
507 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
509 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
511 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
513 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
515 tag, value = map(str.strip, message.split(':', 1))
517 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
519 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
521 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
523 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
525 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
527 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
528 if 'z_max' in message:
529 self.comm.sendCommand('M119')
531 if self.checkupState == 3:
532 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
533 self.checkupState = 4
534 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
535 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
536 elif self.checkupState == 4:
537 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
538 self.checkupState = 5
539 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
540 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
541 elif self.checkupState == 5:
542 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
543 self.checkupState = 6
544 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
545 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
546 elif self.checkupState == 6:
547 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
548 self.checkupState = 7
549 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
550 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
551 elif self.checkupState == 7:
552 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
553 self.checkupState = 8
554 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
555 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
556 elif self.checkupState == 8:
557 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
558 self.checkupState = 9
559 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
560 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
561 elif self.checkupState == 9:
562 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
563 self.checkupState = 10
565 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
566 wx.CallAfter(self.infoBox.SetReadyIndicator)
567 wx.CallAfter(self.endstopBitmap.Show, False)
568 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
569 wx.CallAfter(self.OnSkipClick, None)
571 def mcProgress(self, lineNr):
574 def mcZChange(self, newZ):
578 class UltimakerCalibrationPage(InfoPage):
579 def __init__(self, parent):
580 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
582 self.AddText("Your Ultimaker requires some calibration.")
583 self.AddText("This calibration is needed for a proper extrusion amount.")
585 self.AddText("The following values are needed:")
586 self.AddText("* Diameter of filament")
587 self.AddText("* Number of steps per mm of filament extrusion")
589 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
591 self.AddText("First we need the diameter of your filament:")
592 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
594 "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.")
595 self.AddText("Note: This value can be changed later at any time.")
598 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
601 class UltimakerCalibrateStepsPerEPage(InfoPage):
602 def __init__(self, parent):
603 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
605 if profile.getPreference('steps_per_e') == '0':
606 profile.putPreference('steps_per_e', '865.888')
608 self.AddText("Calibrating the Steps Per E requires some manual actions.")
609 self.AddText("First remove any filament from your machine.")
610 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
611 self.AddText("We'll push the filament 100mm")
612 self.extrudeButton = self.AddButton("Extrude 100mm filament")
613 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
614 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
615 self.AddText("This results in the following steps per E:")
616 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
617 self.AddText("You can repeat these steps to get better calibration.")
620 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
621 self.heatButton = self.AddButton("Heatup for filament removal")
623 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
624 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
625 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
627 def OnSaveLengthClick(self, e):
628 currentEValue = float(self.stepsPerEInput.GetValue())
629 realExtrudeLength = float(self.lengthInput.GetValue())
630 newEValue = currentEValue * 100 / realExtrudeLength
631 self.stepsPerEInput.SetValue(str(newEValue))
632 self.lengthInput.SetValue("100")
634 def OnExtrudeClick(self, e):
635 threading.Thread(target=self.OnExtrudeRun).start()
637 def OnExtrudeRun(self):
638 self.heatButton.Enable(False)
639 self.extrudeButton.Enable(False)
640 currentEValue = float(self.stepsPerEInput.GetValue())
641 self.comm = machineCom.MachineCom()
642 if not self.comm.isOpen():
644 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
645 'Printer error', wx.OK | wx.ICON_INFORMATION)
646 self.heatButton.Enable(True)
647 self.extrudeButton.Enable(True)
650 line = self.comm.readline()
655 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
658 self.sendGCommand('M302') #Disable cold extrusion protection
659 self.sendGCommand("M92 E%f" % (currentEValue))
660 self.sendGCommand("G92 E0")
661 self.sendGCommand("G1 E100 F600")
664 self.extrudeButton.Enable()
665 self.heatButton.Enable()
667 def OnHeatClick(self, e):
668 threading.Thread(target=self.OnHeatRun).start()
671 self.heatButton.Enable(False)
672 self.extrudeButton.Enable(False)
673 self.comm = machineCom.MachineCom()
674 if not self.comm.isOpen():
676 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
677 'Printer error', wx.OK | wx.ICON_INFORMATION)
678 self.heatButton.Enable(True)
679 self.extrudeButton.Enable(True)
682 line = self.comm.readline()
684 self.heatButton.Enable(True)
685 self.extrudeButton.Enable(True)
689 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
692 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
694 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
695 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
696 self.sendGCommand('M104 S0')
699 self.heatButton.Enable(True)
700 self.extrudeButton.Enable(True)
702 def sendGCommand(self, cmd):
703 self.comm.sendCommand(cmd) #Disable cold extrusion protection
705 line = self.comm.readline()
708 if line.startswith('ok'):
712 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
715 class configWizard(wx.wizard.Wizard):
717 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
719 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
720 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
722 self.firstInfoPage = FirstInfoPage(self)
723 self.machineSelectPage = MachineSelectPage(self)
724 self.ultimakerSelectParts = SelectParts(self)
725 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
726 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
727 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
728 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
729 self.bedLevelPage = bedLevelWizardMain(self)
730 self.repRapInfoPage = RepRapInfoPage(self)
732 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
733 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
734 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
735 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
736 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
737 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
739 self.FitToPage(self.firstInfoPage)
740 self.GetPageAreaSizer().Add(self.firstInfoPage)
742 self.RunWizard(self.firstInfoPage)
745 def OnPageChanging(self, e):
746 e.GetPage().StoreData()
748 def OnPageChanged(self, e):
749 if e.GetPage().AllowNext():
750 self.FindWindowById(wx.ID_FORWARD).Enable()
752 self.FindWindowById(wx.ID_FORWARD).Disable()
753 self.FindWindowById(wx.ID_BACKWARD).Disable()
755 class bedLevelWizardMain(InfoPage):
756 def __init__(self, parent):
757 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
759 self.AddText('This wizard will help you in leveling your printer bed')
761 self.AddText('It will do the following steps')
762 self.AddText('* Move the printer head to each corner')
763 self.AddText(' and let you adjust the height of the bed to the nozzle')
764 self.AddText('* Print a line around the bed to check if it is level')
767 self.connectButton = self.AddButton('Connect to printer')
770 self.infoBox = self.AddInfoBox()
771 self.resumeButton = self.AddButton('Resume')
772 self.resumeButton.Enable(False)
774 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
775 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
777 def OnConnect(self, e = None):
778 if self.comm is not None:
782 wx.CallAfter(self.OnConnect)
784 self.connectButton.Enable(False)
785 self.comm = machineCom.MachineCom(callbackObject=self)
786 self.infoBox.SetBusy('Connecting to machine.')
787 self._wizardState = 0
792 def OnResume(self, e):
793 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
794 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
795 if self._wizardState == 2:
796 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
797 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
798 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
799 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
800 self.comm.sendCommand('M400')
801 self._wizardState = 3
802 elif self._wizardState == 4:
803 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
804 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
805 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 25, feedTravel))
806 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
807 self.comm.sendCommand('M400')
808 self._wizardState = 5
809 elif self._wizardState == 6:
810 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
811 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
812 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
813 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
814 self.comm.sendCommand('M400')
815 self._wizardState = 7
816 elif self._wizardState == 8:
817 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
818 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
819 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
820 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
821 self._wizardState = 9
822 self.resumeButton.Enable(False)
824 def mcLog(self, message):
825 print 'Log:', message
827 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
828 if self._wizardState == 1:
829 self._wizardState = 2
830 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
831 wx.CallAfter(self.resumeButton.Enable, True)
832 elif self._wizardState == 3:
833 self._wizardState = 4
834 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
835 wx.CallAfter(self.resumeButton.Enable, True)
836 elif self._wizardState == 5:
837 self._wizardState = 6
838 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
839 wx.CallAfter(self.resumeButton.Enable, True)
840 elif self._wizardState == 7:
841 self._wizardState = 8
842 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
843 wx.CallAfter(self.resumeButton.Enable, True)
844 elif self._wizardState == 9:
845 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
846 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
848 self._wizardState = 10
849 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
850 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
851 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
852 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
853 w = profile.getPreferenceFloat('machine_width')
854 d = profile.getPreferenceFloat('machine_depth')
855 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
856 filamentArea = math.pi * filamentRadius * filamentRadius
857 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
861 'G1 Z2 F%d' % (feedZ),
863 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
864 'G1 Z0.3 F%d' % (feedZ)]
866 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
868 for i in xrange(0, 3):
870 eValue += (d - 2*dist) * ePerMM
871 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
872 eValue += (w - 2*dist) * ePerMM
873 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
874 eValue += (d - 2*dist) * ePerMM
875 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
876 eValue += (w - 2*dist) * ePerMM
877 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
879 gcodeList.append('M400')
880 self.comm.printGCode(gcodeList)
882 def mcStateChange(self, state):
883 if self.comm is None:
885 if self.comm.isOperational():
886 if self._wizardState == 0:
887 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
888 self.comm.sendCommand('M105')
889 self.comm.sendCommand('G28')
890 self._wizardState = 1
891 elif self._wizardState == 10 and not self.comm.isPrinting():
892 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
893 self.comm.sendCommand('G92 E0')
894 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
895 self.comm.sendCommand('M104 S0')
896 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
897 wx.CallAfter(self.infoBox.SetReadyIndicator)
898 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
899 wx.CallAfter(self.connectButton.Enable, True)
900 self._wizardState = 11
901 elif self.comm.isError():
902 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
904 def mcMessage(self, message):
907 def mcProgress(self, lineNr):
910 def mcZChange(self, newZ):
913 class bedLevelWizard(wx.wizard.Wizard):
915 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
917 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
918 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
920 self.mainPage = bedLevelWizardMain(self)
922 self.FitToPage(self.mainPage)
923 self.GetPageAreaSizer().Add(self.mainPage)
925 self.RunWizard(self.mainPage)
928 def OnPageChanging(self, e):
929 e.GetPage().StoreData()
931 def OnPageChanged(self, e):
932 if e.GetPage().AllowNext():
933 self.FindWindowById(wx.ID_FORWARD).Enable()
935 self.FindWindowById(wx.ID_FORWARD).Disable()
936 self.FindWindowById(wx.ID_BACKWARD).Disable()