chiark / gitweb /
f782555c7f5098ad9f678f1e068badb690ab8edf
[cura.git] / Cura / gui / configWizard.py
1 # coding=utf-8
2 from __future__ import absolute_import
3
4 import webbrowser
5 import threading
6 import time
7
8 import wx
9 import wx.wizard
10
11 from gui import firmwareInstall
12 from gui import toolbarUtil
13 from gui import printWindow
14 from util import machineCom
15 from util import profile
16 from util.resources import getPathForImage
17
18 class InfoBox(wx.Panel):
19         def __init__(self, parent):
20                 super(InfoBox, self).__init__(parent)
21                 self.SetBackgroundColour('#FFFF80')
22
23                 self.sizer = wx.GridBagSizer(5, 5)
24                 self.SetSizer(self.sizer)
25
26                 self.attentionBitmap = wx.Bitmap(getPathForImage('attention.png'))
27                 self.errorBitmap = wx.Bitmap(getPathForImage('error.png'))
28                 self.readyBitmap = wx.Bitmap(getPathForImage('ready.png'))
29                 self.busyBitmap = [
30                         wx.Bitmap(getPathForImage('busy-0.png')),
31                         wx.Bitmap(getPathForImage('busy-1.png')),
32                         wx.Bitmap(getPathForImage('busy-2.png')),
33                         wx.Bitmap(getPathForImage('busy-3.png'))
34                 ]
35
36                 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
37                 self.text = wx.StaticText(self, -1, '')
38                 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
39                 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
40                 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
41                 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
42                 self.sizer.AddGrowableCol(1)
43
44                 self.extraInfoButton.Show(False)
45
46                 self.extraInfoUrl = ''
47                 self.busyState = None
48                 self.timer = wx.Timer(self)
49                 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
50                 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
51                 self.timer.Start(100)
52
53         def SetInfo(self, info):
54                 self.SetBackgroundColour('#FFFF80')
55                 self.text.SetLabel(info)
56                 self.extraInfoButton.Show(False)
57                 self.Refresh()
58
59         def SetError(self, info, extraInfoUrl):
60                 self.extraInfoUrl = extraInfoUrl
61                 self.SetBackgroundColour('#FF8080')
62                 self.text.SetLabel(info)
63                 self.extraInfoButton.Show(True)
64                 self.Layout()
65                 self.SetErrorIndicator()
66                 self.Refresh()
67
68         def SetAttention(self, info):
69                 self.SetBackgroundColour('#FFFF80')
70                 self.text.SetLabel(info)
71                 self.extraInfoButton.Show(False)
72                 self.SetAttentionIndicator()
73                 self.Refresh()
74
75         def SetBusyIndicator(self):
76                 self.busyState = 0
77                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
78
79         def doExtraInfo(self, e):
80                 webbrowser.open(self.extraInfoUrl)
81
82         def doBusyUpdate(self, e):
83                 if self.busyState == None:
84                         return
85                 self.busyState += 1
86                 if self.busyState >= len(self.busyBitmap):
87                         self.busyState = 0
88                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
89
90         def SetReadyIndicator(self):
91                 self.busyState = None
92                 self.bitmap.SetBitmap(self.readyBitmap)
93
94         def SetErrorIndicator(self):
95                 self.busyState = None
96                 self.bitmap.SetBitmap(self.errorBitmap)
97
98         def SetAttentionIndicator(self):
99                 self.busyState = None
100                 self.bitmap.SetBitmap(self.attentionBitmap)
101
102
103 class InfoPage(wx.wizard.WizardPageSimple):
104         def __init__(self, parent, title):
105                 wx.wizard.WizardPageSimple.__init__(self, parent)
106
107                 sizer = wx.GridBagSizer(5, 5)
108                 self.sizer = sizer
109                 self.SetSizer(sizer)
110
111                 title = wx.StaticText(self, -1, title)
112                 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
113                 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
114                 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
115                 sizer.AddGrowableCol(1)
116
117                 self.rowNr = 2
118
119         def AddText(self, info):
120                 text = wx.StaticText(self, -1, info)
121                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
122                 self.rowNr += 1
123                 return text
124
125         def AddSeperator(self):
126                 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
127                 self.rowNr += 1
128
129         def AddHiddenSeperator(self):
130                 self.AddText('')
131
132         def AddInfoBox(self):
133                 infoBox = InfoBox(self)
134                 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
135                 self.rowNr += 1
136                 return infoBox
137
138         def AddRadioButton(self, label, style=0):
139                 radio = wx.RadioButton(self, -1, label, style=style)
140                 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
141                 self.rowNr += 1
142                 return radio
143
144         def AddCheckbox(self, label, checked=False):
145                 check = wx.CheckBox(self, -1)
146                 text = wx.StaticText(self, -1, label)
147                 check.SetValue(checked)
148                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
149                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
150                 self.rowNr += 1
151                 return check
152
153         def AddButton(self, label):
154                 button = wx.Button(self, -1, label)
155                 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
156                 self.rowNr += 1
157                 return button
158
159         def AddDualButton(self, label1, label2):
160                 button1 = wx.Button(self, -1, label1)
161                 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
162                 button2 = wx.Button(self, -1, label2)
163                 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
164                 self.rowNr += 1
165                 return button1, button2
166
167         def AddTextCtrl(self, value):
168                 ret = wx.TextCtrl(self, -1, value)
169                 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
170                 self.rowNr += 1
171                 return ret
172
173         def AddLabelTextCtrl(self, info, value):
174                 text = wx.StaticText(self, -1, info)
175                 ret = wx.TextCtrl(self, -1, value)
176                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
177                 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
178                 self.rowNr += 1
179                 return ret
180
181         def AddTextCtrlButton(self, value, buttonText):
182                 text = wx.TextCtrl(self, -1, value)
183                 button = wx.Button(self, -1, buttonText)
184                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
185                 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
186                 self.rowNr += 1
187                 return text, button
188
189         def AddBitmap(self, bitmap):
190                 bitmap = wx.StaticBitmap(self, -1, bitmap)
191                 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
192                 self.rowNr += 1
193                 return bitmap
194
195         def AddCheckmark(self, label, bitmap):
196                 check = wx.StaticBitmap(self, -1, bitmap)
197                 text = wx.StaticText(self, -1, label)
198                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
199                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
200                 self.rowNr += 1
201                 return check
202
203         def AllowNext(self):
204                 return True
205
206         def StoreData(self):
207                 pass
208
209
210 class FirstInfoPage(InfoPage):
211         def __init__(self, parent):
212                 super(FirstInfoPage, self).__init__(parent, "First time run wizard")
213                 self.AddText('Welcome, and thanks for trying Cura!')
214                 self.AddSeperator()
215                 self.AddText('This wizard will help you with the following steps:')
216                 self.AddText('* Configure Cura for your machine')
217                 self.AddText('* Upgrade your firmware')
218                 self.AddText('* Check if your machine is working safely')
219
220                 #self.AddText('* Calibrate your machine')
221                 #self.AddText('* Do your first print')
222
223
224 class RepRapInfoPage(InfoPage):
225         def __init__(self, parent):
226                 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
227                 self.AddText(
228                         'RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.')
229                 self.AddText('If you like a default profile for your machine added,\nthen make an issue on github.')
230                 self.AddSeperator()
231                 self.AddText('You will have to manually install Marlin or Sprinter firmware.')
232                 self.AddSeperator()
233                 self.machineWidth = self.AddLabelTextCtrl('Machine width (mm)', '80')
234                 self.machineDepth = self.AddLabelTextCtrl('Machine depth (mm)', '80')
235                 self.machineHeight = self.AddLabelTextCtrl('Machine height (mm)', '60')
236                 self.nozzleSize = self.AddLabelTextCtrl('Nozzle size (mm)', '0.5')
237                 self.heatedBed = self.AddCheckbox('Heated bed')
238
239         def StoreData(self):
240                 profile.putPreference('machine_width', self.machineWidth.GetValue())
241                 profile.putPreference('machine_depth', self.machineDepth.GetValue())
242                 profile.putPreference('machine_height', self.machineHeight.GetValue())
243                 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
244                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
245                 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
246
247
248 class MachineSelectPage(InfoPage):
249         def __init__(self, parent):
250                 super(MachineSelectPage, self).__init__(parent, "Select your machine")
251                 self.AddText('What kind of machine do you have:')
252
253                 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
254                 self.UltimakerRadio.SetValue(True)
255                 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
256                 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
257                 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
258
259         def OnUltimakerSelect(self, e):
260                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
261
262         def OnOtherSelect(self, e):
263                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
264
265         def StoreData(self):
266                 if self.UltimakerRadio.GetValue():
267                         profile.putPreference('machine_width', '205')
268                         profile.putPreference('machine_depth', '205')
269                         profile.putPreference('machine_height', '200')
270                         profile.putPreference('machine_type', 'ultimaker')
271                         profile.putProfileSetting('nozzle_size', '0.4')
272                 else:
273                         profile.putPreference('machine_width', '80')
274                         profile.putPreference('machine_depth', '80')
275                         profile.putPreference('machine_height', '60')
276                         profile.putPreference('machine_type', 'reprap')
277                         profile.putPreference('startMode', 'Normal')
278                         profile.putProfileSetting('nozzle_size', '0.5')
279                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
280
281
282 class SelectParts(InfoPage):
283         def __init__(self, parent):
284                 super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
285                 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.')
286                 self.AddSeperator()
287                 self.springExtruder = self.AddCheckbox('Extruder drive upgrade')
288                 self.heatedBed = self.AddCheckbox('Heated printer bed (self build)')
289                 self.dualExtrusion = self.AddCheckbox('Dual extrusion (experimental)')
290                 self.AddSeperator()
291                 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 reliablity.')
292                 self.AddText('This upgrade can be bought from the Ultimaker webshop shop\nor found on thingiverse as thing:26094')
293                 self.springExtruder.SetValue(True)
294
295         def StoreData(self):
296                 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
297                 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
298                 if self.dualExtrusion.GetValue():
299                         profile.putPreference('extruder_amount', '2')
300                 if getPreference('ultimaker_extruder_upgrade') == 'True':
301                         putProfileSetting('retraction_enable', 'True')
302
303
304 class FirmwareUpgradePage(InfoPage):
305         def __init__(self, parent):
306                 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
307                 self.AddText(
308                         '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.')
309                 self.AddHiddenSeperator()
310                 self.AddText(
311                         'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
312                 self.AddHiddenSeperator()
313                 self.AddText(
314                         '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.')
315                 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
316                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
317                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
318                 self.AddHiddenSeperator()
319                 self.AddText('Do not upgrade to this firmware if:')
320                 self.AddText('* You have an older machine based on ATMega1280')
321                 self.AddText('* Have other changes in the firmware')
322                 button = self.AddButton('Goto this page for a custom firmware')
323                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
324
325         def AllowNext(self):
326                 return False
327
328         def OnUpgradeClick(self, e):
329                 if firmwareInstall.InstallFirmware():
330                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
331
332         def OnSkipClick(self, e):
333                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
334
335         def OnUrlClick(self, e):
336                 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
337
338
339 class UltimakerCheckupPage(InfoPage):
340         def __init__(self, parent):
341                 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
342
343                 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
344                 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
345                 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
346                 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
347                 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
348                 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
349                 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
350                 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
351                 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
352                 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
353
354                 self.AddText(
355                         '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.')
356                 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
357                 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
358                 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
359                 self.AddSeperator()
360                 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
361                 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
362                 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
363                 self.AddSeperator()
364                 self.infoBox = self.AddInfoBox()
365                 self.machineState = self.AddText('')
366                 self.temperatureLabel = self.AddText('')
367                 self.errorLogButton = self.AddButton('Show error log')
368                 self.errorLogButton.Show(False)
369                 self.AddSeperator()
370                 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
371                 self.comm = None
372                 self.xMinStop = False
373                 self.xMaxStop = False
374                 self.yMinStop = False
375                 self.yMaxStop = False
376                 self.zMinStop = False
377                 self.zMaxStop = False
378
379                 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
380
381         def __del__(self):
382                 if self.comm != None:
383                         self.comm.close()
384
385         def AllowNext(self):
386                 self.endstopBitmap.Show(False)
387                 return False
388
389         def OnSkipClick(self, e):
390                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
391
392         def OnCheckClick(self, e=None):
393                 self.errorLogButton.Show(False)
394                 if self.comm != None:
395                         self.comm.close()
396                         del self.comm
397                         self.comm = None
398                         wx.CallAfter(self.OnCheckClick)
399                         return
400                 self.infoBox.SetInfo('Connecting to machine.')
401                 self.infoBox.SetBusyIndicator()
402                 self.commState.SetBitmap(self.unknownBitmap)
403                 self.tempState.SetBitmap(self.unknownBitmap)
404                 self.stopState.SetBitmap(self.unknownBitmap)
405                 self.checkupState = 0
406                 self.comm = machineCom.MachineCom(callbackObject=self)
407
408         def OnErrorLog(self, e):
409                 printWindow.LogWindow('\n'.join(self.comm.getLog()))
410
411         def mcLog(self, message):
412                 pass
413
414         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
415                 if not self.comm.isOperational():
416                         return
417                 if self.checkupState == 0:
418                         self.tempCheckTimeout = 20
419                         if temp > 70:
420                                 self.checkupState = 1
421                                 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
422                                 self.comm.sendCommand('M104 S0')
423                                 self.comm.sendCommand('M104 S0')
424                         else:
425                                 self.startTemp = temp
426                                 self.checkupState = 2
427                                 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
428                                 self.comm.sendCommand('M104 S200')
429                                 self.comm.sendCommand('M104 S200')
430                 elif self.checkupState == 1:
431                         if temp < 60:
432                                 self.startTemp = temp
433                                 self.checkupState = 2
434                                 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
435                                 self.comm.sendCommand('M104 S200')
436                                 self.comm.sendCommand('M104 S200')
437                 elif self.checkupState == 2:
438                         #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
439                         if temp > self.startTemp + 40:
440                                 self.checkupState = 3
441                                 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
442                                 wx.CallAfter(self.endstopBitmap.Show, True)
443                                 wx.CallAfter(self.Layout)
444                                 self.comm.sendCommand('M104 S0')
445                                 self.comm.sendCommand('M104 S0')
446                                 self.comm.sendCommand('M119')
447                                 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
448                         else:
449                                 self.tempCheckTimeout -= 1
450                                 if self.tempCheckTimeout < 1:
451                                         self.checkupState = -1
452                                         wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
453                                         wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura/Temperature_measurement_problems')
454                                         self.comm.sendCommand('M104 S0')
455                                         self.comm.sendCommand('M104 S0')
456                 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
457
458         def mcStateChange(self, state):
459                 if self.comm == None:
460                         return
461                 if self.comm.isOperational():
462                         wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
463                         wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
464                 elif self.comm.isError():
465                         wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
466                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura/Connection_problems')
467                         wx.CallAfter(self.endstopBitmap.Show, False)
468                         wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
469                         wx.CallAfter(self.errorLogButton.Show, True)
470                         wx.CallAfter(self.Layout)
471                 else:
472                         wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
473
474         def mcMessage(self, message):
475                 if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message:
476                         for data in message.split(' '):
477                                 if ':' in data:
478                                         tag, value = data.split(':', 2)
479                                         if tag == 'x_min':
480                                                 self.xMinStop = (value == 'H')
481                                         if tag == 'x_max':
482                                                 self.xMaxStop = (value == 'H')
483                                         if tag == 'y_min':
484                                                 self.yMinStop = (value == 'H')
485                                         if tag == 'y_max':
486                                                 self.yMaxStop = (value == 'H')
487                                         if tag == 'z_min':
488                                                 self.zMinStop = (value == 'H')
489                                         if tag == 'z_max':
490                                                 self.zMaxStop = (value == 'H')
491                         self.comm.sendCommand('M119')
492
493                         if self.checkupState == 3:
494                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
495                                         self.checkupState = 4
496                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
497                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
498                         elif self.checkupState == 4:
499                                 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
500                                         self.checkupState = 5
501                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
502                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
503                         elif self.checkupState == 5:
504                                 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
505                                         self.checkupState = 6
506                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
507                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
508                         elif self.checkupState == 6:
509                                 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
510                                         self.checkupState = 7
511                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
512                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
513                         elif self.checkupState == 7:
514                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
515                                         self.checkupState = 8
516                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
517                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
518                         elif self.checkupState == 8:
519                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
520                                         self.checkupState = 9
521                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
522                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
523                         elif self.checkupState == 9:
524                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
525                                         self.checkupState = 10
526                                         self.comm.close()
527                                         wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
528                                         wx.CallAfter(self.infoBox.SetReadyIndicator)
529                                         wx.CallAfter(self.endstopBitmap.Show, False)
530                                         wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
531                                         wx.CallAfter(self.OnSkipClick, None)
532
533         def mcProgress(self, lineNr):
534                 pass
535
536         def mcZChange(self, newZ):
537                 pass
538
539
540 class UltimakerCalibrationPage(InfoPage):
541         def __init__(self, parent):
542                 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
543
544                 self.AddText("Your Ultimaker requires some calibration.")
545                 self.AddText("This calibration is needed for a proper extrusion amount.")
546                 self.AddSeperator()
547                 self.AddText("The following values are needed:")
548                 self.AddText("* Diameter of filament")
549                 self.AddText("* Number of steps per mm of filament extrusion")
550                 self.AddSeperator()
551                 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
552                 self.AddSeperator()
553                 self.AddText("First we need the diameter of your filament:")
554                 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
555                 self.AddText(
556                         "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.")
557                 self.AddText("Note: This value can be changed later at any time.")
558
559         def StoreData(self):
560                 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
561
562
563 class UltimakerCalibrateStepsPerEPage(InfoPage):
564         def __init__(self, parent):
565                 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
566
567                 if profile.getPreference('steps_per_e') == '0':
568                         profile.putPreference('steps_per_e', '865.888')
569
570                 self.AddText("Calibrating the Steps Per E requires some manual actions.")
571                 self.AddText("First remove any filament from your machine.")
572                 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
573                 self.AddText("We'll push the filament 100mm")
574                 self.extrudeButton = self.AddButton("Extrude 100mm filament")
575                 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
576                 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
577                 self.AddText("This results in the following steps per E:")
578                 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
579                 self.AddText("You can repeat these steps to get better calibration.")
580                 self.AddSeperator()
581                 self.AddText(
582                         "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
583                 self.heatButton = self.AddButton("Heatup for filament removal")
584
585                 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
586                 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
587                 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
588
589         def OnSaveLengthClick(self, e):
590                 currentEValue = float(self.stepsPerEInput.GetValue())
591                 realExtrudeLength = float(self.lengthInput.GetValue())
592                 newEValue = currentEValue * 100 / realExtrudeLength
593                 self.stepsPerEInput.SetValue(str(newEValue))
594                 self.lengthInput.SetValue("100")
595
596         def OnExtrudeClick(self, e):
597                 threading.Thread(target=self.OnExtrudeRun).start()
598
599         def OnExtrudeRun(self):
600                 self.heatButton.Enable(False)
601                 self.extrudeButton.Enable(False)
602                 currentEValue = float(self.stepsPerEInput.GetValue())
603                 self.comm = machineCom.MachineCom()
604                 if not self.comm.isOpen():
605                         wx.MessageBox(
606                                 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
607                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
608                         self.heatButton.Enable(True)
609                         self.extrudeButton.Enable(True)
610                         return
611                 while True:
612                         line = self.comm.readline()
613                         if line == '':
614                                 return
615                         if 'start' in line:
616                                 break
617                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
618                 time.sleep(3)
619
620                 self.sendGCommand('M302') #Disable cold extrusion protection
621                 self.sendGCommand("M92 E%f" % (currentEValue))
622                 self.sendGCommand("G92 E0")
623                 self.sendGCommand("G1 E100 F600")
624                 time.sleep(15)
625                 self.comm.close()
626                 self.extrudeButton.Enable()
627                 self.heatButton.Enable()
628
629         def OnHeatClick(self, e):
630                 threading.Thread(target=self.OnHeatRun).start()
631
632         def OnHeatRun(self):
633                 self.heatButton.Enable(False)
634                 self.extrudeButton.Enable(False)
635                 self.comm = machineCom.MachineCom()
636                 if not self.comm.isOpen():
637                         wx.MessageBox(
638                                 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
639                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
640                         self.heatButton.Enable(True)
641                         self.extrudeButton.Enable(True)
642                         return
643                 while True:
644                         line = self.comm.readline()
645                         if line == '':
646                                 self.heatButton.Enable(True)
647                                 self.extrudeButton.Enable(True)
648                                 return
649                         if 'start' in line:
650                                 break
651                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
652                 time.sleep(3)
653
654                 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
655                 wx.MessageBox(
656                         'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
657                         'Machine heatup', wx.OK | wx.ICON_INFORMATION)
658                 self.sendGCommand('M104 S0')
659                 time.sleep(1)
660                 self.comm.close()
661                 self.heatButton.Enable(True)
662                 self.extrudeButton.Enable(True)
663
664         def sendGCommand(self, cmd):
665                 self.comm.sendCommand(cmd) #Disable cold extrusion protection
666                 while True:
667                         line = self.comm.readline()
668                         if line == '':
669                                 return
670                         if line.startswith('ok'):
671                                 break
672
673         def StoreData(self):
674                 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
675
676
677 class configWizard(wx.wizard.Wizard):
678         def __init__(self):
679                 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
680
681                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
682                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
683
684                 self.firstInfoPage = FirstInfoPage(self)
685                 self.machineSelectPage = MachineSelectPage(self)
686                 self.ultimakerSelectParts = SelectParts(self)
687                 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
688                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
689                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
690                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
691                 self.repRapInfoPage = RepRapInfoPage(self)
692
693                 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
694                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
695                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
696                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
697                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage)
698                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
699
700                 self.FitToPage(self.firstInfoPage)
701                 self.GetPageAreaSizer().Add(self.firstInfoPage)
702
703                 self.RunWizard(self.firstInfoPage)
704                 self.Destroy()
705
706         def OnPageChanging(self, e):
707                 e.GetPage().StoreData()
708
709         def OnPageChanged(self, e):
710                 if e.GetPage().AllowNext():
711                         self.FindWindowById(wx.ID_FORWARD).Enable()
712                 else:
713                         self.FindWindowById(wx.ID_FORWARD).Disable()
714                 self.FindWindowById(wx.ID_BACKWARD).Disable()