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()
356 self.GetParent().ShowPage(self.GetNext())
358 def OnUrlClick(self, e):
359 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
362 class UltimakerCheckupPage(InfoPage):
363 def __init__(self, parent):
364 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
366 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
367 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
368 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
369 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
370 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
371 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
372 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
373 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
374 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
375 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
378 '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.')
379 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
380 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
381 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
383 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
384 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
385 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
387 self.infoBox = self.AddInfoBox()
388 self.machineState = self.AddText('')
389 self.temperatureLabel = self.AddText('')
390 self.errorLogButton = self.AddButton('Show error log')
391 self.errorLogButton.Show(False)
393 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
395 self.xMinStop = False
396 self.xMaxStop = False
397 self.yMinStop = False
398 self.yMaxStop = False
399 self.zMinStop = False
400 self.zMaxStop = False
402 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
405 if self.comm is not None:
409 self.endstopBitmap.Show(False)
412 def OnSkipClick(self, e):
413 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
414 self.GetParent().ShowPage(self.GetNext())
416 def OnCheckClick(self, e=None):
417 self.errorLogButton.Show(False)
418 if self.comm is not None:
422 wx.CallAfter(self.OnCheckClick)
424 self.infoBox.SetBusy('Connecting to machine.')
425 self.commState.SetBitmap(self.unknownBitmap)
426 self.tempState.SetBitmap(self.unknownBitmap)
427 self.stopState.SetBitmap(self.unknownBitmap)
428 self.checkupState = 0
429 self.comm = machineCom.MachineCom(callbackObject=self)
431 def OnErrorLog(self, e):
432 printWindow.LogWindow('\n'.join(self.comm.getLog()))
434 def mcLog(self, message):
437 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
438 if not self.comm.isOperational():
440 if self.checkupState == 0:
441 self.tempCheckTimeout = 20
443 self.checkupState = 1
444 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
445 self.comm.sendCommand('M104 S0')
446 self.comm.sendCommand('M104 S0')
448 self.startTemp = temp
449 self.checkupState = 2
450 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
451 self.comm.sendCommand('M104 S200')
452 self.comm.sendCommand('M104 S200')
453 elif self.checkupState == 1:
455 self.startTemp = temp
456 self.checkupState = 2
457 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
458 self.comm.sendCommand('M104 S200')
459 self.comm.sendCommand('M104 S200')
460 elif self.checkupState == 2:
461 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
462 if temp > self.startTemp + 40:
463 self.checkupState = 3
464 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
465 wx.CallAfter(self.endstopBitmap.Show, True)
466 wx.CallAfter(self.Layout)
467 self.comm.sendCommand('M104 S0')
468 self.comm.sendCommand('M104 S0')
469 self.comm.sendCommand('M119')
470 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
472 self.tempCheckTimeout -= 1
473 if self.tempCheckTimeout < 1:
474 self.checkupState = -1
475 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
476 wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
477 self.comm.sendCommand('M104 S0')
478 self.comm.sendCommand('M104 S0')
479 elif self.checkupState >= 3 and self.checkupState < 10:
480 self.comm.sendCommand('M119')
481 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
483 def mcStateChange(self, state):
484 if self.comm is None:
486 if self.comm.isOperational():
487 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
488 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
489 elif self.comm.isError():
490 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
491 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
492 wx.CallAfter(self.endstopBitmap.Show, False)
493 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
494 wx.CallAfter(self.errorLogButton.Show, True)
495 wx.CallAfter(self.Layout)
497 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
499 def mcMessage(self, message):
500 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
501 for data in message.split(' '):
503 tag, value = data.split(':', 1)
505 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
507 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
509 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
511 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
513 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
515 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
517 tag, value = map(str.strip, message.split(':', 1))
519 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
521 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
523 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
525 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
527 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
529 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
530 if 'z_max' in message:
531 self.comm.sendCommand('M119')
533 if self.checkupState == 3:
534 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
535 self.checkupState = 4
536 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
537 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
538 elif self.checkupState == 4:
539 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
540 self.checkupState = 5
541 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
542 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
543 elif self.checkupState == 5:
544 if 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 = 6
546 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
547 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
548 elif self.checkupState == 6:
549 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
550 self.checkupState = 7
551 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
552 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
553 elif self.checkupState == 7:
554 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
555 self.checkupState = 8
556 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
557 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
558 elif self.checkupState == 8:
559 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
560 self.checkupState = 9
561 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
562 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
563 elif self.checkupState == 9:
564 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
565 self.checkupState = 10
567 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
568 wx.CallAfter(self.infoBox.SetReadyIndicator)
569 wx.CallAfter(self.endstopBitmap.Show, False)
570 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
571 wx.CallAfter(self.OnSkipClick, None)
573 def mcProgress(self, lineNr):
576 def mcZChange(self, newZ):
580 class UltimakerCalibrationPage(InfoPage):
581 def __init__(self, parent):
582 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
584 self.AddText("Your Ultimaker requires some calibration.")
585 self.AddText("This calibration is needed for a proper extrusion amount.")
587 self.AddText("The following values are needed:")
588 self.AddText("* Diameter of filament")
589 self.AddText("* Number of steps per mm of filament extrusion")
591 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
593 self.AddText("First we need the diameter of your filament:")
594 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
596 "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.")
597 self.AddText("Note: This value can be changed later at any time.")
600 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
603 class UltimakerCalibrateStepsPerEPage(InfoPage):
604 def __init__(self, parent):
605 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
607 #if profile.getPreference('steps_per_e') == '0':
608 # profile.putPreference('steps_per_e', '865.888')
610 self.AddText("Calibrating the Steps Per E requires some manual actions.")
611 self.AddText("First remove any filament from your machine.")
612 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
613 self.AddText("We'll push the filament 100mm")
614 self.extrudeButton = self.AddButton("Extrude 100mm filament")
615 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
616 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
617 self.AddText("This results in the following steps per E:")
618 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
619 self.AddText("You can repeat these steps to get better calibration.")
622 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
623 self.heatButton = self.AddButton("Heatup for filament removal")
625 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
626 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
627 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
629 def OnSaveLengthClick(self, e):
630 currentEValue = float(self.stepsPerEInput.GetValue())
631 realExtrudeLength = float(self.lengthInput.GetValue())
632 newEValue = currentEValue * 100 / realExtrudeLength
633 self.stepsPerEInput.SetValue(str(newEValue))
634 self.lengthInput.SetValue("100")
636 def OnExtrudeClick(self, e):
637 threading.Thread(target=self.OnExtrudeRun).start()
639 def OnExtrudeRun(self):
640 self.heatButton.Enable(False)
641 self.extrudeButton.Enable(False)
642 currentEValue = float(self.stepsPerEInput.GetValue())
643 self.comm = machineCom.MachineCom()
644 if not self.comm.isOpen():
646 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
647 'Printer error', wx.OK | wx.ICON_INFORMATION)
648 self.heatButton.Enable(True)
649 self.extrudeButton.Enable(True)
652 line = self.comm.readline()
657 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
660 self.sendGCommand('M302') #Disable cold extrusion protection
661 self.sendGCommand("M92 E%f" % (currentEValue))
662 self.sendGCommand("G92 E0")
663 self.sendGCommand("G1 E100 F600")
666 self.extrudeButton.Enable()
667 self.heatButton.Enable()
669 def OnHeatClick(self, e):
670 threading.Thread(target=self.OnHeatRun).start()
673 self.heatButton.Enable(False)
674 self.extrudeButton.Enable(False)
675 self.comm = machineCom.MachineCom()
676 if not self.comm.isOpen():
678 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
679 'Printer error', wx.OK | wx.ICON_INFORMATION)
680 self.heatButton.Enable(True)
681 self.extrudeButton.Enable(True)
684 line = self.comm.readline()
686 self.heatButton.Enable(True)
687 self.extrudeButton.Enable(True)
691 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
694 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
696 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
697 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
698 self.sendGCommand('M104 S0')
701 self.heatButton.Enable(True)
702 self.extrudeButton.Enable(True)
704 def sendGCommand(self, cmd):
705 self.comm.sendCommand(cmd) #Disable cold extrusion protection
707 line = self.comm.readline()
710 if line.startswith('ok'):
714 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
717 class configWizard(wx.wizard.Wizard):
719 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
721 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
722 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
724 self.firstInfoPage = FirstInfoPage(self)
725 self.machineSelectPage = MachineSelectPage(self)
726 self.ultimakerSelectParts = SelectParts(self)
727 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
728 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
729 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
730 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
731 self.bedLevelPage = bedLevelWizardMain(self)
732 self.repRapInfoPage = RepRapInfoPage(self)
734 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
735 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
736 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
737 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
738 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
739 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
741 self.FitToPage(self.firstInfoPage)
742 self.GetPageAreaSizer().Add(self.firstInfoPage)
744 self.RunWizard(self.firstInfoPage)
747 def OnPageChanging(self, e):
748 e.GetPage().StoreData()
750 def OnPageChanged(self, e):
751 if e.GetPage().AllowNext():
752 self.FindWindowById(wx.ID_FORWARD).Enable()
754 self.FindWindowById(wx.ID_FORWARD).Disable()
755 self.FindWindowById(wx.ID_BACKWARD).Disable()
757 class bedLevelWizardMain(InfoPage):
758 def __init__(self, parent):
759 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
761 self.AddText('This wizard will help you in leveling your printer bed')
763 self.AddText('It will do the following steps')
764 self.AddText('* Move the printer head to each corner')
765 self.AddText(' and let you adjust the height of the bed to the nozzle')
766 self.AddText('* Print a line around the bed to check if it is level')
769 self.connectButton = self.AddButton('Connect to printer')
772 self.infoBox = self.AddInfoBox()
773 self.resumeButton = self.AddButton('Resume')
774 self.resumeButton.Enable(False)
776 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
777 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
779 def OnConnect(self, e = None):
780 if self.comm is not None:
784 wx.CallAfter(self.OnConnect)
786 self.connectButton.Enable(False)
787 self.comm = machineCom.MachineCom(callbackObject=self)
788 self.infoBox.SetBusy('Connecting to machine.')
789 self._wizardState = 0
794 def OnResume(self, e):
795 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
796 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
797 if self._wizardState == 2:
798 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
799 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
800 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
801 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
802 self.comm.sendCommand('M400')
803 self._wizardState = 3
804 elif self._wizardState == 4:
805 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
806 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
807 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 25, feedTravel))
808 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
809 self.comm.sendCommand('M400')
810 self._wizardState = 5
811 elif self._wizardState == 6:
812 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
813 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
814 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
815 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
816 self.comm.sendCommand('M400')
817 self._wizardState = 7
818 elif self._wizardState == 8:
819 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
820 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
821 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
822 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
823 self._wizardState = 9
824 self.resumeButton.Enable(False)
826 def mcLog(self, message):
827 print 'Log:', message
829 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
830 if self._wizardState == 1:
831 self._wizardState = 2
832 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
833 wx.CallAfter(self.resumeButton.Enable, True)
834 elif self._wizardState == 3:
835 self._wizardState = 4
836 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
837 wx.CallAfter(self.resumeButton.Enable, True)
838 elif self._wizardState == 5:
839 self._wizardState = 6
840 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
841 wx.CallAfter(self.resumeButton.Enable, True)
842 elif self._wizardState == 7:
843 self._wizardState = 8
844 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
845 wx.CallAfter(self.resumeButton.Enable, True)
846 elif self._wizardState == 9:
847 if temp < profile.getProfileSettingFloat('print_temperature') - 5:
848 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
850 self._wizardState = 10
851 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
852 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
853 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
854 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
855 w = profile.getPreferenceFloat('machine_width')
856 d = profile.getPreferenceFloat('machine_depth')
857 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
858 filamentArea = math.pi * filamentRadius * filamentRadius
859 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
863 'G1 Z2 F%d' % (feedZ),
865 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
866 'G1 Z0.3 F%d' % (feedZ)]
868 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
870 for i in xrange(0, 3):
872 eValue += (d - 2*dist) * ePerMM
873 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
874 eValue += (w - 2*dist) * ePerMM
875 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
876 eValue += (d - 2*dist) * ePerMM
877 gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
878 eValue += (w - 2*dist) * ePerMM
879 gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
881 gcodeList.append('M400')
882 self.comm.printGCode(gcodeList)
884 def mcStateChange(self, state):
885 if self.comm is None:
887 if self.comm.isOperational():
888 if self._wizardState == 0:
889 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
890 self.comm.sendCommand('M105')
891 self.comm.sendCommand('G28')
892 self._wizardState = 1
893 elif self._wizardState == 10 and not self.comm.isPrinting():
894 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
895 self.comm.sendCommand('G92 E0')
896 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
897 self.comm.sendCommand('M104 S0')
898 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
899 wx.CallAfter(self.infoBox.SetReadyIndicator)
900 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
901 wx.CallAfter(self.connectButton.Enable, True)
902 self._wizardState = 11
903 elif self.comm.isError():
904 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
906 def mcMessage(self, message):
909 def mcProgress(self, lineNr):
912 def mcZChange(self, newZ):
915 class bedLevelWizard(wx.wizard.Wizard):
917 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
919 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
920 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
922 self.mainPage = bedLevelWizardMain(self)
924 self.FitToPage(self.mainPage)
925 self.GetPageAreaSizer().Add(self.mainPage)
927 self.RunWizard(self.mainPage)
930 def OnPageChanging(self, e):
931 e.GetPage().StoreData()
933 def OnPageChanged(self, e):
934 if e.GetPage().AllowNext():
935 self.FindWindowById(wx.ID_FORWARD).Enable()
937 self.FindWindowById(wx.ID_FORWARD).Disable()
938 self.FindWindowById(wx.ID_BACKWARD).Disable()