chiark / gitweb /
Add missing else statements in initial wizard
[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 import math
8
9 import wx
10 import wx.wizard
11
12 from Cura.gui import firmwareInstall
13 from Cura.gui import printWindow
14 from Cura.util import machineCom
15 from Cura.util import profile
16 from Cura.util.resources import getPathForImage
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.Layout()
74                 self.Refresh()
75
76         def SetBusy(self, info):
77                 self.SetInfo(info)
78                 self.SetBusyIndicator()
79
80         def SetBusyIndicator(self):
81                 self.busyState = 0
82                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
83
84         def doExtraInfo(self, e):
85                 webbrowser.open(self.extraInfoUrl)
86
87         def doBusyUpdate(self, e):
88                 if self.busyState is None:
89                         return
90                 self.busyState += 1
91                 if self.busyState >= len(self.busyBitmap):
92                         self.busyState = 0
93                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
94
95         def SetReadyIndicator(self):
96                 self.busyState = None
97                 self.bitmap.SetBitmap(self.readyBitmap)
98
99         def SetErrorIndicator(self):
100                 self.busyState = None
101                 self.bitmap.SetBitmap(self.errorBitmap)
102
103         def SetAttentionIndicator(self):
104                 self.busyState = None
105                 self.bitmap.SetBitmap(self.attentionBitmap)
106
107
108 class InfoPage(wx.wizard.WizardPageSimple):
109         def __init__(self, parent, title):
110                 wx.wizard.WizardPageSimple.__init__(self, parent)
111
112                 sizer = wx.GridBagSizer(5, 5)
113                 self.sizer = sizer
114                 self.SetSizer(sizer)
115
116                 title = wx.StaticText(self, -1, title)
117                 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
118                 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
119                 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
120                 sizer.AddGrowableCol(1)
121
122                 self.rowNr = 2
123
124         def AddText(self, info):
125                 text = wx.StaticText(self, -1, info)
126                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
127                 self.rowNr += 1
128                 return text
129
130         def AddSeperator(self):
131                 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
132                 self.rowNr += 1
133
134         def AddHiddenSeperator(self):
135                 self.AddText('')
136
137         def AddInfoBox(self):
138                 infoBox = InfoBox(self)
139                 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
140                 self.rowNr += 1
141                 return infoBox
142
143         def AddRadioButton(self, label, style=0):
144                 radio = wx.RadioButton(self, -1, label, style=style)
145                 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
146                 self.rowNr += 1
147                 return radio
148
149         def AddCheckbox(self, label, checked=False):
150                 check = wx.CheckBox(self, -1)
151                 text = wx.StaticText(self, -1, label)
152                 check.SetValue(checked)
153                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
154                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
155                 self.rowNr += 1
156                 return check
157
158         def AddButton(self, label):
159                 button = wx.Button(self, -1, label)
160                 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
161                 self.rowNr += 1
162                 return button
163
164         def AddDualButton(self, label1, label2):
165                 button1 = wx.Button(self, -1, label1)
166                 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
167                 button2 = wx.Button(self, -1, label2)
168                 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
169                 self.rowNr += 1
170                 return button1, button2
171
172         def AddTextCtrl(self, value):
173                 ret = wx.TextCtrl(self, -1, value)
174                 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
175                 self.rowNr += 1
176                 return ret
177
178         def AddLabelTextCtrl(self, info, value):
179                 text = wx.StaticText(self, -1, info)
180                 ret = wx.TextCtrl(self, -1, value)
181                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
182                 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
183                 self.rowNr += 1
184                 return ret
185
186         def AddTextCtrlButton(self, value, buttonText):
187                 text = wx.TextCtrl(self, -1, value)
188                 button = wx.Button(self, -1, buttonText)
189                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
190                 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
191                 self.rowNr += 1
192                 return text, button
193
194         def AddBitmap(self, bitmap):
195                 bitmap = wx.StaticBitmap(self, -1, bitmap)
196                 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
197                 self.rowNr += 1
198                 return bitmap
199
200         def AddCheckmark(self, label, bitmap):
201                 check = wx.StaticBitmap(self, -1, bitmap)
202                 text = wx.StaticText(self, -1, label)
203                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
204                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
205                 self.rowNr += 1
206                 return check
207
208         def AllowNext(self):
209                 return True
210
211         def StoreData(self):
212                 pass
213
214
215 class FirstInfoPage(InfoPage):
216         def __init__(self, parent):
217                 super(FirstInfoPage, self).__init__(parent, "First time run wizard")
218                 self.AddText('Welcome, and thanks for trying Cura!')
219                 self.AddSeperator()
220                 self.AddText('This wizard will help you with the following steps:')
221                 self.AddText('* Configure Cura for your machine')
222                 self.AddText('* Upgrade your firmware')
223                 self.AddText('* Check if your machine is working safely')
224                 self.AddText('* Level your printer bed')
225
226                 #self.AddText('* Calibrate your machine')
227                 #self.AddText('* Do your first print')
228
229
230 class RepRapInfoPage(InfoPage):
231         def __init__(self, parent):
232                 super(RepRapInfoPage, self).__init__(parent, "RepRap information")
233                 self.AddText(
234                         'RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.')
235                 self.AddText('If you like a default profile for your machine added,\nthen make an issue on github.')
236                 self.AddSeperator()
237                 self.AddText('You will have to manually install Marlin or Sprinter firmware.')
238                 self.AddSeperator()
239                 self.machineWidth = self.AddLabelTextCtrl('Machine width (mm)', '80')
240                 self.machineDepth = self.AddLabelTextCtrl('Machine depth (mm)', '80')
241                 self.machineHeight = self.AddLabelTextCtrl('Machine height (mm)', '60')
242                 self.nozzleSize = self.AddLabelTextCtrl('Nozzle size (mm)', '0.5')
243                 self.heatedBed = self.AddCheckbox('Heated bed')
244
245         def StoreData(self):
246                 profile.putPreference('machine_width', self.machineWidth.GetValue())
247                 profile.putPreference('machine_depth', self.machineDepth.GetValue())
248                 profile.putPreference('machine_height', self.machineHeight.GetValue())
249                 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
250                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
251                 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
252
253
254 class MachineSelectPage(InfoPage):
255         def __init__(self, parent):
256                 super(MachineSelectPage, self).__init__(parent, "Select your machine")
257                 self.AddText('What kind of machine do you have:')
258
259                 self.UltimakerRadio = self.AddRadioButton("Ultimaker", style=wx.RB_GROUP)
260                 self.UltimakerRadio.SetValue(True)
261                 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
262                 self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
263                 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
264
265         def OnUltimakerSelect(self, e):
266                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
267
268         def OnOtherSelect(self, e):
269                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
270
271         def StoreData(self):
272                 if self.UltimakerRadio.GetValue():
273                         profile.putPreference('machine_width', '205')
274                         profile.putPreference('machine_depth', '205')
275                         profile.putPreference('machine_height', '200')
276                         profile.putPreference('machine_type', 'ultimaker')
277                         profile.putProfileSetting('nozzle_size', '0.4')
278                 else:
279                         profile.putPreference('machine_width', '80')
280                         profile.putPreference('machine_depth', '80')
281                         profile.putPreference('machine_height', '60')
282                         profile.putPreference('machine_type', 'reprap')
283                         profile.putPreference('startMode', 'Normal')
284                         profile.putProfileSetting('nozzle_size', '0.5')
285                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
286
287
288 class SelectParts(InfoPage):
289         def __init__(self, parent):
290                 super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
291                 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.')
292                 self.AddSeperator()
293                 self.springExtruder = self.AddCheckbox('Extruder drive upgrade')
294                 self.heatedBed = self.AddCheckbox('Heated printer bed (self build)')
295                 self.dualExtrusion = self.AddCheckbox('Dual extrusion (experimental)')
296                 self.AddSeperator()
297                 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.')
298                 self.AddText('This upgrade can be bought from the Ultimaker webshop shop\nor found on thingiverse as thing:26094')
299                 self.springExtruder.SetValue(True)
300
301         def StoreData(self):
302                 profile.putPreference('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
303                 profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
304                 if self.dualExtrusion.GetValue():
305                         profile.putPreference('extruder_amount', '2')
306                 else:
307                         profile.putPreference('extruder_amount', '1')
308                 if profile.getPreference('ultimaker_extruder_upgrade') == 'True':
309                         profile.putProfileSetting('retraction_enable', 'True')
310                 else:
311                         profile.putProfileSetting('retraction_enable', 'False')
312
313
314 class FirmwareUpgradePage(InfoPage):
315         def __init__(self, parent):
316                 super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
317                 self.AddText(
318                         '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.')
319                 self.AddHiddenSeperator()
320                 self.AddText(
321                         'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
322                 self.AddHiddenSeperator()
323                 self.AddText(
324                         '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.')
325                 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
326                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
327                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
328                 self.AddHiddenSeperator()
329                 self.AddText('Do not upgrade to this firmware if:')
330                 self.AddText('* You have an older machine based on ATMega1280')
331                 self.AddText('* Have other changes in the firmware')
332                 button = self.AddButton('Goto this page for a custom firmware')
333                 button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
334
335         def AllowNext(self):
336                 return False
337
338         def OnUpgradeClick(self, e):
339                 if firmwareInstall.InstallFirmware():
340                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
341
342         def OnSkipClick(self, e):
343                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
344
345         def OnUrlClick(self, e):
346                 webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
347
348
349 class UltimakerCheckupPage(InfoPage):
350         def __init__(self, parent):
351                 super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
352
353                 self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
354                 self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
355                 self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
356                 self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
357                 self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
358                 self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
359                 self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
360                 self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
361                 self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
362                 self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
363
364                 self.AddText(
365                         '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.')
366                 b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
367                 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
368                 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
369                 self.AddSeperator()
370                 self.commState = self.AddCheckmark('Communication:', self.unknownBitmap)
371                 self.tempState = self.AddCheckmark('Temperature:', self.unknownBitmap)
372                 self.stopState = self.AddCheckmark('Endstops:', self.unknownBitmap)
373                 self.AddSeperator()
374                 self.infoBox = self.AddInfoBox()
375                 self.machineState = self.AddText('')
376                 self.temperatureLabel = self.AddText('')
377                 self.errorLogButton = self.AddButton('Show error log')
378                 self.errorLogButton.Show(False)
379                 self.AddSeperator()
380                 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
381                 self.comm = None
382                 self.xMinStop = False
383                 self.xMaxStop = False
384                 self.yMinStop = False
385                 self.yMaxStop = False
386                 self.zMinStop = False
387                 self.zMaxStop = False
388
389                 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
390
391         def __del__(self):
392                 if self.comm != None:
393                         self.comm.close()
394
395         def AllowNext(self):
396                 self.endstopBitmap.Show(False)
397                 return False
398
399         def OnSkipClick(self, e):
400                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
401
402         def OnCheckClick(self, e=None):
403                 self.errorLogButton.Show(False)
404                 if self.comm is not None:
405                         self.comm.close()
406                         del self.comm
407                         self.comm = None
408                         wx.CallAfter(self.OnCheckClick)
409                         return
410                 self.infoBox.SetBusy('Connecting to machine.')
411                 self.commState.SetBitmap(self.unknownBitmap)
412                 self.tempState.SetBitmap(self.unknownBitmap)
413                 self.stopState.SetBitmap(self.unknownBitmap)
414                 self.checkupState = 0
415                 self.comm = machineCom.MachineCom(callbackObject=self)
416
417         def OnErrorLog(self, e):
418                 printWindow.LogWindow('\n'.join(self.comm.getLog()))
419
420         def mcLog(self, message):
421                 pass
422
423         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
424                 if not self.comm.isOperational():
425                         return
426                 if self.checkupState == 0:
427                         self.tempCheckTimeout = 20
428                         if temp > 70:
429                                 self.checkupState = 1
430                                 wx.CallAfter(self.infoBox.SetInfo, 'Cooldown before temperature check.')
431                                 self.comm.sendCommand('M104 S0')
432                                 self.comm.sendCommand('M104 S0')
433                         else:
434                                 self.startTemp = temp
435                                 self.checkupState = 2
436                                 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
437                                 self.comm.sendCommand('M104 S200')
438                                 self.comm.sendCommand('M104 S200')
439                 elif self.checkupState == 1:
440                         if temp < 60:
441                                 self.startTemp = temp
442                                 self.checkupState = 2
443                                 wx.CallAfter(self.infoBox.SetInfo, 'Checking the heater and temperature sensor.')
444                                 self.comm.sendCommand('M104 S200')
445                                 self.comm.sendCommand('M104 S200')
446                 elif self.checkupState == 2:
447                         #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
448                         if temp > self.startTemp + 40:
449                                 self.checkupState = 3
450                                 wx.CallAfter(self.infoBox.SetAttention, 'Please make sure none of the endstops are pressed.')
451                                 wx.CallAfter(self.endstopBitmap.Show, True)
452                                 wx.CallAfter(self.Layout)
453                                 self.comm.sendCommand('M104 S0')
454                                 self.comm.sendCommand('M104 S0')
455                                 self.comm.sendCommand('M119')
456                                 wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
457                         else:
458                                 self.tempCheckTimeout -= 1
459                                 if self.tempCheckTimeout < 1:
460                                         self.checkupState = -1
461                                         wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
462                                         wx.CallAfter(self.infoBox.SetError, 'Temperature measurement FAILED!', 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
463                                         self.comm.sendCommand('M104 S0')
464                                         self.comm.sendCommand('M104 S0')
465                 wx.CallAfter(self.temperatureLabel.SetLabel, 'Head temperature: %d' % (temp))
466
467         def mcStateChange(self, state):
468                 if self.comm is None:
469                         return
470                 if self.comm.isOperational():
471                         wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
472                         wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
473                 elif self.comm.isError():
474                         wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
475                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
476                         wx.CallAfter(self.endstopBitmap.Show, False)
477                         wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
478                         wx.CallAfter(self.errorLogButton.Show, True)
479                         wx.CallAfter(self.Layout)
480                 else:
481                         wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
482
483         def mcMessage(self, message):
484                 if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message:
485                         for data in message.split(' '):
486                                 if ':' in data:
487                                         tag, value = data.split(':', 2)
488                                         if tag == 'x_min':
489                                                 self.xMinStop = (value == 'H')
490                                         if tag == 'x_max':
491                                                 self.xMaxStop = (value == 'H')
492                                         if tag == 'y_min':
493                                                 self.yMinStop = (value == 'H')
494                                         if tag == 'y_max':
495                                                 self.yMaxStop = (value == 'H')
496                                         if tag == 'z_min':
497                                                 self.zMinStop = (value == 'H')
498                                         if tag == 'z_max':
499                                                 self.zMaxStop = (value == 'H')
500                         self.comm.sendCommand('M119')
501
502                         if self.checkupState == 3:
503                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
504                                         self.checkupState = 4
505                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the right X endstop.')
506                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
507                         elif self.checkupState == 4:
508                                 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
509                                         self.checkupState = 5
510                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the left X endstop.')
511                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
512                         elif self.checkupState == 5:
513                                 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
514                                         self.checkupState = 6
515                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the front Y endstop.')
516                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
517                         elif self.checkupState == 6:
518                                 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
519                                         self.checkupState = 7
520                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the back Y endstop.')
521                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
522                         elif self.checkupState == 7:
523                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
524                                         self.checkupState = 8
525                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the top Z endstop.')
526                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
527                         elif self.checkupState == 8:
528                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
529                                         self.checkupState = 9
530                                         wx.CallAfter(self.infoBox.SetAttention, 'Please press the bottom Z endstop.')
531                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
532                         elif self.checkupState == 9:
533                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
534                                         self.checkupState = 10
535                                         self.comm.close()
536                                         wx.CallAfter(self.infoBox.SetInfo, 'Checkup finished')
537                                         wx.CallAfter(self.infoBox.SetReadyIndicator)
538                                         wx.CallAfter(self.endstopBitmap.Show, False)
539                                         wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
540                                         wx.CallAfter(self.OnSkipClick, None)
541
542         def mcProgress(self, lineNr):
543                 pass
544
545         def mcZChange(self, newZ):
546                 pass
547
548
549 class UltimakerCalibrationPage(InfoPage):
550         def __init__(self, parent):
551                 super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
552
553                 self.AddText("Your Ultimaker requires some calibration.")
554                 self.AddText("This calibration is needed for a proper extrusion amount.")
555                 self.AddSeperator()
556                 self.AddText("The following values are needed:")
557                 self.AddText("* Diameter of filament")
558                 self.AddText("* Number of steps per mm of filament extrusion")
559                 self.AddSeperator()
560                 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
561                 self.AddSeperator()
562                 self.AddText("First we need the diameter of your filament:")
563                 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
564                 self.AddText(
565                         "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.")
566                 self.AddText("Note: This value can be changed later at any time.")
567
568         def StoreData(self):
569                 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
570
571
572 class UltimakerCalibrateStepsPerEPage(InfoPage):
573         def __init__(self, parent):
574                 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
575
576                 if profile.getPreference('steps_per_e') == '0':
577                         profile.putPreference('steps_per_e', '865.888')
578
579                 self.AddText("Calibrating the Steps Per E requires some manual actions.")
580                 self.AddText("First remove any filament from your machine.")
581                 self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
582                 self.AddText("We'll push the filament 100mm")
583                 self.extrudeButton = self.AddButton("Extrude 100mm filament")
584                 self.AddText("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)")
585                 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton('100', 'Save')
586                 self.AddText("This results in the following steps per E:")
587                 self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
588                 self.AddText("You can repeat these steps to get better calibration.")
589                 self.AddSeperator()
590                 self.AddText(
591                         "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
592                 self.heatButton = self.AddButton("Heatup for filament removal")
593
594                 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
595                 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
596                 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
597
598         def OnSaveLengthClick(self, e):
599                 currentEValue = float(self.stepsPerEInput.GetValue())
600                 realExtrudeLength = float(self.lengthInput.GetValue())
601                 newEValue = currentEValue * 100 / realExtrudeLength
602                 self.stepsPerEInput.SetValue(str(newEValue))
603                 self.lengthInput.SetValue("100")
604
605         def OnExtrudeClick(self, e):
606                 threading.Thread(target=self.OnExtrudeRun).start()
607
608         def OnExtrudeRun(self):
609                 self.heatButton.Enable(False)
610                 self.extrudeButton.Enable(False)
611                 currentEValue = float(self.stepsPerEInput.GetValue())
612                 self.comm = machineCom.MachineCom()
613                 if not self.comm.isOpen():
614                         wx.MessageBox(
615                                 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
616                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
617                         self.heatButton.Enable(True)
618                         self.extrudeButton.Enable(True)
619                         return
620                 while True:
621                         line = self.comm.readline()
622                         if line == '':
623                                 return
624                         if 'start' in line:
625                                 break
626                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
627                 time.sleep(3)
628
629                 self.sendGCommand('M302') #Disable cold extrusion protection
630                 self.sendGCommand("M92 E%f" % (currentEValue))
631                 self.sendGCommand("G92 E0")
632                 self.sendGCommand("G1 E100 F600")
633                 time.sleep(15)
634                 self.comm.close()
635                 self.extrudeButton.Enable()
636                 self.heatButton.Enable()
637
638         def OnHeatClick(self, e):
639                 threading.Thread(target=self.OnHeatRun).start()
640
641         def OnHeatRun(self):
642                 self.heatButton.Enable(False)
643                 self.extrudeButton.Enable(False)
644                 self.comm = machineCom.MachineCom()
645                 if not self.comm.isOpen():
646                         wx.MessageBox(
647                                 "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
648                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
649                         self.heatButton.Enable(True)
650                         self.extrudeButton.Enable(True)
651                         return
652                 while True:
653                         line = self.comm.readline()
654                         if line == '':
655                                 self.heatButton.Enable(True)
656                                 self.extrudeButton.Enable(True)
657                                 return
658                         if 'start' in line:
659                                 break
660                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
661                 time.sleep(3)
662
663                 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
664                 wx.MessageBox(
665                         'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
666                         'Machine heatup', wx.OK | wx.ICON_INFORMATION)
667                 self.sendGCommand('M104 S0')
668                 time.sleep(1)
669                 self.comm.close()
670                 self.heatButton.Enable(True)
671                 self.extrudeButton.Enable(True)
672
673         def sendGCommand(self, cmd):
674                 self.comm.sendCommand(cmd) #Disable cold extrusion protection
675                 while True:
676                         line = self.comm.readline()
677                         if line == '':
678                                 return
679                         if line.startswith('ok'):
680                                 break
681
682         def StoreData(self):
683                 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
684
685
686 class configWizard(wx.wizard.Wizard):
687         def __init__(self):
688                 super(configWizard, self).__init__(None, -1, "Configuration Wizard")
689
690                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
691                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
692
693                 self.firstInfoPage = FirstInfoPage(self)
694                 self.machineSelectPage = MachineSelectPage(self)
695                 self.ultimakerSelectParts = SelectParts(self)
696                 self.ultimakerFirmwareUpgradePage = FirmwareUpgradePage(self)
697                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
698                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
699                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
700                 self.bedLevelPage = bedLevelWizardMain(self)
701                 self.repRapInfoPage = RepRapInfoPage(self)
702
703                 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
704                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
705                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
706                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
707                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
708                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
709
710                 self.FitToPage(self.firstInfoPage)
711                 self.GetPageAreaSizer().Add(self.firstInfoPage)
712
713                 self.RunWizard(self.firstInfoPage)
714                 self.Destroy()
715
716         def OnPageChanging(self, e):
717                 e.GetPage().StoreData()
718
719         def OnPageChanged(self, e):
720                 if e.GetPage().AllowNext():
721                         self.FindWindowById(wx.ID_FORWARD).Enable()
722                 else:
723                         self.FindWindowById(wx.ID_FORWARD).Disable()
724                 self.FindWindowById(wx.ID_BACKWARD).Disable()
725
726 class bedLevelWizardMain(InfoPage):
727         def __init__(self, parent):
728                 super(bedLevelWizardMain, self).__init__(parent, "Bed leveling wizard")
729
730                 self.AddText('This wizard will help you in leveling your printer bed')
731                 self.AddSeperator()
732                 self.AddText('It will do the following steps')
733                 self.AddText('* Move the printer head to each corner')
734                 self.AddText('  and let you adjust the height of the bed to the nozzle')
735                 self.AddText('* Print a line around the bed to check if it is level')
736                 self.AddSeperator()
737
738                 self.connectButton = self.AddButton('Connect to printer')
739                 self.comm = None
740
741                 self.infoBox = self.AddInfoBox()
742                 self.resumeButton = self.AddButton('Resume')
743                 self.resumeButton.Enable(False)
744
745                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
746                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
747
748         def OnConnect(self, e = None):
749                 if self.comm is not None:
750                         self.comm.close()
751                         del self.comm
752                         self.comm = None
753                         wx.CallAfter(self.OnConnect)
754                         return
755                 self.connectButton.Enable(False)
756                 self.comm = machineCom.MachineCom(callbackObject=self)
757                 self.infoBox.SetBusy('Connecting to machine.')
758                 self._wizardState = 0
759
760         def AllowNext(self):
761                 return False
762
763         def OnResume(self, e):
764                 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
765                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
766                 if self._wizardState == 2:
767                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back left corner...')
768                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
769                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getPreferenceFloat('machine_depth'), feedTravel))
770                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
771                         self.comm.sendCommand('M400')
772                         self._wizardState = 3
773                 elif self._wizardState == 4:
774                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to back right corner...')
775                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
776                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth') - 20, feedTravel))
777                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
778                         self.comm.sendCommand('M400')
779                         self._wizardState = 5
780                 elif self._wizardState == 6:
781                         wx.CallAfter(self.infoBox.SetBusy, 'Moving head to front right corner...')
782                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
783                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getPreferenceFloat('machine_width'), 20, feedTravel))
784                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
785                         self.comm.sendCommand('M400')
786                         self._wizardState = 7
787                 elif self._wizardState == 8:
788                         wx.CallAfter(self.infoBox.SetBusy, 'Heating up printer...')
789                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
790                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
791                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
792                         self._wizardState = 9
793                 self.resumeButton.Enable(False)
794
795         def mcLog(self, message):
796                 print 'Log:', message
797
798         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
799                 if self._wizardState == 1:
800                         self._wizardState = 2
801                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.')
802                         wx.CallAfter(self.resumeButton.Enable, True)
803                 elif self._wizardState == 3:
804                         self._wizardState = 4
805                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.')
806                         wx.CallAfter(self.resumeButton.Enable, True)
807                 elif self._wizardState == 5:
808                         self._wizardState = 6
809                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.')
810                         wx.CallAfter(self.resumeButton.Enable, True)
811                 elif self._wizardState == 7:
812                         self._wizardState = 8
813                         wx.CallAfter(self.infoBox.SetAttention, 'Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.')
814                         wx.CallAfter(self.resumeButton.Enable, True)
815                 elif self._wizardState == 9:
816                         if temp < profile.getProfileSettingFloat('print_temperature') - 5:
817                                 wx.CallAfter(self.infoBox.SetInfo, 'Heating up printer: %d/%d' % (temp, profile.getProfileSettingFloat('print_temperature')))
818                         else:
819                                 self._wizardState = 10
820                                 wx.CallAfter(self.infoBox.SetInfo, 'Printing a square on the printer bed at 0.3mm height.')
821                                 feedZ = profile.getProfileSettingFloat('max_z_speed') * 60
822                                 feedPrint = profile.getProfileSettingFloat('print_speed') * 60
823                                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
824                                 w = profile.getPreferenceFloat('machine_width')
825                                 d = profile.getPreferenceFloat('machine_depth')
826                                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
827                                 filamentArea = math.pi * filamentRadius * filamentRadius
828                                 ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
829                                 eValue = 0.0
830
831                                 gcodeList = [
832                                         'G1 Z2 F%d' % (feedZ),
833                                         'G92 E0',
834                                         'G1 X%d Y%d F%d' % (5, 5, feedTravel),
835                                         'G1 Z0.3 F%d' % (feedZ)]
836                                 eValue += 5;
837                                 gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
838
839                                 for i in xrange(0, 3):
840                                         dist = 5.0 + 0.4 * i
841                                         eValue += (d - 2*dist) * ePerMM
842                                         gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, d - dist, eValue, feedPrint))
843                                         eValue += (w - 2*dist) * ePerMM
844                                         gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
845                                         eValue += (d - 2*dist) * ePerMM
846                                         gcodeList.append('G1 X%d Y%d E%f F%d' % (w - dist, dist, eValue, feedPrint))
847                                         eValue += (w - 2*dist) * ePerMM
848                                         gcodeList.append('G1 X%d Y%d E%f F%d' % (dist, dist, eValue, feedPrint))
849
850                                 gcodeList.append('M400')
851                                 self.comm.printGCode(gcodeList)
852
853         def mcStateChange(self, state):
854                 if self.comm is None:
855                         return
856                 if self.comm.isOperational():
857                         if self._wizardState == 0:
858                                 wx.CallAfter(self.infoBox.SetInfo, 'Homing printer...')
859                                 self.comm.sendCommand('G28')
860                                 self._wizardState = 1
861                         elif self._wizardState == 10 and not self.comm.isPrinting():
862                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('max_z_speed') * 60))
863                                 self.comm.sendCommand('G92 E0')
864                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
865                                 self.comm.sendCommand('M104 S0')
866                                 wx.CallAfter(self.infoBox.SetInfo, 'Calibration finished.\nThe squares on the bed should slightly touch each other.')
867                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
868                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
869                                 wx.CallAfter(self.connectButton.Enable, True)
870                                 self._wizardState = 11
871                 elif self.comm.isError():
872                         wx.CallAfter(self.infoBox.SetError, 'Failed to establish connection with the printer.', 'http://wiki.ultimaker.com/Cura:_Connection_problems')
873
874         def mcMessage(self, message):
875                 pass
876
877         def mcProgress(self, lineNr):
878                 pass
879
880         def mcZChange(self, newZ):
881                 pass
882
883 class bedLevelWizard(wx.wizard.Wizard):
884         def __init__(self):
885                 super(bedLevelWizard, self).__init__(None, -1, "Bed leveling wizard")
886
887                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
888                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
889
890                 self.mainPage = bedLevelWizardMain(self)
891
892                 self.FitToPage(self.mainPage)
893                 self.GetPageAreaSizer().Add(self.mainPage)
894
895                 self.RunWizard(self.mainPage)
896                 self.Destroy()
897
898         def OnPageChanging(self, e):
899                 e.GetPage().StoreData()
900
901         def OnPageChanged(self, e):
902                 if e.GetPage().AllowNext():
903                         self.FindWindowById(wx.ID_FORWARD).Enable()
904                 else:
905                         self.FindWindowById(wx.ID_FORWARD).Disable()
906                 self.FindWindowById(wx.ID_BACKWARD).Disable()