1 __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 import resources
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(resources.getPathForImage('attention.png'))
29 self.errorBitmap = wx.Bitmap(resources.getPathForImage('error.png'))
30 self.readyBitmap = wx.Bitmap(resources.getPathForImage('ready.png'))
32 wx.Bitmap(resources.getPathForImage('busy-0.png')),
33 wx.Bitmap(resources.getPathForImage('busy-1.png')),
34 wx.Bitmap(resources.getPathForImage('busy-2.png')),
35 wx.Bitmap(resources.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 in setting up Cura for your machine."))
226 # self.AddText(_("This wizard will help you with the following steps:"))
227 # self.AddText(_("* Configure Cura for your machine"))
228 # self.AddText(_("* Optionally upgrade your firmware"))
229 # self.AddText(_("* Optionally check if your machine is working safely"))
230 # self.AddText(_("* Optionally level your printer bed"))
232 #self.AddText('* Calibrate your machine')
233 #self.AddText('* Do your first print')
236 class OtherMachineSelectPage(InfoPage):
237 def __init__(self, parent):
238 super(OtherMachineSelectPage, self).__init__(parent, "Other machine information")
239 self.AddText(_("The following pre-defined machine profiles are available"))
240 self.AddText(_("Note that these profiles are not guaranteed to give good results,\nor work at all. Extra tweaks might be required.\nIf you find issues with the predefined profiles,\nor want an extra profile.\nPlease report it at the github issue tracker."))
242 machines = resources.getDefaultMachineProfiles()
244 for filename in machines:
245 name = os.path.splitext(os.path.basename(filename))[0]
246 item = self.AddRadioButton(name)
247 item.filename = filename
248 item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
249 self.options.append(item)
251 item = self.AddRadioButton('Custom...')
253 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
255 def OnProfileSelect(self, e):
256 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
258 def OnOtherSelect(self, e):
259 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
262 for option in self.options:
263 if option.GetValue():
264 profile.loadProfile(option.filename)
265 profile.loadMachineSettings(option.filename)
267 class OtherMachineInfoPage(InfoPage):
268 def __init__(self, parent):
269 super(OtherMachineInfoPage, self).__init__(parent, "Cura Ready!")
270 self.AddText(_("Cura is now ready to be used!"))
272 class CustomRepRapInfoPage(InfoPage):
273 def __init__(self, parent):
274 super(CustomRepRapInfoPage, self).__init__(parent, "Custom RepRap information")
275 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
276 self.AddText(_("Be sure to review the default profile before running it on your machine."))
277 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
279 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
281 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
282 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
283 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
284 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "55")
285 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
286 self.heatedBed = self.AddCheckbox(_("Heated bed"))
287 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
290 profile.putMachineSetting('machine_name', self.machineName.GetValue())
291 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
292 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
293 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
294 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
295 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
296 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
297 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
298 profile.putMachineSetting('extruder_head_size_min_x', '0')
299 profile.putMachineSetting('extruder_head_size_min_y', '0')
300 profile.putMachineSetting('extruder_head_size_max_x', '0')
301 profile.putMachineSetting('extruder_head_size_max_y', '0')
302 profile.putMachineSetting('extruder_head_size_height', '0')
303 profile.checkAndUpdateMachineName()
305 class MachineSelectPage(InfoPage):
306 def __init__(self, parent):
307 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
308 self.AddText(_("What kind of machine do you have:"))
310 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2", style=wx.RB_GROUP)
311 self.Ultimaker2Radio.SetValue(True)
312 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
313 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
314 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
315 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot)"))
316 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
318 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
319 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
320 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
321 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
322 self.SubmitUserStats.SetValue(True)
324 def OnUltimaker2Select(self, e):
325 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
327 def OnUltimakerSelect(self, e):
328 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
330 def OnOtherSelect(self, e):
331 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
334 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
338 profile.putProfileSetting('retraction_enable', 'True')
339 if self.Ultimaker2Radio.GetValue():
340 profile.putMachineSetting('machine_width', '230')
341 profile.putMachineSetting('machine_depth', '225')
342 profile.putMachineSetting('machine_height', '205')
343 profile.putMachineSetting('machine_name', 'ultimaker2')
344 profile.putMachineSetting('machine_type', 'ultimaker2')
345 profile.putMachineSetting('machine_center_is_zero', 'False')
346 profile.putMachineSetting('has_heated_bed', 'True')
347 profile.putMachineSetting('gcode_flavor', 'UltiGCode')
348 profile.putMachineSetting('extruder_head_size_min_x', '40.0')
349 profile.putMachineSetting('extruder_head_size_min_y', '10.0')
350 profile.putMachineSetting('extruder_head_size_max_x', '60.0')
351 profile.putMachineSetting('extruder_head_size_max_y', '30.0')
352 profile.putMachineSetting('extruder_head_size_height', '55.0')
353 profile.putProfileSetting('nozzle_size', '0.4')
354 profile.putProfileSetting('fan_full_height', '5.0')
355 profile.putMachineSetting('extruder_offset_x1', '18.0')
356 profile.putMachineSetting('extruder_offset_y1', '0.0')
357 elif self.UltimakerRadio.GetValue():
358 profile.putMachineSetting('machine_width', '205')
359 profile.putMachineSetting('machine_depth', '205')
360 profile.putMachineSetting('machine_height', '200')
361 profile.putMachineSetting('machine_name', 'ultimaker original')
362 profile.putMachineSetting('machine_type', 'ultimaker')
363 profile.putMachineSetting('machine_center_is_zero', 'False')
364 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
365 profile.putProfileSetting('nozzle_size', '0.4')
366 profile.putMachineSetting('extruder_head_size_min_x', '75.0')
367 profile.putMachineSetting('extruder_head_size_min_y', '18.0')
368 profile.putMachineSetting('extruder_head_size_max_x', '18.0')
369 profile.putMachineSetting('extruder_head_size_max_y', '35.0')
370 profile.putMachineSetting('extruder_head_size_height', '55.0')
372 profile.putMachineSetting('machine_width', '80')
373 profile.putMachineSetting('machine_depth', '80')
374 profile.putMachineSetting('machine_height', '60')
375 profile.putMachineSetting('machine_name', 'reprap')
376 profile.putMachineSetting('machine_type', 'reprap')
377 profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
378 profile.putPreference('startMode', 'Normal')
379 profile.putProfileSetting('nozzle_size', '0.5')
380 profile.checkAndUpdateMachineName()
381 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
382 if self.SubmitUserStats.GetValue():
383 profile.putPreference('submit_slice_information', 'True')
385 profile.putPreference('submit_slice_information', 'False')
388 class SelectParts(InfoPage):
389 def __init__(self, parent):
390 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
391 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."))
393 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
394 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
395 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
397 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."))
398 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
399 self.springExtruder.SetValue(True)
402 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
403 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
404 if self.dualExtrusion.GetValue():
405 profile.putMachineSetting('extruder_amount', '2')
406 profile.putMachineSetting('machine_depth', '195')
408 profile.putMachineSetting('extruder_amount', '1')
409 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
410 profile.putProfileSetting('retraction_enable', 'True')
412 profile.putProfileSetting('retraction_enable', 'False')
415 class UltimakerFirmwareUpgradePage(InfoPage):
416 def __init__(self, parent):
417 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
418 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."))
419 self.AddHiddenSeperator()
420 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
421 self.AddHiddenSeperator()
422 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."))
423 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
424 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
425 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
426 self.AddHiddenSeperator()
427 self.AddText(_("Do not upgrade to this firmware if:"))
428 self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
429 self.AddText(_("* Have other changes in the firmware"))
430 # button = self.AddButton('Goto this page for a custom firmware')
431 # button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
436 def OnUpgradeClick(self, e):
437 if firmwareInstall.InstallFirmware():
438 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
440 def OnSkipClick(self, e):
441 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
442 self.GetParent().ShowPage(self.GetNext())
444 def OnUrlClick(self, e):
445 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
447 class UltimakerCheckupPage(InfoPage):
448 def __init__(self, parent):
449 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
451 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
452 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
453 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
454 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
455 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
456 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
457 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
458 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
459 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
460 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
463 _("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."))
464 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
465 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
466 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
468 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
469 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
470 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
472 self.infoBox = self.AddInfoBox()
473 self.machineState = self.AddText("")
474 self.temperatureLabel = self.AddText("")
475 self.errorLogButton = self.AddButton(_("Show error log"))
476 self.errorLogButton.Show(False)
478 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
480 self.xMinStop = False
481 self.xMaxStop = False
482 self.yMinStop = False
483 self.yMaxStop = False
484 self.zMinStop = False
485 self.zMaxStop = False
487 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
490 if self.comm is not None:
494 self.endstopBitmap.Show(False)
497 def OnSkipClick(self, e):
498 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
499 self.GetParent().ShowPage(self.GetNext())
501 def OnCheckClick(self, e=None):
502 self.errorLogButton.Show(False)
503 if self.comm is not None:
507 wx.CallAfter(self.OnCheckClick)
509 self.infoBox.SetBusy(_("Connecting to machine."))
510 self.commState.SetBitmap(self.unknownBitmap)
511 self.tempState.SetBitmap(self.unknownBitmap)
512 self.stopState.SetBitmap(self.unknownBitmap)
513 self.checkupState = 0
514 self.checkExtruderNr = 0
515 self.comm = machineCom.MachineCom(callbackObject=self)
517 def OnErrorLog(self, e):
518 printWindow.LogWindow('\n'.join(self.comm.getLog()))
520 def mcLog(self, message):
523 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
524 if not self.comm.isOperational():
526 if self.checkupState == 0:
527 self.tempCheckTimeout = 20
528 if temp[self.checkExtruderNr] > 70:
529 self.checkupState = 1
530 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
531 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
532 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
534 self.startTemp = temp[self.checkExtruderNr]
535 self.checkupState = 2
536 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
537 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
538 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
539 elif self.checkupState == 1:
541 self.startTemp = temp[self.checkExtruderNr]
542 self.checkupState = 2
543 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
544 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
545 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
546 elif self.checkupState == 2:
547 #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
548 if temp[self.checkExtruderNr] > self.startTemp + 40:
549 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
550 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
551 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
552 self.checkExtruderNr = 0
553 self.checkupState = 3
554 wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
555 wx.CallAfter(self.endstopBitmap.Show, True)
556 wx.CallAfter(self.Layout)
557 self.comm.sendCommand('M119')
558 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
560 self.checkupState = 0
561 self.checkExtruderNr += 1
563 self.tempCheckTimeout -= 1
564 if self.tempCheckTimeout < 1:
565 self.checkupState = -1
566 wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
567 wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
568 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
569 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
570 elif self.checkupState >= 3 and self.checkupState < 10:
571 self.comm.sendCommand('M119')
572 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
574 def mcStateChange(self, state):
575 if self.comm is None:
577 if self.comm.isOperational():
578 wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
579 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
580 elif self.comm.isError():
581 wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
582 wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
583 wx.CallAfter(self.endstopBitmap.Show, False)
584 wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
585 wx.CallAfter(self.errorLogButton.Show, True)
586 wx.CallAfter(self.Layout)
588 wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
590 def mcMessage(self, message):
591 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
592 for data in message.split(' '):
594 tag, value = data.split(':', 1)
596 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
598 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
600 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
602 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
604 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
606 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
608 tag, value = map(str.strip, message.split(':', 1))
610 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
612 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
614 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
616 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
618 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
620 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
621 if 'z_max' in message:
622 self.comm.sendCommand('M119')
624 if self.checkupState == 3:
625 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
626 self.checkupState = 4
627 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
628 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
629 elif self.checkupState == 4:
630 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
631 self.checkupState = 5
632 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
633 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
634 elif self.checkupState == 5:
635 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
636 self.checkupState = 6
637 wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
638 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
639 elif self.checkupState == 6:
640 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
641 self.checkupState = 7
642 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
643 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
644 elif self.checkupState == 7:
645 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
646 self.checkupState = 8
647 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
648 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
649 elif self.checkupState == 8:
650 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
651 self.checkupState = 9
652 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
653 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
654 elif self.checkupState == 9:
655 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
656 self.checkupState = 10
658 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
659 wx.CallAfter(self.infoBox.SetReadyIndicator)
660 wx.CallAfter(self.endstopBitmap.Show, False)
661 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
662 wx.CallAfter(self.OnSkipClick, None)
664 def mcProgress(self, lineNr):
667 def mcZChange(self, newZ):
671 class UltimakerCalibrationPage(InfoPage):
672 def __init__(self, parent):
673 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
675 self.AddText("Your Ultimaker requires some calibration.")
676 self.AddText("This calibration is needed for a proper extrusion amount.")
678 self.AddText("The following values are needed:")
679 self.AddText("* Diameter of filament")
680 self.AddText("* Number of steps per mm of filament extrusion")
682 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
684 self.AddText("First we need the diameter of your filament:")
685 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
687 "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.")
688 self.AddText("Note: This value can be changed later at any time.")
691 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
694 class UltimakerCalibrateStepsPerEPage(InfoPage):
695 def __init__(self, parent):
696 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
698 #if profile.getMachineSetting('steps_per_e') == '0':
699 # profile.putMachineSetting('steps_per_e', '865.888')
701 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
702 self.AddText(_("First remove any filament from your machine."))
703 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
704 self.AddText(_("We'll push the filament 100mm"))
705 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
706 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
707 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
708 self.AddText(_("This results in the following steps per E:"))
709 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
710 self.AddText(_("You can repeat these steps to get better calibration."))
713 _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
714 self.heatButton = self.AddButton(_("Heatup for filament removal"))
716 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
717 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
718 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
720 def OnSaveLengthClick(self, e):
721 currentEValue = float(self.stepsPerEInput.GetValue())
722 realExtrudeLength = float(self.lengthInput.GetValue())
723 newEValue = currentEValue * 100 / realExtrudeLength
724 self.stepsPerEInput.SetValue(str(newEValue))
725 self.lengthInput.SetValue("100")
727 def OnExtrudeClick(self, e):
728 t = threading.Thread(target=self.OnExtrudeRun)
732 def OnExtrudeRun(self):
733 self.heatButton.Enable(False)
734 self.extrudeButton.Enable(False)
735 currentEValue = float(self.stepsPerEInput.GetValue())
736 self.comm = machineCom.MachineCom()
737 if not self.comm.isOpen():
739 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
740 'Printer error', wx.OK | wx.ICON_INFORMATION)
741 self.heatButton.Enable(True)
742 self.extrudeButton.Enable(True)
745 line = self.comm.readline()
750 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
753 self.sendGCommand('M302') #Disable cold extrusion protection
754 self.sendGCommand("M92 E%f" % (currentEValue))
755 self.sendGCommand("G92 E0")
756 self.sendGCommand("G1 E100 F600")
759 self.extrudeButton.Enable()
760 self.heatButton.Enable()
762 def OnHeatClick(self, e):
763 t = threading.Thread(target=self.OnHeatRun)
768 self.heatButton.Enable(False)
769 self.extrudeButton.Enable(False)
770 self.comm = machineCom.MachineCom()
771 if not self.comm.isOpen():
773 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
774 'Printer error', wx.OK | wx.ICON_INFORMATION)
775 self.heatButton.Enable(True)
776 self.extrudeButton.Enable(True)
779 line = self.comm.readline()
781 self.heatButton.Enable(True)
782 self.extrudeButton.Enable(True)
786 #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
789 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
791 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
792 'Machine heatup', wx.OK | wx.ICON_INFORMATION)
793 self.sendGCommand('M104 S0')
796 self.heatButton.Enable(True)
797 self.extrudeButton.Enable(True)
799 def sendGCommand(self, cmd):
800 self.comm.sendCommand(cmd) #Disable cold extrusion protection
802 line = self.comm.readline()
805 if line.startswith('ok'):
809 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
811 class Ultimaker2ReadyPage(InfoPage):
812 def __init__(self, parent):
813 super(Ultimaker2ReadyPage, self).__init__(parent, "Ultimaker2")
814 self.AddText('Congratulations on your the purchase of your brand new Ultimaker2.')
815 self.AddText('Cura is now ready to be used with your Ultimaker2.')
818 class configWizard(wx.wizard.Wizard):
819 def __init__(self, addNew = False):
820 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
822 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
823 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
825 self.firstInfoPage = FirstInfoPage(self, addNew)
826 self.machineSelectPage = MachineSelectPage(self)
827 self.ultimakerSelectParts = SelectParts(self)
828 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
829 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
830 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
831 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
832 self.bedLevelPage = bedLevelWizardMain(self)
833 self.headOffsetCalibration = headOffsetCalibrationPage(self)
834 self.otherMachineSelectPage = OtherMachineSelectPage(self)
835 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
836 self.otherMachineInfoPage = OtherMachineInfoPage(self)
838 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
840 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
841 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
842 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
843 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
844 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
845 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
846 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
847 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
849 self.FitToPage(self.firstInfoPage)
850 self.GetPageAreaSizer().Add(self.firstInfoPage)
852 self.RunWizard(self.firstInfoPage)
855 def OnPageChanging(self, e):
856 e.GetPage().StoreData()
858 def OnPageChanged(self, e):
859 if e.GetPage().AllowNext():
860 self.FindWindowById(wx.ID_FORWARD).Enable()
862 self.FindWindowById(wx.ID_FORWARD).Disable()
863 self.FindWindowById(wx.ID_BACKWARD).Disable()
865 class bedLevelWizardMain(InfoPage):
866 def __init__(self, parent):
867 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
869 self.AddText('This wizard will help you in leveling your printer bed')
871 self.AddText('It will do the following steps')
872 self.AddText('* Move the printer head to each corner')
873 self.AddText(' and let you adjust the height of the bed to the nozzle')
874 self.AddText('* Print a line around the bed to check if it is level')
877 self.connectButton = self.AddButton('Connect to printer')
880 self.infoBox = self.AddInfoBox()
881 self.resumeButton = self.AddButton('Resume')
882 self.resumeButton.Enable(False)
884 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
885 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
887 def OnConnect(self, e = None):
888 if self.comm is not None:
892 wx.CallAfter(self.OnConnect)
894 self.connectButton.Enable(False)
895 self.comm = machineCom.MachineCom(callbackObject=self)
896 self.infoBox.SetBusy('Connecting to machine.')
897 self._wizardState = 0
900 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
901 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
904 def OnResume(self, e):
905 feedZ = profile.getProfileSettingFloat('print_speed') * 60
906 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
907 if self._wizardState == 2:
908 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
909 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
910 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
911 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
912 self.comm.sendCommand('M400')
913 self._wizardState = 3
914 elif self._wizardState == 4:
915 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
916 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
917 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
918 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
919 self.comm.sendCommand('M400')
920 self._wizardState = 5
921 elif self._wizardState == 6:
922 wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
923 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
924 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
925 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
926 self.comm.sendCommand('M400')
927 self._wizardState = 7
928 elif self._wizardState == 8:
929 wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
930 self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
931 self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
932 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
933 self._wizardState = 9
934 elif self._wizardState == 10:
935 self._wizardState = 11
936 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
937 feedZ = profile.getProfileSettingFloat('print_speed') * 60
938 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
939 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
940 w = profile.getMachineSettingFloat('machine_width')
941 d = profile.getMachineSettingFloat('machine_depth')
942 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
943 filamentArea = math.pi * filamentRadius * filamentRadius
944 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
948 'G1 Z2 F%d' % (feedZ),
950 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
951 'G1 Z0.3 F%d' % (feedZ)]
953 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
955 for i in xrange(0, 3):
956 dist = 5.0 + 0.4 * float(i)
957 eValue += (d - 2.0*dist) * ePerMM
958 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
959 eValue += (w - 2.0*dist) * ePerMM
960 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
961 eValue += (d - 2.0*dist) * ePerMM
962 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
963 eValue += (w - 2.0*dist) * ePerMM
964 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
966 gcodeList.append('M400')
967 self.comm.printGCode(gcodeList)
968 self.resumeButton.Enable(False)
970 def mcLog(self, message):
971 print 'Log:', message
973 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
974 if self._wizardState == 1:
975 self._wizardState = 2
976 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
977 wx.CallAfter(self.resumeButton.Enable, True)
978 elif self._wizardState == 3:
979 self._wizardState = 4
980 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
981 wx.CallAfter(self.resumeButton.Enable, True)
982 elif self._wizardState == 5:
983 self._wizardState = 6
984 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
985 wx.CallAfter(self.resumeButton.Enable, True)
986 elif self._wizardState == 7:
987 self._wizardState = 8
988 wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
989 wx.CallAfter(self.resumeButton.Enable, True)
990 elif self._wizardState == 9:
991 if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
992 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp[0], profile.getProfileSettingFloat('print_temperature')))
994 wx.CallAfter(self.infoBox.SetAttention, 'The printer is hot now. Please insert some PLA filament into the printer.')
995 wx.CallAfter(self.resumeButton.Enable, True)
996 self._wizardState = 10
998 def mcStateChange(self, state):
999 if self.comm is None:
1001 if self.comm.isOperational():
1002 if self._wizardState == 0:
1003 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
1004 self.comm.sendCommand('M105')
1005 self.comm.sendCommand('G28')
1006 self._wizardState = 1
1007 elif self._wizardState == 11 and not self.comm.isPrinting():
1008 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1009 self.comm.sendCommand('G92 E0')
1010 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1011 self.comm.sendCommand('M104 S0')
1012 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
1013 wx.CallAfter(self.infoBox.SetReadyIndicator)
1014 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1015 wx.CallAfter(self.connectButton.Enable, True)
1016 self._wizardState = 12
1017 elif self.comm.isError():
1018 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1020 def mcMessage(self, message):
1023 def mcProgress(self, lineNr):
1026 def mcZChange(self, newZ):
1029 class headOffsetCalibrationPage(InfoPage):
1030 def __init__(self, parent):
1031 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1033 self.AddText('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine')
1036 self.connectButton = self.AddButton('Connect to printer')
1039 self.infoBox = self.AddInfoBox()
1040 self.textEntry = self.AddTextCtrl('')
1041 self.textEntry.Enable(False)
1042 self.resumeButton = self.AddButton('Resume')
1043 self.resumeButton.Enable(False)
1045 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1046 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1048 def OnConnect(self, e = None):
1049 if self.comm is not None:
1053 wx.CallAfter(self.OnConnect)
1055 self.connectButton.Enable(False)
1056 self.comm = machineCom.MachineCom(callbackObject=self)
1057 self.infoBox.SetBusy('Connecting to machine.')
1058 self._wizardState = 0
1060 def OnResume(self, e):
1061 if self._wizardState == 2:
1062 self._wizardState = 3
1063 wx.CallAfter(self.infoBox.SetBusy, 'Printing initial calibration cross')
1065 w = profile.getMachineSettingFloat('machine_width')
1066 d = profile.getMachineSettingFloat('machine_depth')
1068 gcode = gcodeGenerator.gcodeGenerator()
1069 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1070 gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1077 gcode.addMove(w/2, 5)
1078 gcode.addMove(z=0.2)
1080 gcode.addExtrude(w/2, d-5.0)
1082 gcode.addMove(5, d/2)
1084 gcode.addExtrude(w-5.0, d/2)
1085 gcode.addRetract(15)
1088 gcode.addMove(w/2, 5)
1090 gcode.addExtrude(w/2, d-5.0)
1092 gcode.addMove(5, d/2)
1094 gcode.addExtrude(w-5.0, d/2)
1095 gcode.addRetract(15)
1100 gcode.addCmd('M400')
1102 self.comm.printGCode(gcode.list())
1103 self.resumeButton.Enable(False)
1104 elif self._wizardState == 4:
1106 float(self.textEntry.GetValue())
1109 profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1110 self._wizardState = 5
1111 self.infoBox.SetAttention('Please measure the distance between the horizontal lines in millimeters.')
1112 self.textEntry.SetValue('0.0')
1113 self.textEntry.Enable(True)
1114 elif self._wizardState == 5:
1116 float(self.textEntry.GetValue())
1119 profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1120 self._wizardState = 6
1121 self.infoBox.SetBusy('Printing the fine calibration lines.')
1122 self.textEntry.SetValue('')
1123 self.textEntry.Enable(False)
1124 self.resumeButton.Enable(False)
1126 x = profile.getMachineSettingFloat('extruder_offset_x1')
1127 y = profile.getMachineSettingFloat('extruder_offset_y1')
1128 gcode = gcodeGenerator.gcodeGenerator()
1129 gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1130 gcode.setPrintSpeed(25)
1133 gcode.addMove(50, 40, 0.2)
1135 for n in xrange(0, 10):
1136 gcode.addExtrude(50 + n * 10, 150)
1137 gcode.addExtrude(50 + n * 10 + 5, 150)
1138 gcode.addExtrude(50 + n * 10 + 5, 40)
1139 gcode.addExtrude(50 + n * 10 + 10, 40)
1140 gcode.addMove(40, 50)
1141 for n in xrange(0, 10):
1142 gcode.addExtrude(150, 50 + n * 10)
1143 gcode.addExtrude(150, 50 + n * 10 + 5)
1144 gcode.addExtrude(40, 50 + n * 10 + 5)
1145 gcode.addExtrude(40, 50 + n * 10 + 10)
1146 gcode.addRetract(15)
1149 gcode.addMove(50 - x, 30 - y, 0.2)
1151 for n in xrange(0, 10):
1152 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1153 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1154 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1155 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1156 gcode.addMove(30 - x, 50 - y, 0.2)
1157 for n in xrange(0, 10):
1158 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1159 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1160 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1161 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1162 gcode.addRetract(15)
1164 gcode.addCmd('M400')
1165 gcode.addCmd('M104 T0 S0')
1166 gcode.addCmd('M104 T1 S0')
1167 self.comm.printGCode(gcode.list())
1168 elif self._wizardState == 7:
1170 n = int(self.textEntry.GetValue()) - 1
1173 x = profile.getMachineSettingFloat('extruder_offset_x1')
1175 profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1176 self.infoBox.SetAttention('Which horizontal line number lays perfect on top of each other? Front most line is zero.')
1177 self.textEntry.SetValue('10')
1178 self._wizardState = 8
1179 elif self._wizardState == 8:
1181 n = int(self.textEntry.GetValue()) - 1
1184 y = profile.getMachineSettingFloat('extruder_offset_y1')
1186 profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1187 self.infoBox.SetInfo('Calibration finished. Offsets are: %s %s' % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1188 self.infoBox.SetReadyIndicator()
1189 self._wizardState = 8
1191 self.resumeButton.Enable(False)
1193 def mcLog(self, message):
1194 print 'Log:', message
1196 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1197 if self._wizardState == 1:
1198 if temp[0] >= 210 and temp[1] >= 210:
1199 self._wizardState = 2
1200 wx.CallAfter(self.infoBox.SetAttention, 'Please load both extruders with PLA.')
1201 wx.CallAfter(self.resumeButton.Enable, True)
1202 wx.CallAfter(self.resumeButton.SetFocus)
1204 def mcStateChange(self, state):
1205 if self.comm is None:
1207 if self.comm.isOperational():
1208 if self._wizardState == 0:
1209 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer and heating up both extruders.')
1210 self.comm.sendCommand('M105')
1211 self.comm.sendCommand('M104 S220 T0')
1212 self.comm.sendCommand('M104 S220 T1')
1213 self.comm.sendCommand('G28')
1214 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1215 self._wizardState = 1
1216 if not self.comm.isPrinting():
1217 if self._wizardState == 3:
1218 self._wizardState = 4
1219 wx.CallAfter(self.infoBox.SetAttention, 'Please measure the distance between the vertical lines in millimeters.')
1220 wx.CallAfter(self.textEntry.SetValue, '0.0')
1221 wx.CallAfter(self.textEntry.Enable, True)
1222 wx.CallAfter(self.resumeButton.Enable, True)
1223 wx.CallAfter(self.resumeButton.SetFocus)
1224 elif self._wizardState == 6:
1225 self._wizardState = 7
1226 wx.CallAfter(self.infoBox.SetAttention, 'Which vertical line number lays perfect on top of each other? Leftmost line is zero.')
1227 wx.CallAfter(self.textEntry.SetValue, '10')
1228 wx.CallAfter(self.textEntry.Enable, True)
1229 wx.CallAfter(self.resumeButton.Enable, True)
1230 wx.CallAfter(self.resumeButton.SetFocus)
1232 elif self.comm.isError():
1233 wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1235 def mcMessage(self, message):
1238 def mcProgress(self, lineNr):
1241 def mcZChange(self, newZ):
1244 class bedLevelWizard(wx.wizard.Wizard):
1246 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
1248 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1249 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1251 self.mainPage = bedLevelWizardMain(self)
1252 self.headOffsetCalibration = None
1254 self.FitToPage(self.mainPage)
1255 self.GetPageAreaSizer().Add(self.mainPage)
1257 self.RunWizard(self.mainPage)
1260 def OnPageChanging(self, e):
1261 e.GetPage().StoreData()
1263 def OnPageChanged(self, e):
1264 if e.GetPage().AllowNext():
1265 self.FindWindowById(wx.ID_FORWARD).Enable()
1267 self.FindWindowById(wx.ID_FORWARD).Disable()
1268 self.FindWindowById(wx.ID_BACKWARD).Disable()
1270 class headOffsetWizard(wx.wizard.Wizard):
1272 super(headOffsetWizard, self).__init__(None, -1, "Head offset wizard")
1274 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1275 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1277 self.mainPage = headOffsetCalibrationPage(self)
1279 self.FitToPage(self.mainPage)
1280 self.GetPageAreaSizer().Add(self.mainPage)
1282 self.RunWizard(self.mainPage)
1285 def OnPageChanging(self, e):
1286 e.GetPage().StoreData()
1288 def OnPageChanged(self, e):
1289 if e.GetPage().AllowNext():
1290 self.FindWindowById(wx.ID_FORWARD).Enable()
1292 self.FindWindowById(wx.ID_FORWARD).Disable()
1293 self.FindWindowById(wx.ID_BACKWARD).Disable()