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
452 if temp[self.checkExtruderNr] > 70:
453 self.checkupState = 1
454 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
455 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
456 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
458 self.startTemp = temp[self.checkExtruderNr]
459 self.checkupState = 2
460 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
461 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
462 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
463 elif self.checkupState == 1:
465 self.startTemp = temp[self.checkExtruderNr]
466 self.checkupState = 2
467 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
468 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
469 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
470 elif self.checkupState == 2:
471 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
472 if temp[self.checkExtruderNr] > self.startTemp + 40:
473 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
474 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
475 if self.checkExtruderNr < int(profile.getPreference('extruder_amount')):
476 self.checkExtruderNr = 0
477 self.checkupState = 3
478 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
479 wx.CallAfter(self.endstopBitmap.Show, True)
480 wx.CallAfter(self.Layout)
481 self.comm.sendCommand('M119')
482 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
484 self.checkupState = 0
485 self.checkExtruderNr += 1
487 self.tempCheckTimeout -= 1
488 if self.tempCheckTimeout < 1:
489 self.checkupState = -1
490 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
491 wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
492 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
493 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
494 elif self.checkupState >= 3 and self.checkupState < 10:
495 self.comm.sendCommand('M119')
496 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp[self.checkExtruderNr]))
498 def mcStateChange(self, state):
499 if self.comm is None:
501 if self.comm.isOperational():
502 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
503 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
504 elif self.comm.isError():
505 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
506 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
507 wx.CallAfter(self.endstopBitmap.Show, False)
508 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
509 wx.CallAfter(self.errorLogButton.Show, True)
510 wx.CallAfter(self.Layout)
512 wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
514 def mcMessage(self, message):
515 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
516 for data in message.split(' '):
518 tag, value = data.split(':', 1)
520 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
522 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
524 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
526 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
528 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
530 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
532 tag, value = map(str.strip, message.split(':', 1))
534 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
536 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
538 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
540 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
542 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
544 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
545 if 'z_max' in message:
546 self.comm.sendCommand('M119')
548 if self.checkupState == 3:
549 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
550 self.checkupState = 4
551 wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
552 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
553 elif self.checkupState == 4:
554 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
555 self.checkupState = 5
556 wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
557 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
558 elif self.checkupState == 5:
559 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
560 self.checkupState = 6
561 wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
562 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
563 elif self.checkupState == 6:
564 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
565 self.checkupState = 7
566 wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
567 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
568 elif self.checkupState == 7:
569 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
570 self.checkupState = 8
571 wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
572 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
573 elif self.checkupState == 8:
574 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
575 self.checkupState = 9
576 wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
577 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
578 elif self.checkupState == 9:
579 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
580 self.checkupState = 10
582 wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
583 wx.CallAfter(self.infoBox.SetReadyIndicator)
584 wx.CallAfter(self.endstopBitmap.Show, False)
585 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
586 wx.CallAfter(self.OnSkipClick, None)
588 def mcProgress(self, lineNr):
591 def mcZChange(self, newZ):
595 class UltimakerCalibrationPage(InfoPage):
596 def __init__(self, parent):
597 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
599 self.AddText("Your Ultimaker requires some calibration.")
600 self.AddText("This calibration is needed for a proper extrusion amount.")
602 self.AddText("The following values are needed:")
603 self.AddText("* Diameter of filament")
604 self.AddText("* Number of steps per mm of filament extrusion")
606 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
608 self.AddText("First we need the diameter of your filament:")
609 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
611 "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.")
612 self.AddText("Note: This value can be changed later at any time.")
615 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
618 class UltimakerCalibrateStepsPerEPage(InfoPage):
619 def __init__(self, parent):
620 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
622 #if profile.getPreference('steps_per_e') == '0':
623 # profile.putPreference('steps_per_e', '865.888')
625 self.AddText("Calibrating the Steps Per E requires some manual actions.")
626 self.AddText("First remove any filament from your machine.")
627 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
628 self.AddText("We'll push the filament 100mm")
629 self.extrudeButton = self.AddButton("Extrude 100mm filament")
630 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
631 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
632 self.AddText("This results in the following steps per E:")
633 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
634 self.AddText("You can repeat these steps to get better calibration.")
637 "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
638 self.heatButton = self.AddButton("Heatup for filament removal")
640 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
641 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
642 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
644 def OnSaveLengthClick(self, e):
645 currentEValue = float(self.stepsPerEInput.GetValue())
646 realExtrudeLength = float(self.lengthInput.GetValue())
647 newEValue = currentEValue * 100 / realExtrudeLength
648 self.stepsPerEInput.SetValue(str(newEValue))
649 self.lengthInput.SetValue("100")
651 def OnExtrudeClick(self, e):
652 threading.Thread(target=self.OnExtrudeRun).start()
654 def OnExtrudeRun(self):
655 self.heatButton.Enable(False)
656 self.extrudeButton.Enable(False)
657 currentEValue = float(self.stepsPerEInput.GetValue())
658 self.comm = machineCom.MachineCom()
659 if not self.comm.isOpen():
661 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
662 'Printer error', wx.OK | wx.ICON_INFORMATION)
663 self.heatButton.Enable(True)
664 self.extrudeButton.Enable(True)
667 line = self.comm.readline()
672 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
675 self.sendGCommand('M302') #Disable cold extrusion protection
676 self.sendGCommand("M92 E%f" % (currentEValue))
677 self.sendGCommand("G92 E0")
678 self.sendGCommand("G1 E100 F600")
681 self.extrudeButton.Enable()
682 self.heatButton.Enable()
684 def OnHeatClick(self, e):
685 threading.Thread(target=self.OnHeatRun).start()
688 self.heatButton.Enable(False)
689 self.extrudeButton.Enable(False)
690 self.comm = machineCom.MachineCom()
691 if not self.comm.isOpen():
693 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
694 'Printer error', wx.OK | wx.ICON_INFORMATION)
695 self.heatButton.Enable(True)
696 self.extrudeButton.Enable(True)
699 line = self.comm.readline()
701 self.heatButton.Enable(True)
702 self.extrudeButton.Enable(True)
706 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
709 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
711 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
712 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
713 self.sendGCommand('M104 S0')
716 self.heatButton.Enable(True)
717 self.extrudeButton.Enable(True)
719 def sendGCommand(self, cmd):
720 self.comm.sendCommand(cmd) #Disable cold extrusion protection
722 line = self.comm.readline()
725 if line.startswith('ok'):
729 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
732 class configWizard(wx.wizard.Wizard):
734 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
736 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
737 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
739 self.firstInfoPage = FirstInfoPage(self)
740 self.machineSelectPage = MachineSelectPage(self)
741 self.ultimakerSelectParts = SelectParts(self)
742 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
743 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
744 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
745 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
746 self.bedLevelPage = bedLevelWizardMain(self)
747 self.repRapInfoPage = RepRapInfoPage(self)
749 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
750 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
751 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
752 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
753 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
754 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
756 self.FitToPage(self.firstInfoPage)
757 self.GetPageAreaSizer().Add(self.firstInfoPage)
759 self.RunWizard(self.firstInfoPage)
762 def OnPageChanging(self, e):
763 e.GetPage().StoreData()
765 def OnPageChanged(self, e):
766 if e.GetPage().AllowNext():
767 self.FindWindowById(wx.ID_FORWARD).Enable()
769 self.FindWindowById(wx.ID_FORWARD).Disable()
770 self.FindWindowById(wx.ID_BACKWARD).Disable()
772 class bedLevelWizardMain(InfoPage):
773 def __init__(self, parent):
774 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
776 self.AddText('This wizard will help you in leveling your printer bed')
778 self.AddText('It will do the following steps')
779 self.AddText('* Move the printer head to each corner')
780 self.AddText(' and let you adjust the height of the bed to the nozzle')
781 self.AddText('* Print a line around the bed to check if it is level')
784 self.connectButton = self.AddButton('Connect to printer')
787 self.infoBox = self.AddInfoBox()
788 self.resumeButton = self.AddButton('Resume')
789 self.resumeButton.Enable(False)
791 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
792 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
794 def OnConnect(self, e = None):
795 if self.comm is not None:
799 wx.CallAfter(self.OnConnect)
801 self.connectButton.Enable(False)
802 self.comm = machineCom.MachineCom(callbackObject=self)
803 self.infoBox.SetBusy('Connecting to machine.')
804 self._wizardState = 0
809 def OnResume(self, e):
810 feedZ = profile.getProfileSettingFloat('print_speed') * 60
811 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
812 if self._wizardState == 2:
813 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
814 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
815 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
816 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
817 self.comm.sendCommand('M400')
818 self._wizardState = 3
819 elif self._wizardState == 4:
820 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
821 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
822 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width') - 5.0, profile.getPreferenceFloat('machine_depth') - 25, feedTravel))
823 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
824 self.comm.sendCommand('M400')
825 self._wizardState = 5
826 elif self._wizardState == 6:
827 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
828 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
829 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width') - 5.0, 20, feedTravel))
830 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
831 self.comm.sendCommand('M400')
832 self._wizardState = 7
833 elif self._wizardState == 8:
834 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
835 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
836 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
837 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
838 self._wizardState = 9
839 elif self._wizardState == 10:
840 self._wizardState = 11
841 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
842 feedZ = profile.getProfileSettingFloat('print_speed') * 60
843 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
844 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
845 w = profile.getPreferenceFloat('machine_width')
846 d = profile.getPreferenceFloat('machine_depth')
847 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
848 filamentArea = math.pi * filamentRadius * filamentRadius
849 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
853 'G1 Z2 F%d' % (feedZ),
855 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
856 'G1 Z0.3 F%d' % (feedZ)]
858 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
860 for i in xrange(0, 3):
861 dist = 5.0 + 0.4 * float(i)
862 eValue += (d - 2.0*dist) * ePerMM
863 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
864 eValue += (w - 2.0*dist) * ePerMM
865 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
866 eValue += (d - 2.0*dist) * ePerMM
867 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
868 eValue += (w - 2.0*dist) * ePerMM
869 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
871 gcodeList.append('M400')
872 self.comm.printGCode(gcodeList)
873 self.resumeButton.Enable(False)
875 def mcLog(self, message):
876 print 'Log:', message
878 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
879 if self._wizardState == 1:
880 self._wizardState = 2
881 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
882 wx.CallAfter(self.resumeButton.Enable, True)
883 elif self._wizardState == 3:
884 self._wizardState = 4
885 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
886 wx.CallAfter(self.resumeButton.Enable, True)
887 elif self._wizardState == 5:
888 self._wizardState = 6
889 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
890 wx.CallAfter(self.resumeButton.Enable, True)
891 elif self._wizardState == 7:
892 self._wizardState = 8
893 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
894 wx.CallAfter(self.resumeButton.Enable, True)
895 elif self._wizardState == 9:
896 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
897 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
899 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
900 wx.CallAfter(self.resumeButton.Enable, True)
901 self._wizardState = 10
903 def mcStateChange(self, state):
904 if self.comm is None:
906 if self.comm.isOperational():
907 if self._wizardState == 0:
908 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
909 self.comm.sendCommand('M105')
910 self.comm.sendCommand('G28')
911 self._wizardState = 1
912 elif self._wizardState == 11 and not self.comm.isPrinting():
913 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
914 self.comm.sendCommand('G92 E0')
915 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
916 self.comm.sendCommand('M104 S0')
917 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
918 wx.CallAfter(self.infoBox.SetReadyIndicator)
919 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
920 wx.CallAfter(self.connectButton.Enable, True)
921 self._wizardState = 12
922 elif self.comm.isError():
923 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
925 def mcMessage(self, message):
928 def mcProgress(self, lineNr):
931 def mcZChange(self, newZ):
934 class bedLevelWizard(wx.wizard.Wizard):
936 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
938 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
939 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
941 self.mainPage = bedLevelWizardMain(self)
943 self.FitToPage(self.mainPage)
944 self.GetPageAreaSizer().Add(self.mainPage)
946 self.RunWizard(self.mainPage)
949 def OnPageChanging(self, e):
950 e.GetPage().StoreData()
952 def OnPageChanged(self, e):
953 if e.GetPage().AllowNext():
954 self.FindWindowById(wx.ID_FORWARD).Enable()
956 self.FindWindowById(wx.ID_FORWARD).Disable()
957 self.FindWindowById(wx.ID_BACKWARD).Disable()