1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
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 import gcodeGenerator
17 from Cura.util.resources import getPathForImage
20 class InfoBox(wx.Panel):
21 def __init__(self, parent):
22 super(InfoBox, self).__init__(parent)
23 self.SetBackgroundColour('#FFFF80')
25 self.sizer = wx.GridBagSizer(5, 5)
26 self.SetSizer(self.sizer)
28 self.attentionBitmap = wx.Bitmap(getPathForImage('attention.png'))
29 self.errorBitmap = wx.Bitmap(getPathForImage('error.png'))
30 self.readyBitmap = wx.Bitmap(getPathForImage('ready.png'))
32 wx.Bitmap(getPathForImage('busy-0.png')),
33 wx.Bitmap(getPathForImage('busy-1.png')),
34 wx.Bitmap(getPathForImage('busy-2.png')),
35 wx.Bitmap(getPathForImage('busy-3.png'))
38 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
39 self.text = wx.StaticText(self, -1, '')
40 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
41 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
42 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
43 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
44 self.sizer.AddGrowableCol(1)
46 self.extraInfoButton.Show(False)
48 self.extraInfoUrl = ''
50 self.timer = wx.Timer(self)
51 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
52 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
55 def SetInfo(self, info):
56 self.SetBackgroundColour('#FFFF80')
57 self.text.SetLabel(info)
58 self.extraInfoButton.Show(False)
61 def SetError(self, info, extraInfoUrl):
62 self.extraInfoUrl = extraInfoUrl
63 self.SetBackgroundColour('#FF8080')
64 self.text.SetLabel(info)
65 self.extraInfoButton.Show(True)
67 self.SetErrorIndicator()
70 def SetAttention(self, info):
71 self.SetBackgroundColour('#FFFF80')
72 self.text.SetLabel(info)
73 self.extraInfoButton.Show(False)
74 self.SetAttentionIndicator()
78 def SetBusy(self, info):
80 self.SetBusyIndicator()
82 def SetBusyIndicator(self):
84 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
86 def doExtraInfo(self, e):
87 webbrowser.open(self.extraInfoUrl)
89 def doBusyUpdate(self, e):
90 if self.busyState is None:
93 if self.busyState >= len(self.busyBitmap):
95 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
97 def SetReadyIndicator(self):
99 self.bitmap.SetBitmap(self.readyBitmap)
101 def SetErrorIndicator(self):
102 self.busyState = None
103 self.bitmap.SetBitmap(self.errorBitmap)
105 def SetAttentionIndicator(self):
106 self.busyState = None
107 self.bitmap.SetBitmap(self.attentionBitmap)
110 class InfoPage(wx.wizard.WizardPageSimple):
111 def __init__(self, parent, title):
112 wx.wizard.WizardPageSimple.__init__(self, parent)
114 sizer = wx.GridBagSizer(5, 5)
118 title = wx.StaticText(self, -1, title)
119 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
120 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
121 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
122 sizer.AddGrowableCol(1)
126 def AddText(self, info):
127 text = wx.StaticText(self, -1, info)
128 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
132 def AddSeperator(self):
133 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
136 def AddHiddenSeperator(self):
139 def AddInfoBox(self):
140 infoBox = InfoBox(self)
141 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
145 def AddRadioButton(self, label, style=0):
146 radio = wx.RadioButton(self, -1, label, style=style)
147 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
151 def AddCheckbox(self, label, checked=False):
152 check = wx.CheckBox(self, -1)
153 text = wx.StaticText(self, -1, label)
154 check.SetValue(checked)
155 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
156 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
160 def AddButton(self, label):
161 button = wx.Button(self, -1, label)
162 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
166 def AddDualButton(self, label1, label2):
167 button1 = wx.Button(self, -1, label1)
168 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
169 button2 = wx.Button(self, -1, label2)
170 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
172 return button1, button2
174 def AddTextCtrl(self, value):
175 ret = wx.TextCtrl(self, -1, value)
176 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
180 def AddLabelTextCtrl(self, info, value):
181 text = wx.StaticText(self, -1, info)
182 ret = wx.TextCtrl(self, -1, value)
183 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
184 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
188 def AddTextCtrlButton(self, value, buttonText):
189 text = wx.TextCtrl(self, -1, value)
190 button = wx.Button(self, -1, buttonText)
191 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
192 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
196 def AddBitmap(self, bitmap):
197 bitmap = wx.StaticBitmap(self, -1, bitmap)
198 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
202 def AddCheckmark(self, label, bitmap):
203 check = wx.StaticBitmap(self, -1, bitmap)
204 text = wx.StaticText(self, -1, label)
205 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
206 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
217 class FirstInfoPage(InfoPage):
218 def __init__(self, parent, addNew):
220 super(FirstInfoPage, self).__init__(parent, _("Add new machine wizard"))
222 super(FirstInfoPage, self).__init__(parent, _("First time run wizard"))
223 self.AddText(_("Welcome, and thanks for trying Cura!"))
225 self.AddText(_("This wizard will help you with the following steps:"))
226 self.AddText(_("* Configure Cura for your machine"))
227 self.AddText(_("* Optionally upgrade your firmware"))
228 self.AddText(_("* Optionally check if your machine is working safely"))
229 self.AddText(_("* Optionally level your printer bed"))
231 #self.AddText('* Calibrate your machine')
232 #self.AddText('* Do your first print')
235 class RepRapInfoPage(InfoPage):
236 def __init__(self, parent):
237 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
239 _("RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them."))
240 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
242 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
244 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
245 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
246 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
247 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "60")
248 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
249 self.heatedBed = self.AddCheckbox(_("Heated bed"))
250 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
253 profile.putMachineSetting('machine_name', self.machineName.GetValue())
254 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
255 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
256 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
257 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
258 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
259 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
260 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
261 profile.putMachineSetting('extruder_head_size_min_x', '0')
262 profile.putMachineSetting('extruder_head_size_min_y', '0')
263 profile.putMachineSetting('extruder_head_size_max_x', '0')
264 profile.putMachineSetting('extruder_head_size_max_y', '0')
265 profile.putMachineSetting('extruder_head_size_height', '0')
266 profile.checkAndUpdateMachineName()
268 class MachineSelectPage(InfoPage):
269 def __init__(self, parent):
270 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
271 self.AddText(_("What kind of machine do you have:"))
273 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2", style=wx.RB_GROUP)
274 self.Ultimaker2Radio.SetValue(True)
275 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
276 self.UltimakerRadio = self.AddRadioButton("Ultimaker")
277 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
278 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap)"))
279 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
281 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
282 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
283 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
284 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
285 self.SubmitUserStats.SetValue(True)
287 def OnUltimaker2Select(self, e):
288 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
290 def OnUltimakerSelect(self, e):
291 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
293 def OnOtherSelect(self, e):
294 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
297 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
301 if self.Ultimaker2Radio.GetValue():
302 profile.putMachineSetting('machine_width', '230')
303 profile.putMachineSetting('machine_depth', '225')
304 profile.putMachineSetting('machine_height', '205')
305 profile.putMachineSetting('machine_type', 'ultimaker2')
306 profile.putMachineSetting('machine_center_is_zero', 'False')
307 profile.putMachineSetting('has_heated_bed', 'True')
308 profile.putMachineSetting('gcode_flavor', 'UltiGCode')
309 profile.putProfileSetting('nozzle_size', '0.4')
310 profile.putMachineSetting('extruder_head_size_min_x', '40.0')
311 profile.putMachineSetting('extruder_head_size_min_y', '10.0')
312 profile.putMachineSetting('extruder_head_size_max_x', '60.0')
313 profile.putMachineSetting('extruder_head_size_max_y', '30.0')
314 profile.putMachineSetting('extruder_head_size_height', '60.0')
315 elif self.UltimakerRadio.GetValue():
316 profile.putMachineSetting('machine_width', '205')
317 profile.putMachineSetting('machine_depth', '205')
318 profile.putMachineSetting('machine_height', '200')
319 profile.putMachineSetting('machine_type', 'ultimaker')
320 profile.putMachineSetting('machine_center_is_zero', 'False')
321 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
322 profile.putProfileSetting('nozzle_size', '0.4')
323 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
324 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
325 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
326 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
327 profile.putMachineSetting('extruder_head_size_height', '60.0')
329 profile.putMachineSetting('machine_width', '80')
330 profile.putMachineSetting('machine_depth', '80')
331 profile.putMachineSetting('machine_height', '60')
332 profile.putMachineSetting('machine_type', 'reprap')
333 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
334 profile.putPreference('startMode', 'Normal')
335 profile.putProfileSetting('nozzle_size', '0.5')
336 profile.checkAndUpdateMachineName()
337 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
338 if self.SubmitUserStats.GetValue():
339 profile.putPreference('submit_slice_information', 'True')
341 profile.putPreference('submit_slice_information', 'False')
344 class SelectParts(InfoPage):
345 def __init__(self, parent):
346 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
347 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."))
349 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
350 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
351 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
353 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."))
354 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
355 self.springExtruder.SetValue(True)
358 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
359 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
360 if self.dualExtrusion.GetValue():
361 profile.putMachineSetting('extruder_amount', '2')
362 profile.putMachineSetting('machine_depth', '195')
364 profile.putMachineSetting('extruder_amount', '1')
365 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
366 profile.putProfileSetting('retraction_enable', 'True')
368 profile.putProfileSetting('retraction_enable', 'False')
371 class UltimakerFirmwareUpgradePage(InfoPage):
372 def __init__(self, parent):
373 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
374 self.AddText(_("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."))
375 self.AddHiddenSeperator()
376 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
377 self.AddHiddenSeperator()
378 self.AddText(_("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."))
379 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
380 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
381 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
382 self.AddHiddenSeperator()
383 self.AddText(_("Do not upgrade to this firmware if:"))
384 self.AddText(_("* You have an older machine based on ATMega1280"))
385 self.AddText(_("* Have other changes in the firmware"))
386 # button = self.AddButton('Goto this page for a custom firmware')
387 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
392 def OnUpgradeClick(self, e):
393 if firmwareInstall.InstallFirmware():
394 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
396 def OnSkipClick(self, e):
397 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
398 self.GetParent().ShowPage(self.GetNext())
400 def OnUrlClick(self, e):
401 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
403 class UltimakerCheckupPage(InfoPage):
404 def __init__(self, parent):
405 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
407 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
408 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
409 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
410 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
411 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
412 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
413 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
414 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
415 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
416 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
419 _("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."))
420 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
421 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
422 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
424 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
425 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
426 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
428 self.infoBox = self.AddInfoBox()
429 self.machineState = self.AddText("")
430 self.temperatureLabel = self.AddText("")
431 self.errorLogButton = self.AddButton(_("Show error log"))
432 self.errorLogButton.Show(False)
434 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
436 self.xMinStop = False
437 self.xMaxStop = False
438 self.yMinStop = False
439 self.yMaxStop = False
440 self.zMinStop = False
441 self.zMaxStop = False
443 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
446 if self.comm is not None:
450 self.endstopBitmap.Show(False)
453 def OnSkipClick(self, e):
454 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
455 self.GetParent().ShowPage(self.GetNext())
457 def OnCheckClick(self, e=None):
458 self.errorLogButton.Show(False)
459 if self.comm is not None:
463 wx.CallAfter(self.OnCheckClick)
465 self.infoBox.SetBusy(_("Connecting to machine."))
466 self.commState.SetBitmap(self.unknownBitmap)
467 self.tempState.SetBitmap(self.unknownBitmap)
468 self.stopState.SetBitmap(self.unknownBitmap)
469 self.checkupState = 0
470 self.checkExtruderNr = 0
471 self.comm = machineCom.MachineCom(callbackObject=self)
473 def OnErrorLog(self, e):
474 printWindow.LogWindow('\n'.join(self.comm.getLog()))
476 def mcLog(self, message):
479 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
480 if not self.comm.isOperational():
482 if self.checkupState == 0:
483 self.tempCheckTimeout = 20
484 if temp[self.checkExtruderNr] > 70:
485 self.checkupState = 1
486 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
487 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
488 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
490 self.startTemp = temp[self.checkExtruderNr]
491 self.checkupState = 2
492 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
493 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
494 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
495 elif self.checkupState == 1:
497 self.startTemp = temp[self.checkExtruderNr]
498 self.checkupState = 2
499 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
500 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
501 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
502 elif self.checkupState == 2:
503 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
504 if temp[self.checkExtruderNr] > self.startTemp + 40:
505 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
506 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
507 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
508 self.checkExtruderNr = 0
509 self.checkupState = 3
510 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
511 wx.CallAfter(self.endstopBitmap.Show, True)
512 wx.CallAfter(self.Layout)
513 self.comm.sendCommand('M119')
514 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
516 self.checkupState = 0
517 self.checkExtruderNr += 1
519 self.tempCheckTimeout -= 1
520 if self.tempCheckTimeout < 1:
521 self.checkupState = -1
522 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
523 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
524 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
525 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
526 elif self.checkupState >= 3 and self.checkupState < 10:
527 self.comm.sendCommand('M119')
528 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
530 def mcStateChange(self, state):
531 if self.comm is None:
533 if self.comm.isOperational():
534 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
535 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
536 elif self.comm.isError():
537 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
538 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
539 wx.CallAfter(self.endstopBitmap.Show, False)
540 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
541 wx.CallAfter(self.errorLogButton.Show, True)
542 wx.CallAfter(self.Layout)
544 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
546 def mcMessage(self, message):
547 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
548 for data in message.split(' '):
550 tag, value = data.split(':', 1)
552 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
554 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
556 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
558 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
560 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
562 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
564 tag, value = map(str.strip, message.split(':', 1))
566 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
568 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
570 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
572 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
574 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
576 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
577 if 'z_max' in message:
578 self.comm.sendCommand('M119')
580 if self.checkupState == 3:
581 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
582 self.checkupState = 4
583 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
584 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
585 elif self.checkupState == 4:
586 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
587 self.checkupState = 5
588 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
589 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
590 elif self.checkupState == 5:
591 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
592 self.checkupState = 6
593 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
594 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
595 elif self.checkupState == 6:
596 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
597 self.checkupState = 7
598 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
599 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
600 elif self.checkupState == 7:
601 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
602 self.checkupState = 8
603 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
604 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
605 elif self.checkupState == 8:
606 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
607 self.checkupState = 9
608 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
609 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
610 elif self.checkupState == 9:
611 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
612 self.checkupState = 10
614 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
615 wx.CallAfter(self.infoBox.SetReadyIndicator)
616 wx.CallAfter(self.endstopBitmap.Show, False)
617 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
618 wx.CallAfter(self.OnSkipClick, None)
620 def mcProgress(self, lineNr):
623 def mcZChange(self, newZ):
627 class UltimakerCalibrationPage(InfoPage):
628 def __init__(self, parent):
629 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
631 self.AddText("Your Ultimaker requires some calibration.")
632 self.AddText("This calibration is needed for a proper extrusion amount.")
634 self.AddText("The following values are needed:")
635 self.AddText("* Diameter of filament")
636 self.AddText("* Number of steps per mm of filament extrusion")
638 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
640 self.AddText("First we need the diameter of your filament:")
641 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
643 "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.")
644 self.AddText("Note: This value can be changed later at any time.")
647 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
650 class UltimakerCalibrateStepsPerEPage(InfoPage):
651 def __init__(self, parent):
652 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
654 #if profile.getMachineSetting('steps_per_e') == '0':
655 # profile.putMachineSetting('steps_per_e', '865.888')
657 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
658 self.AddText(_("First remove any filament from your machine."))
659 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
660 self.AddText(_("We'll push the filament 100mm"))
661 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
662 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
663 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
664 self.AddText(_("This results in the following steps per E:"))
665 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
666 self.AddText(_("You can repeat these steps to get better calibration."))
669 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
670 self.heatButton = self.AddButton(_("Heatup for filament removal"))
672 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
673 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
674 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
676 def OnSaveLengthClick(self, e):
677 currentEValue = float(self.stepsPerEInput.GetValue())
678 realExtrudeLength = float(self.lengthInput.GetValue())
679 newEValue = currentEValue * 100 / realExtrudeLength
680 self.stepsPerEInput.SetValue(str(newEValue))
681 self.lengthInput.SetValue("100")
683 def OnExtrudeClick(self, e):
684 threading.Thread(target=self.OnExtrudeRun).start()
686 def OnExtrudeRun(self):
687 self.heatButton.Enable(False)
688 self.extrudeButton.Enable(False)
689 currentEValue = float(self.stepsPerEInput.GetValue())
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()
704 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
707 self.sendGCommand('M302') #Disable cold extrusion protection
708 self.sendGCommand("M92 E%f" % (currentEValue))
709 self.sendGCommand("G92 E0")
710 self.sendGCommand("G1 E100 F600")
713 self.extrudeButton.Enable()
714 self.heatButton.Enable()
716 def OnHeatClick(self, e):
717 threading.Thread(target=self.OnHeatRun).start()
720 self.heatButton.Enable(False)
721 self.extrudeButton.Enable(False)
722 self.comm = machineCom.MachineCom()
723 if not self.comm.isOpen():
725 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
726 'Printer error', wx.OK | wx.ICON_INFORMATION)
727 self.heatButton.Enable(True)
728 self.extrudeButton.Enable(True)
731 line = self.comm.readline()
733 self.heatButton.Enable(True)
734 self.extrudeButton.Enable(True)
738 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
741 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
743 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
744 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
745 self.sendGCommand('M104 S0')
748 self.heatButton.Enable(True)
749 self.extrudeButton.Enable(True)
751 def sendGCommand(self, cmd):
752 self.comm.sendCommand(cmd) #Disable cold extrusion protection
754 line = self.comm.readline()
757 if line.startswith('ok'):
761 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
763 class Ultimaker2ReadyPage(InfoPage):
764 def __init__(self, parent):
765 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
766 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
767 self.AddText('Cura is now ready to be used with your Ultimaker2.')
770 class configWizard(wx.wizard.Wizard):
771 def __init__(self, addNew = False):
772 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
774 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
775 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
777 self.firstInfoPage = FirstInfoPage(self, addNew)
778 self.machineSelectPage = MachineSelectPage(self)
779 self.ultimakerSelectParts = SelectParts(self)
780 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
781 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
782 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
783 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
784 self.bedLevelPage = bedLevelWizardMain(self)
785 self.headOffsetCalibration = headOffsetCalibrationPage(self)
786 self.repRapInfoPage = RepRapInfoPage(self)
788 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
790 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
791 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
792 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
793 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
794 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
795 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
796 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
798 self.FitToPage(self.firstInfoPage)
799 self.GetPageAreaSizer().Add(self.firstInfoPage)
801 self.RunWizard(self.firstInfoPage)
804 def OnPageChanging(self, e):
805 e.GetPage().StoreData()
807 def OnPageChanged(self, e):
808 if e.GetPage().AllowNext():
809 self.FindWindowById(wx.ID_FORWARD).Enable()
811 self.FindWindowById(wx.ID_FORWARD).Disable()
812 self.FindWindowById(wx.ID_BACKWARD).Disable()
814 class bedLevelWizardMain(InfoPage):
815 def __init__(self, parent):
816 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
818 self.AddText('This wizard will help you in leveling your printer bed')
820 self.AddText('It will do the following steps')
821 self.AddText('* Move the printer head to each corner')
822 self.AddText(' and let you adjust the height of the bed to the nozzle')
823 self.AddText('* Print a line around the bed to check if it is level')
826 self.connectButton = self.AddButton('Connect to printer')
829 self.infoBox = self.AddInfoBox()
830 self.resumeButton = self.AddButton('Resume')
831 self.resumeButton.Enable(False)
833 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
834 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
836 def OnConnect(self, e = None):
837 if self.comm is not None:
841 wx.CallAfter(self.OnConnect)
843 self.connectButton.Enable(False)
844 self.comm = machineCom.MachineCom(callbackObject=self)
845 self.infoBox.SetBusy('Connecting to machine.')
846 self._wizardState = 0
849 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
850 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
853 def OnResume(self, e):
854 feedZ = profile.getProfileSettingFloat('print_speed') * 60
855 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
856 if self._wizardState == 2:
857 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
858 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
859 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
860 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
861 self.comm.sendCommand('M400')
862 self._wizardState = 3
863 elif self._wizardState == 4:
864 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
865 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
866 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
867 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
868 self.comm.sendCommand('M400')
869 self._wizardState = 5
870 elif self._wizardState == 6:
871 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
872 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
873 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
874 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
875 self.comm.sendCommand('M400')
876 self._wizardState = 7
877 elif self._wizardState == 8:
878 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
879 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
880 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
881 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
882 self._wizardState = 9
883 elif self._wizardState == 10:
884 self._wizardState = 11
885 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
886 feedZ = profile.getProfileSettingFloat('print_speed') * 60
887 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
888 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
889 w = profile.getMachineSettingFloat('machine_width')
890 d = profile.getMachineSettingFloat('machine_depth')
891 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
892 filamentArea = math.pi * filamentRadius * filamentRadius
893 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
897 'G1 Z2 F%d' % (feedZ),
899 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
900 'G1 Z0.3 F%d' % (feedZ)]
902 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
904 for i in xrange(0, 3):
905 dist = 5.0 + 0.4 * float(i)
906 eValue += (d - 2.0*dist) * ePerMM
907 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
908 eValue += (w - 2.0*dist) * ePerMM
909 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
910 eValue += (d - 2.0*dist) * ePerMM
911 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
912 eValue += (w - 2.0*dist) * ePerMM
913 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
915 gcodeList.append('M400')
916 self.comm.printGCode(gcodeList)
917 self.resumeButton.Enable(False)
919 def mcLog(self, message):
920 print 'Log:', message
922 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
923 if self._wizardState == 1:
924 self._wizardState = 2
925 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
926 wx.CallAfter(self.resumeButton.Enable, True)
927 elif self._wizardState == 3:
928 self._wizardState = 4
929 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
930 wx.CallAfter(self.resumeButton.Enable, True)
931 elif self._wizardState == 5:
932 self._wizardState = 6
933 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
934 wx.CallAfter(self.resumeButton.Enable, True)
935 elif self._wizardState == 7:
936 self._wizardState = 8
937 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
938 wx.CallAfter(self.resumeButton.Enable, True)
939 elif self._wizardState == 9:
940 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
941 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
943 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
944 wx.CallAfter(self.resumeButton.Enable, True)
945 self._wizardState = 10
947 def mcStateChange(self, state):
948 if self.comm is None:
950 if self.comm.isOperational():
951 if self._wizardState == 0:
952 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
953 self.comm.sendCommand('M105')
954 self.comm.sendCommand('G28')
955 self._wizardState = 1
956 elif self._wizardState == 11 and not self.comm.isPrinting():
957 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
958 self.comm.sendCommand('G92 E0')
959 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
960 self.comm.sendCommand('M104 S0')
961 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
962 wx.CallAfter(self.infoBox.SetReadyIndicator)
963 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
964 wx.CallAfter(self.connectButton.Enable, True)
965 self._wizardState = 12
966 elif self.comm.isError():
967 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
969 def mcMessage(self, message):
972 def mcProgress(self, lineNr):
975 def mcZChange(self, newZ):
978 class headOffsetCalibrationPage(InfoPage):
979 def __init__(self, parent):
980 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
982 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
985 self.connectButton = self.AddButton('Connect to printer')
988 self.infoBox = self.AddInfoBox()
989 self.textEntry = self.AddTextCtrl('')
990 self.textEntry.Enable(False)
991 self.resumeButton = self.AddButton('Resume')
992 self.resumeButton.Enable(False)
994 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
995 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
997 def OnConnect(self, e = None):
998 if self.comm is not None:
1002 wx.CallAfter(self.OnConnect)
1004 self.connectButton.Enable(False)
1005 self.comm = machineCom.MachineCom(callbackObject=self)
1006 self.infoBox.SetBusy('Connecting to machine.')
1007 self._wizardState = 0
1009 def OnResume(self, e):
1010 if self._wizardState == 2:
1011 self._wizardState = 3
1012 wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1014 w = profile.getMachineSettingFloat('machine_width')
1015 d = profile.getMachineSettingFloat('machine_depth')
1017 gcode = gcodeGenerator.gcodeGenerator()
1018 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1019 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1026 gcode.addMove(w/2, 5)
1027 gcode.addMove(z=0.2)
1029 gcode.addExtrude(w/2, d-5.0)
1031 gcode.addMove(5, d/2)
1033 gcode.addExtrude(w-5.0, d/2)
1034 gcode.addRetract(15)
1037 gcode.addMove(w/2, 5)
1039 gcode.addExtrude(w/2, d-5.0)
1041 gcode.addMove(5, d/2)
1043 gcode.addExtrude(w-5.0, d/2)
1044 gcode.addRetract(15)
1049 gcode.addCmd('M400')
1051 self.comm.printGCode(gcode.list())
1052 self.resumeButton.Enable(False)
1053 elif self._wizardState == 4:
1055 float(self.textEntry.GetValue())
1058 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1059 self._wizardState = 5
1060 self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1061 self.textEntry.SetValue('0.0')
1062 self.textEntry.Enable(True)
1063 elif self._wizardState == 5:
1065 float(self.textEntry.GetValue())
1068 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1069 self._wizardState = 6
1070 self.infoBox.SetBusy('Printing the fine calibration lines.')
1071 self.textEntry.SetValue('')
1072 self.textEntry.Enable(False)
1073 self.resumeButton.Enable(False)
1075 x = profile.getMachineSettingFloat('extruder_offset_x1')
1076 y = profile.getMachineSettingFloat('extruder_offset_y1')
1077 gcode = gcodeGenerator.gcodeGenerator()
1078 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1079 gcode.setPrintSpeed(25)
1082 gcode.addMove(50, 40, 0.2)
1084 for n in xrange(0, 10):
1085 gcode.addExtrude(50 + n * 10, 150)
1086 gcode.addExtrude(50 + n * 10 + 5, 150)
1087 gcode.addExtrude(50 + n * 10 + 5, 40)
1088 gcode.addExtrude(50 + n * 10 + 10, 40)
1089 gcode.addMove(40, 50)
1090 for n in xrange(0, 10):
1091 gcode.addExtrude(150, 50 + n * 10)
1092 gcode.addExtrude(150, 50 + n * 10 + 5)
1093 gcode.addExtrude(40, 50 + n * 10 + 5)
1094 gcode.addExtrude(40, 50 + n * 10 + 10)
1095 gcode.addRetract(15)
1098 gcode.addMove(50 - x, 30 - y, 0.2)
1100 for n in xrange(0, 10):
1101 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1102 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1103 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1104 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1105 gcode.addMove(30 - x, 50 - y, 0.2)
1106 for n in xrange(0, 10):
1107 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1108 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1109 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1110 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1111 gcode.addRetract(15)
1113 gcode.addCmd('M400')
1114 gcode.addCmd('M104 T0 S0')
1115 gcode.addCmd('M104 T1 S0')
1116 self.comm.printGCode(gcode.list())
1117 elif self._wizardState == 7:
1119 n = int(self.textEntry.GetValue()) - 1
1122 x = profile.getMachineSettingFloat('extruder_offset_x1')
1124 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1125 self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1126 self.textEntry.SetValue('10')
1127 self._wizardState = 8
1128 elif self._wizardState == 8:
1130 n = int(self.textEntry.GetValue()) - 1
1133 y = profile.getMachineSettingFloat('extruder_offset_y1')
1135 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1136 self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1137 self.infoBox.SetReadyIndicator()
1138 self._wizardState = 8
1140 self.resumeButton.Enable(False)
1142 def mcLog(self, message):
1143 print 'Log:', message
1145 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1146 if self._wizardState == 1:
1147 if temp[0] >= 210 and temp[1] >= 210:
1148 self._wizardState = 2
1149 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1150 wx.CallAfter(self.resumeButton.Enable, True)
1151 wx.CallAfter(self.resumeButton.SetFocus)
1153 def mcStateChange(self, state):
1154 if self.comm is None:
1156 if self.comm.isOperational():
1157 if self._wizardState == 0:
1158 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1159 self.comm.sendCommand('M105')
1160 self.comm.sendCommand('M104 S220 T0')
1161 self.comm.sendCommand('M104 S220 T1')
1162 self.comm.sendCommand('G28')
1163 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1164 self._wizardState = 1
1165 if not self.comm.isPrinting():
1166 if self._wizardState == 3:
1167 self._wizardState = 4
1168 wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1169 wx.CallAfter(self.textEntry.SetValue, '0.0')
1170 wx.CallAfter(self.textEntry.Enable, True)
1171 wx.CallAfter(self.resumeButton.Enable, True)
1172 wx.CallAfter(self.resumeButton.SetFocus)
1173 elif self._wizardState == 6:
1174 self._wizardState = 7
1175 wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1176 wx.CallAfter(self.textEntry.SetValue, '10')
1177 wx.CallAfter(self.textEntry.Enable, True)
1178 wx.CallAfter(self.resumeButton.Enable, True)
1179 wx.CallAfter(self.resumeButton.SetFocus)
1181 elif self.comm.isError():
1182 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1184 def mcMessage(self, message):
1187 def mcProgress(self, lineNr):
1190 def mcZChange(self, newZ):
1193 class bedLevelWizard(wx.wizard.Wizard):
1195 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1197 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1198 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1200 self.mainPage = bedLevelWizardMain(self)
1201 self.headOffsetCalibration = None
1203 self.FitToPage(self.mainPage)
1204 self.GetPageAreaSizer().Add(self.mainPage)
1206 self.RunWizard(self.mainPage)
1209 def OnPageChanging(self, e):
1210 e.GetPage().StoreData()
1212 def OnPageChanged(self, e):
1213 if e.GetPage().AllowNext():
1214 self.FindWindowById(wx.ID_FORWARD).Enable()
1216 self.FindWindowById(wx.ID_FORWARD).Disable()
1217 self.FindWindowById(wx.ID_BACKWARD).Disable()
1219 class headOffsetWizard(wx.wizard.Wizard):
1221 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1223 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1224 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1226 self.mainPage = headOffsetCalibrationPage(self)
1228 self.FitToPage(self.mainPage)
1229 self.GetPageAreaSizer().Add(self.mainPage)
1231 self.RunWizard(self.mainPage)
1234 def OnPageChanging(self, e):
1235 e.GetPage().StoreData()
1237 def OnPageChanged(self, e):
1238 if e.GetPage().AllowNext():
1239 self.FindWindowById(wx.ID_FORWARD).Enable()
1241 self.FindWindowById(wx.ID_FORWARD).Disable()
1242 self.FindWindowById(wx.ID_BACKWARD).Disable()