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()))
254 profile.putPreference('extruder_head_size_min_x', '0')
255 profile.putPreference('extruder_head_size_min_y', '0')
256 profile.putPreference('extruder_head_size_max_x', '0')
257 profile.putPreference('extruder_head_size_max_y', '0')
258 profile.putPreference('extruder_head_size_height', '0')
261 class MachineSelectPage(InfoPage):
262 def __init__(self, parent):
263 super(MachineSelectPage, self).__init__(parent, "Select your machine")
264 self.AddText('What kind of machine do you have:')
266 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
267 self.UltimakerRadio.SetValue(True)
268 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
269 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
270 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
272 self.AddText('The collection of anonymous usage information helps with the continued improvement of Cura.')
273 self.AddText('This does NOT submit your models online nor gathers any privacy related information.')
274 self.SubmitUserStats = self.AddCheckbox('Submit anonymous usage information:')
275 self.AddText('For full details see: http://wiki.ultimaker.com/Cura:stats')
276 self.SubmitUserStats.SetValue(True)
278 def OnUltimakerSelect(self, e):
279 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
281 def OnOtherSelect(self, e):
282 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
285 if self.UltimakerRadio.GetValue():
286 profile.putPreference('machine_width', '205')
287 profile.putPreference('machine_depth', '205')
288 profile.putPreference('machine_height', '200')
289 profile.putPreference('machine_type', 'ultimaker')
290 profile.putPreference('machine_center_is_zero', 'False')
291 profile.putProfileSetting('nozzle_size', '0.4')
292 profile.putPreference('extruder_head_size_min_x', '75.0')
293 profile.putPreference('extruder_head_size_min_y', '18.0')
294 profile.putPreference('extruder_head_size_max_x', '18.0')
295 profile.putPreference('extruder_head_size_max_y', '35.0')
296 profile.putPreference('extruder_head_size_height', '60.0')
298 profile.putPreference('machine_width', '80')
299 profile.putPreference('machine_depth', '80')
300 profile.putPreference('machine_height', '60')
301 profile.putPreference('machine_type', 'reprap')
302 profile.putPreference('startMode', 'Normal')
303 profile.putProfileSetting('nozzle_size', '0.5')
304 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
305 if self.SubmitUserStats.GetValue():
306 profile.putPreference('submit_slice_information', 'True')
308 profile.putPreference('submit_slice_information', 'False')
310 class SelectParts(InfoPage):
311 def __init__(self, parent):
312 super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
313 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.')
315 self.springExtruder = self.AddCheckbox('Extruder drive upgrade')
316 self.heatedBed = self.AddCheckbox('Heated printer bed (self built)')
317 self.dualExtrusion = self.AddCheckbox('Dual extrusion (experimental)')
319 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.')
320 self.AddText('This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094')
321 self.springExtruder.SetValue(True)
324 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
325 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
326 if self.dualExtrusion.GetValue():
327 profile.putPreference('extruder_amount', '2')
329 profile.putPreference('extruder_amount', '1')
330 if profile.getPreference('ultimaker_extruder_upgrade') == 'True':
331 profile.putProfileSetting('retraction_enable', 'True')
333 profile.putProfileSetting('retraction_enable', 'False')
336 class FirmwareUpgradePage(InfoPage):
337 def __init__(self, parent):
338 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
340 '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.')
341 self.AddHiddenSeperator()
343 'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
344 self.AddHiddenSeperator()
346 '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.')
347 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
348 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
349 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
350 self.AddHiddenSeperator()
351 self.AddText('Do not upgrade to this firmware if:')
352 self.AddText('* You have an older machine based on ATMega1280')
353 self.AddText('* Have other changes in the firmware')
354 button = self.AddButton('Goto this page for a custom firmware')
355 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
360 def OnUpgradeClick(self, e):
361 if firmwareInstall.InstallFirmware():
362 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
364 def OnSkipClick(self, e):
365 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
366 self.GetParent().ShowPage(self.GetNext())
368 def OnUrlClick(self, e):
369 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
372 class UltimakerCheckupPage(InfoPage):
373 def __init__(self, parent):
374 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
376 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
377 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
378 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
379 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
380 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
381 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
382 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
383 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
384 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
385 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
388 '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.')
389 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
390 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
391 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
393 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
394 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
395 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
397 self.infoBox = self.AddInfoBox()
398 self.machineState = self.AddText('')
399 self.temperatureLabel = self.AddText('')
400 self.errorLogButton = self.AddButton('Show error log')
401 self.errorLogButton.Show(False)
403 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
405 self.xMinStop = False
406 self.xMaxStop = False
407 self.yMinStop = False
408 self.yMaxStop = False
409 self.zMinStop = False
410 self.zMaxStop = False
412 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
415 if self.comm is not None:
419 self.endstopBitmap.Show(False)
422 def OnSkipClick(self, e):
423 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
424 self.GetParent().ShowPage(self.GetNext())
426 def OnCheckClick(self, e=None):
427 self.errorLogButton.Show(False)
428 if self.comm is not None:
432 wx.CallAfter(self.OnCheckClick)
434 self.infoBox.SetBusy('Connecting to machine.')
435 self.commState.SetBitmap(self.unknownBitmap)
436 self.tempState.SetBitmap(self.unknownBitmap)
437 self.stopState.SetBitmap(self.unknownBitmap)
438 self.checkupState = 0
439 self.comm = machineCom.MachineCom(callbackObject=self)
441 def OnErrorLog(self, e):
442 printWindow.LogWindow('\n'.join(self.comm.getLog()))
444 def mcLog(self, message):
447 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
448 if not self.comm.isOperational():
450 if self.checkupState == 0:
451 self.tempCheckTimeout = 20
453 self.checkupState = 1
454 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
455 self.comm.sendCommand('M104 S0')
456 self.comm.sendCommand('M104 S0')
458 self.startTemp = temp
459 self.checkupState = 2
460 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
461 self.comm.sendCommand('M104 S200')
462 self.comm.sendCommand('M104 S200')
463 elif self.checkupState == 1:
465 self.startTemp = temp
466 self.checkupState = 2
467 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
468 self.comm.sendCommand('M104 S200')
469 self.comm.sendCommand('M104 S200')
470 elif self.checkupState == 2:
471 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
472 if temp > self.startTemp + 40:
473 self.checkupState = 3
474 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
475 wx.CallAfter(self.endstopBitmap.Show, True)
476 wx.CallAfter(self.Layout)
477 self.comm.sendCommand('M104 S0')
478 self.comm.sendCommand('M104 S0')
479 self.comm.sendCommand('M119')
480 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
482 self.tempCheckTimeout -= 1
483 if self.tempCheckTimeout < 1:
484 self.checkupState = -1
485 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
486 wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
487 self.comm.sendCommand('M104 S0')
488 self.comm.sendCommand('M104 S0')
489 elif self.checkupState >= 3 and self.checkupState < 10:
490 self.comm.sendCommand('M119')
491 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
493 def mcStateChange(self, state):
494 if self.comm is None:
496 if self.comm.isOperational():
497 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
498 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
499 elif self.comm.isError():
500 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
501 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
502 wx.CallAfter(self.endstopBitmap.Show, False)
503 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
504 wx.CallAfter(self.errorLogButton.Show, True)
505 wx.CallAfter(self.Layout)
507 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
509 def mcMessage(self, message):
510 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
511 for data in message.split(' '):
513 tag, value = data.split(':', 1)
515 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
517 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
519 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
521 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
523 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
525 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
527 tag, value = map(str.strip, message.split(':', 1))
529 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
531 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
533 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
535 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
537 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
539 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
540 if 'z_max' in message:
541 self.comm.sendCommand('M119')
543 if self.checkupState == 3:
544 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
545 self.checkupState = 4
546 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
547 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
548 elif self.checkupState == 4:
549 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
550 self.checkupState = 5
551 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
552 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
553 elif self.checkupState == 5:
554 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
555 self.checkupState = 6
556 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
557 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
558 elif self.checkupState == 6:
559 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
560 self.checkupState = 7
561 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
562 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
563 elif self.checkupState == 7:
564 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
565 self.checkupState = 8
566 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
567 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
568 elif self.checkupState == 8:
569 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
570 self.checkupState = 9
571 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
572 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
573 elif self.checkupState == 9:
574 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
575 self.checkupState = 10
577 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
578 wx.CallAfter(self.infoBox.SetReadyIndicator)
579 wx.CallAfter(self.endstopBitmap.Show, False)
580 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
581 wx.CallAfter(self.OnSkipClick, None)
583 def mcProgress(self, lineNr):
586 def mcZChange(self, newZ):
590 class UltimakerCalibrationPage(InfoPage):
591 def __init__(self, parent):
592 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
594 self.AddText("Your Ultimaker requires some calibration.")
595 self.AddText("This calibration is needed for a proper extrusion amount.")
597 self.AddText("The following values are needed:")
598 self.AddText("* Diameter of filament")
599 self.AddText("* Number of steps per mm of filament extrusion")
601 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
603 self.AddText("First we need the diameter of your filament:")
604 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
606 "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.")
607 self.AddText("Note: This value can be changed later at any time.")
610 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
613 class UltimakerCalibrateStepsPerEPage(InfoPage):
614 def __init__(self, parent):
615 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
617 #if profile.getPreference('steps_per_e') == '0':
618 # profile.putPreference('steps_per_e', '865.888')
620 self.AddText("Calibrating the Steps Per E requires some manual actions.")
621 self.AddText("First remove any filament from your machine.")
622 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
623 self.AddText("We'll push the filament 100mm")
624 self.extrudeButton = self.AddButton("Extrude 100mm filament")
625 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
626 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
627 self.AddText("This results in the following steps per E:")
628 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
629 self.AddText("You can repeat these steps to get better calibration.")
632 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
633 self.heatButton = self.AddButton("Heatup for filament removal")
635 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
636 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
637 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
639 def OnSaveLengthClick(self, e):
640 currentEValue = float(self.stepsPerEInput.GetValue())
641 realExtrudeLength = float(self.lengthInput.GetValue())
642 newEValue = currentEValue * 100 / realExtrudeLength
643 self.stepsPerEInput.SetValue(str(newEValue))
644 self.lengthInput.SetValue("100")
646 def OnExtrudeClick(self, e):
647 threading.Thread(target=self.OnExtrudeRun).start()
649 def OnExtrudeRun(self):
650 self.heatButton.Enable(False)
651 self.extrudeButton.Enable(False)
652 currentEValue = float(self.stepsPerEInput.GetValue())
653 self.comm = machineCom.MachineCom()
654 if not self.comm.isOpen():
656 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
657 'Printer error', wx.OK | wx.ICON_INFORMATION)
658 self.heatButton.Enable(True)
659 self.extrudeButton.Enable(True)
662 line = self.comm.readline()
667 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
670 self.sendGCommand('M302') #Disable cold extrusion protection
671 self.sendGCommand("M92 E%f" % (currentEValue))
672 self.sendGCommand("G92 E0")
673 self.sendGCommand("G1 E100 F600")
676 self.extrudeButton.Enable()
677 self.heatButton.Enable()
679 def OnHeatClick(self, e):
680 threading.Thread(target=self.OnHeatRun).start()
683 self.heatButton.Enable(False)
684 self.extrudeButton.Enable(False)
685 self.comm = machineCom.MachineCom()
686 if not self.comm.isOpen():
688 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
689 'Printer error', wx.OK | wx.ICON_INFORMATION)
690 self.heatButton.Enable(True)
691 self.extrudeButton.Enable(True)
694 line = self.comm.readline()
696 self.heatButton.Enable(True)
697 self.extrudeButton.Enable(True)
701 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
704 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
706 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
707 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
708 self.sendGCommand('M104 S0')
711 self.heatButton.Enable(True)
712 self.extrudeButton.Enable(True)
714 def sendGCommand(self, cmd):
715 self.comm.sendCommand(cmd) #Disable cold extrusion protection
717 line = self.comm.readline()
720 if line.startswith('ok'):
724 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
727 class configWizard(wx.wizard.Wizard):
729 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
731 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
732 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
734 self.firstInfoPage = FirstInfoPage(self)
735 self.machineSelectPage = MachineSelectPage(self)
736 self.ultimakerSelectParts = SelectParts(self)
737 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
738 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
739 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
740 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
741 self.bedLevelPage = bedLevelWizardMain(self)
742 self.repRapInfoPage = RepRapInfoPage(self)
744 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
745 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
746 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
747 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
748 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
749 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
751 self.FitToPage(self.firstInfoPage)
752 self.GetPageAreaSizer().Add(self.firstInfoPage)
754 self.RunWizard(self.firstInfoPage)
757 def OnPageChanging(self, e):
758 e.GetPage().StoreData()
760 def OnPageChanged(self, e):
761 if e.GetPage().AllowNext():
762 self.FindWindowById(wx.ID_FORWARD).Enable()
764 self.FindWindowById(wx.ID_FORWARD).Disable()
765 self.FindWindowById(wx.ID_BACKWARD).Disable()
767 class bedLevelWizardMain(InfoPage):
768 def __init__(self, parent):
769 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
771 self.AddText('This wizard will help you in leveling your printer bed')
773 self.AddText('It will do the following steps')
774 self.AddText('* Move the printer head to each corner')
775 self.AddText(' and let you adjust the height of the bed to the nozzle')
776 self.AddText('* Print a line around the bed to check if it is level')
779 self.connectButton = self.AddButton('Connect to printer')
782 self.infoBox = self.AddInfoBox()
783 self.resumeButton = self.AddButton('Resume')
784 self.resumeButton.Enable(False)
786 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
787 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
789 def OnConnect(self, e = None):
790 if self.comm is not None:
794 wx.CallAfter(self.OnConnect)
796 self.connectButton.Enable(False)
797 self.comm = machineCom.MachineCom(callbackObject=self)
798 self.infoBox.SetBusy('Connecting to machine.')
799 self._wizardState = 0
804 def OnResume(self, e):
805 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
806 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
807 if self._wizardState == 2:
808 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
809 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
810 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
811 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
812 self.comm.sendCommand('M400')
813 self._wizardState = 3
814 elif self._wizardState == 4:
815 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
816 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
817 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 25, feedTravel))
818 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
819 self.comm.sendCommand('M400')
820 self._wizardState = 5
821 elif self._wizardState == 6:
822 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
823 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
824 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
825 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
826 self.comm.sendCommand('M400')
827 self._wizardState = 7
828 elif self._wizardState == 8:
829 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
830 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
831 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
832 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
833 self._wizardState = 9
834 self.resumeButton.Enable(False)
836 def mcLog(self, message):
837 print 'Log:', message
839 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
840 if self._wizardState == 1:
841 self._wizardState = 2
842 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
843 wx.CallAfter(self.resumeButton.Enable, True)
844 elif self._wizardState == 3:
845 self._wizardState = 4
846 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
847 wx.CallAfter(self.resumeButton.Enable, True)
848 elif self._wizardState == 5:
849 self._wizardState = 6
850 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
851 wx.CallAfter(self.resumeButton.Enable, True)
852 elif self._wizardState == 7:
853 self._wizardState = 8
854 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
855 wx.CallAfter(self.resumeButton.Enable, True)
856 elif self._wizardState == 9:
857 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
858 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
860 self._wizardState = 10
861 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
862 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
863 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
864 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
865 w = profile.getPreferenceFloat('machine_width')
866 d = profile.getPreferenceFloat('machine_depth')
867 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
868 filamentArea = math.pi * filamentRadius * filamentRadius
869 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
873 'G1 Z2 F%d' % (feedZ),
875 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
876 'G1 Z0.3 F%d' % (feedZ)]
878 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
880 for i in xrange(0, 3):
882 eValue += (d - 2*dist) * ePerMM
883 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
884 eValue += (w - 2*dist) * ePerMM
885 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
886 eValue += (d - 2*dist) * ePerMM
887 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
888 eValue += (w - 2*dist) * ePerMM
889 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
891 gcodeList.append('M400')
892 self.comm.printGCode(gcodeList)
894 def mcStateChange(self, state):
895 if self.comm is None:
897 if self.comm.isOperational():
898 if self._wizardState == 0:
899 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
900 self.comm.sendCommand('M105')
901 self.comm.sendCommand('G28')
902 self._wizardState = 1
903 elif self._wizardState == 10 and not self.comm.isPrinting():
904 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
905 self.comm.sendCommand('G92 E0')
906 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
907 self.comm.sendCommand('M104 S0')
908 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
909 wx.CallAfter(self.infoBox.SetReadyIndicator)
910 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
911 wx.CallAfter(self.connectButton.Enable, True)
912 self._wizardState = 11
913 elif self.comm.isError():
914 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
916 def mcMessage(self, message):
919 def mcProgress(self, lineNr):
922 def mcZChange(self, newZ):
925 class bedLevelWizard(wx.wizard.Wizard):
927 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
929 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
930 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
932 self.mainPage = bedLevelWizardMain(self)
934 self.FitToPage(self.mainPage)
935 self.GetPageAreaSizer().Add(self.mainPage)
937 self.RunWizard(self.mainPage)
940 def OnPageChanging(self, e):
941 e.GetPage().StoreData()
943 def OnPageChanged(self, e):
944 if e.GetPage().AllowNext():
945 self.FindWindowById(wx.ID_FORWARD).Enable()
947 self.FindWindowById(wx.ID_FORWARD).Disable()
948 self.FindWindowById(wx.ID_BACKWARD).Disable()