chiark / gitweb /
Merge branch 'SteamEngine' of github.com:daid/Cura into SteamEngine
[cura.git] / Cura / gui / configWizard.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import os
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 import gcodeGenerator
17 from Cura.util import resources
18
19
20 class InfoBox(wx.Panel):
21         def __init__(self, parent):
22                 super(InfoBox, self).__init__(parent)
23                 self.SetBackgroundColour('#FFFF80')
24
25                 self.sizer = wx.GridBagSizer(5, 5)
26                 self.SetSizer(self.sizer)
27
28                 self.attentionBitmap = wx.Bitmap(resources.getPathForImage('attention.png'))
29                 self.errorBitmap = wx.Bitmap(resources.getPathForImage('error.png'))
30                 self.readyBitmap = wx.Bitmap(resources.getPathForImage('ready.png'))
31                 self.busyBitmap = [
32                         wx.Bitmap(resources.getPathForImage('busy-0.png')),
33                         wx.Bitmap(resources.getPathForImage('busy-1.png')),
34                         wx.Bitmap(resources.getPathForImage('busy-2.png')),
35                         wx.Bitmap(resources.getPathForImage('busy-3.png'))
36                 ]
37
38                 self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
39                 self.text = wx.StaticText(self, -1, '')
40                 self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
41                 self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
42                 self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
43                 self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
44                 self.sizer.AddGrowableCol(1)
45
46                 self.extraInfoButton.Show(False)
47
48                 self.extraInfoUrl = ''
49                 self.busyState = None
50                 self.timer = wx.Timer(self)
51                 self.Bind(wx.EVT_TIMER, self.doBusyUpdate, self.timer)
52                 self.Bind(wx.EVT_BUTTON, self.doExtraInfo, self.extraInfoButton)
53                 self.timer.Start(100)
54
55         def SetInfo(self, info):
56                 self.SetBackgroundColour('#FFFF80')
57                 self.text.SetLabel(info)
58                 self.extraInfoButton.Show(False)
59                 self.Refresh()
60
61         def SetError(self, info, extraInfoUrl):
62                 self.extraInfoUrl = extraInfoUrl
63                 self.SetBackgroundColour('#FF8080')
64                 self.text.SetLabel(info)
65                 self.extraInfoButton.Show(True)
66                 self.Layout()
67                 self.SetErrorIndicator()
68                 self.Refresh()
69
70         def SetAttention(self, info):
71                 self.SetBackgroundColour('#FFFF80')
72                 self.text.SetLabel(info)
73                 self.extraInfoButton.Show(False)
74                 self.SetAttentionIndicator()
75                 self.Layout()
76                 self.Refresh()
77
78         def SetBusy(self, info):
79                 self.SetInfo(info)
80                 self.SetBusyIndicator()
81
82         def SetBusyIndicator(self):
83                 self.busyState = 0
84                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
85
86         def doExtraInfo(self, e):
87                 webbrowser.open(self.extraInfoUrl)
88
89         def doBusyUpdate(self, e):
90                 if self.busyState is None:
91                         return
92                 self.busyState += 1
93                 if self.busyState >= len(self.busyBitmap):
94                         self.busyState = 0
95                 self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
96
97         def SetReadyIndicator(self):
98                 self.busyState = None
99                 self.bitmap.SetBitmap(self.readyBitmap)
100
101         def SetErrorIndicator(self):
102                 self.busyState = None
103                 self.bitmap.SetBitmap(self.errorBitmap)
104
105         def SetAttentionIndicator(self):
106                 self.busyState = None
107                 self.bitmap.SetBitmap(self.attentionBitmap)
108
109
110 class InfoPage(wx.wizard.WizardPageSimple):
111         def __init__(self, parent, title):
112                 wx.wizard.WizardPageSimple.__init__(self, parent)
113
114                 sizer = wx.GridBagSizer(5, 5)
115                 self.sizer = sizer
116                 self.SetSizer(sizer)
117
118                 title = wx.StaticText(self, -1, title)
119                 title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
120                 sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
121                 sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
122                 sizer.AddGrowableCol(1)
123
124                 self.rowNr = 2
125
126         def AddText(self, info):
127                 text = wx.StaticText(self, -1, info)
128                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
129                 self.rowNr += 1
130                 return text
131
132         def AddSeperator(self):
133                 self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
134                 self.rowNr += 1
135
136         def AddHiddenSeperator(self):
137                 self.AddText("")
138
139         def AddInfoBox(self):
140                 infoBox = InfoBox(self)
141                 self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
142                 self.rowNr += 1
143                 return infoBox
144
145         def AddRadioButton(self, label, style=0):
146                 radio = wx.RadioButton(self, -1, label, style=style)
147                 self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
148                 self.rowNr += 1
149                 return radio
150
151         def AddCheckbox(self, label, checked=False):
152                 check = wx.CheckBox(self, -1)
153                 text = wx.StaticText(self, -1, label)
154                 check.SetValue(checked)
155                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
156                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
157                 self.rowNr += 1
158                 return check
159
160         def AddButton(self, label):
161                 button = wx.Button(self, -1, label)
162                 self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
163                 self.rowNr += 1
164                 return button
165
166         def AddDualButton(self, label1, label2):
167                 button1 = wx.Button(self, -1, label1)
168                 self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
169                 button2 = wx.Button(self, -1, label2)
170                 self.GetSizer().Add(button2, pos=(self.rowNr, 1))
171                 self.rowNr += 1
172                 return button1, button2
173
174         def AddTextCtrl(self, value):
175                 ret = wx.TextCtrl(self, -1, value)
176                 self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
177                 self.rowNr += 1
178                 return ret
179
180         def AddLabelTextCtrl(self, info, value):
181                 text = wx.StaticText(self, -1, info)
182                 ret = wx.TextCtrl(self, -1, value)
183                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
184                 self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
185                 self.rowNr += 1
186                 return ret
187
188         def AddTextCtrlButton(self, value, buttonText):
189                 text = wx.TextCtrl(self, -1, value)
190                 button = wx.Button(self, -1, buttonText)
191                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
192                 self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
193                 self.rowNr += 1
194                 return text, button
195
196         def AddBitmap(self, bitmap):
197                 bitmap = wx.StaticBitmap(self, -1, bitmap)
198                 self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
199                 self.rowNr += 1
200                 return bitmap
201
202         def AddCheckmark(self, label, bitmap):
203                 check = wx.StaticBitmap(self, -1, bitmap)
204                 text = wx.StaticText(self, -1, label)
205                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
206                 self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
207                 self.rowNr += 1
208                 return check
209
210         def AddCombo(self, label, options):
211                 combo = wx.ComboBox(self, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
212                 text = wx.StaticText(self, -1, label)
213                 self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
214                 self.GetSizer().Add(combo, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
215                 self.rowNr += 1
216                 return combo
217
218         def AllowNext(self):
219                 return True
220
221         def AllowBack(self):
222                 return True
223
224         def StoreData(self):
225                 pass
226
227
228 class FirstInfoPage(InfoPage):
229         def __init__(self, parent, addNew):
230                 if addNew:
231                         super(FirstInfoPage, self).__init__(parent, _("Add new machine wizard"))
232                 else:
233                         super(FirstInfoPage, self).__init__(parent, _("First time run wizard"))
234                         self.AddText(_("Welcome, and thanks for trying Cura!"))
235                         self.AddSeperator()
236                 self.AddText(_("This wizard will help you in setting up Cura for your machine."))
237                 if not addNew:
238                         self.AddSeperator()
239                         self._language_option = self.AddCombo(_("Select your language:"), map(lambda o: o[1], resources.getLanguageOptions()))
240                 else:
241                         self._language_option = None
242                 # self.AddText(_("This wizard will help you with the following steps:"))
243                 # self.AddText(_("* Configure Cura for your machine"))
244                 # self.AddText(_("* Optionally upgrade your firmware"))
245                 # self.AddText(_("* Optionally check if your machine is working safely"))
246                 # self.AddText(_("* Optionally level your printer bed"))
247
248                 #self.AddText('* Calibrate your machine')
249                 #self.AddText('* Do your first print')
250
251         def AllowBack(self):
252                 return False
253
254         def StoreData(self):
255                 if self._language_option is not None:
256                         profile.putPreference('language', self._language_option.GetValue())
257                         resources.setupLocalization(self._language_option.GetValue())
258
259 class PrintrbotPage(InfoPage):
260         def __init__(self, parent):
261                 self._printer_info = [
262                         # X, Y, Z, Filament Diameter, PrintTemperature, Print Speed, Travel Speed, Retract speed, Retract amount
263                         ("Simple Metal", 150, 150, 150, 1.75, 208, 40, 70, 30, 1),
264                         ("Metal Plus", 250, 250, 250, 1.75, 208, 40, 70, 30, 1),
265                         ("Simple Makers Kit", 100, 100, 100, 1.75, 208, 40, 70, 30, 1),
266                         (":" + _("Older models"),),
267                         ("Original", 130, 130, 130, 2.95, 208, 40, 70, 30, 1),
268                         ("Simple Maker's Edition v1", 100, 100, 100, 1.75, 208, 40, 70, 30, 1),
269                         ("Simple Maker's Edition v2 (2013 Printrbot Simple)", 100, 100, 100, 1.75, 208, 40, 70, 30, 1),
270                         ("Simple Maker's Edition v3 (2014 Printrbot Simple)", 100, 100, 100, 1.75, 208, 40, 70, 30, 1),
271                         ("Simple Maker's Edition v4 (Model 1405)", 100, 100, 100, 1.75, 208, 40, 70, 30, 1),
272                         ("Jr v1", 150, 100, 80, 1.75, 208, 40, 70, 30, 1),
273                         ("Jr v2", 150, 150, 150, 1.75, 208, 40, 70, 30, 1),
274                         ("LC v2", 150, 150, 150, 1.75, 208, 40, 70, 30, 1),
275                         ("Plus v2", 200, 200, 200, 1.75, 208, 40, 70, 30, 1),
276                         ("Plus v2.1", 200, 200, 200, 1.75, 208, 40, 70, 30, 1),
277                         ("Plus v2.2 (Model 1404/140422)", 250, 250, 250, 1.75, 208, 40, 70, 30, 1),
278                         ("Plus v2.3 (Model 140501)", 250, 250, 250, 1.75, 208, 40, 70, 30, 1),
279                         ("Plus v2.4 (Model 140507)", 250, 250, 250, 1.75, 208, 40, 70, 30, 1),
280                         ("Go v2 Large", 609, 305, 305, 1.75, 208, 35, 70, 30, 1),
281                 ]
282
283                 super(PrintrbotPage, self).__init__(parent, _("Printrbot Selection"))
284                 self.AddBitmap(wx.Bitmap(resources.getPathForImage('Printrbot_logo.png')))
285                 self.AddText(_("Select which Printrbot machine you have:"))
286                 self._items = []
287                 for printer in self._printer_info:
288                         if printer[0].startswith(":"):
289                                 self.AddSeperator()
290                                 self.AddText(printer[0][1:])
291                         else:
292                                 item = self.AddRadioButton(printer[0])
293                                 item.data = printer[1:]
294                                 self._items.append(item)
295
296         def StoreData(self):
297                 profile.putMachineSetting('machine_name', 'Printrbot ???')
298                 for item in self._items:
299                         if item.GetValue():
300                                 data = item.data
301                                 profile.putMachineSetting('machine_name', 'Printrbot ' + item.GetLabel())
302                                 profile.putMachineSetting('machine_width', data[0])
303                                 profile.putMachineSetting('machine_depth', data[1])
304                                 profile.putMachineSetting('machine_height', data[2])
305                                 profile.putProfileSetting('nozzle_size', '0.5')
306                                 profile.putProfileSetting('filament_diameter', data[3])
307                                 profile.putProfileSetting('print_temperature', data[4])
308                                 profile.putProfileSetting('print_speed', data[5])
309                                 profile.putProfileSetting('travel_speed', data[6])
310                                 profile.putProfileSetting('retraction_speed', data[7])
311                                 profile.putProfileSetting('retraction_amount', data[8])
312                                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
313                                 profile.putMachineSetting('has_heated_bed', 'False')
314                                 profile.putMachineSetting('machine_center_is_zero', 'False')
315                                 profile.putMachineSetting('extruder_head_size_min_x', '0')
316                                 profile.putMachineSetting('extruder_head_size_min_y', '0')
317                                 profile.putMachineSetting('extruder_head_size_max_x', '0')
318                                 profile.putMachineSetting('extruder_head_size_max_y', '0')
319                                 profile.putMachineSetting('extruder_head_size_height', '0')
320
321 class OtherMachineSelectPage(InfoPage):
322         def __init__(self, parent):
323                 super(OtherMachineSelectPage, self).__init__(parent, _("Other machine information"))
324                 self.AddText(_("The following pre-defined machine profiles are available"))
325                 self.AddText(_("Note that these profiles are not guaranteed to give good results,\nor work at all. Extra tweaks might be required.\nIf you find issues with the predefined profiles,\nor want an extra profile.\nPlease report it at the github issue tracker."))
326                 self.options = []
327                 machines = resources.getDefaultMachineProfiles()
328                 machines.sort()
329                 for filename in machines:
330                         name = os.path.splitext(os.path.basename(filename))[0]
331                         item = self.AddRadioButton(name)
332                         item.filename = filename
333                         item.Bind(wx.EVT_RADIOBUTTON, self.OnProfileSelect)
334                         self.options.append(item)
335                 self.AddSeperator()
336                 item = self.AddRadioButton(_('Custom...'))
337                 item.SetValue(True)
338                 item.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
339
340         def OnProfileSelect(self, e):
341                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineInfoPage)
342
343         def OnOtherSelect(self, e):
344                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().customRepRapInfoPage)
345
346         def StoreData(self):
347                 for option in self.options:
348                         if option.GetValue():
349                                 profile.loadProfile(option.filename)
350                                 profile.loadMachineSettings(option.filename)
351
352 class OtherMachineInfoPage(InfoPage):
353         def __init__(self, parent):
354                 super(OtherMachineInfoPage, self).__init__(parent, _("Cura Ready!"))
355                 self.AddText(_("Cura is now ready to be used!"))
356
357 class CustomRepRapInfoPage(InfoPage):
358         def __init__(self, parent):
359                 super(CustomRepRapInfoPage, self).__init__(parent, _("Custom RepRap information"))
360                 self.AddText(_("RepRap machines can be vastly different, so here you can set your own settings."))
361                 self.AddText(_("Be sure to review the default profile before running it on your machine."))
362                 self.AddText(_("If you like a default profile for your machine added,\nthen make an issue on github."))
363                 self.AddSeperator()
364                 self.AddText(_("You will have to manually install Marlin or Sprinter firmware."))
365                 self.AddSeperator()
366                 self.machineName = self.AddLabelTextCtrl(_("Machine name"), "RepRap")
367                 self.machineWidth = self.AddLabelTextCtrl(_("Machine width (mm)"), "80")
368                 self.machineDepth = self.AddLabelTextCtrl(_("Machine depth (mm)"), "80")
369                 self.machineHeight = self.AddLabelTextCtrl(_("Machine height (mm)"), "55")
370                 self.nozzleSize = self.AddLabelTextCtrl(_("Nozzle size (mm)"), "0.5")
371                 self.heatedBed = self.AddCheckbox(_("Heated bed"))
372                 self.HomeAtCenter = self.AddCheckbox(_("Bed center is 0,0,0 (RoStock)"))
373
374         def StoreData(self):
375                 profile.putMachineSetting('machine_name', self.machineName.GetValue())
376                 profile.putMachineSetting('machine_width', self.machineWidth.GetValue())
377                 profile.putMachineSetting('machine_depth', self.machineDepth.GetValue())
378                 profile.putMachineSetting('machine_height', self.machineHeight.GetValue())
379                 profile.putProfileSetting('nozzle_size', self.nozzleSize.GetValue())
380                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
381                 profile.putMachineSetting('has_heated_bed', str(self.heatedBed.GetValue()))
382                 profile.putMachineSetting('machine_center_is_zero', str(self.HomeAtCenter.GetValue()))
383                 profile.putMachineSetting('extruder_head_size_min_x', '0')
384                 profile.putMachineSetting('extruder_head_size_min_y', '0')
385                 profile.putMachineSetting('extruder_head_size_max_x', '0')
386                 profile.putMachineSetting('extruder_head_size_max_y', '0')
387                 profile.putMachineSetting('extruder_head_size_height', '0')
388                 profile.checkAndUpdateMachineName()
389
390 class MachineSelectPage(InfoPage):
391         def __init__(self, parent):
392                 super(MachineSelectPage, self).__init__(parent, _("Select your machine"))
393                 self.AddText(_("What kind of machine do you have:"))
394
395                 self.Ultimaker2Radio = self.AddRadioButton("Ultimaker2", style=wx.RB_GROUP)
396                 self.Ultimaker2Radio.SetValue(True)
397                 self.Ultimaker2Radio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimaker2Select)
398                 self.UltimakerRadio = self.AddRadioButton("Ultimaker Original")
399                 self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
400                 self.UltimakerOPRadio = self.AddRadioButton("Ultimaker Original+")
401                 self.UltimakerOPRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerOPSelect)
402                 self.PrintrbotRadio = self.AddRadioButton("Printrbot")
403                 self.PrintrbotRadio.Bind(wx.EVT_RADIOBUTTON, self.OnPrintrbotSelect)
404                 self.LulzbotTazRadio = self.AddRadioButton("Lulzbot TAZ")
405                 self.LulzbotTazRadio.Bind(wx.EVT_RADIOBUTTON, self.OnLulzbotSelect)
406                 self.LulzbotMiniRadio = self.AddRadioButton("Lulzbot Mini")
407                 self.LulzbotMiniRadio.Bind(wx.EVT_RADIOBUTTON, self.OnLulzbotSelect)
408                 self.OtherRadio = self.AddRadioButton(_("Other (Ex: RepRap, MakerBot, Witbox)"))
409                 self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
410                 self.AddSeperator()
411                 self.AddText(_("The collection of anonymous usage information helps with the continued improvement of Cura."))
412                 self.AddText(_("This does NOT submit your models online nor gathers any privacy related information."))
413                 self.SubmitUserStats = self.AddCheckbox(_("Submit anonymous usage information:"))
414                 self.AddText(_("For full details see: http://wiki.ultimaker.com/Cura:stats"))
415                 self.SubmitUserStats.SetValue(True)
416
417         def OnUltimaker2Select(self, e):
418                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
419
420         def OnUltimakerSelect(self, e):
421                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerSelectParts)
422
423         def OnUltimakerOPSelect(self, e):
424                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
425
426         def OnPrintrbotSelect(self, e):
427                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().printrbotSelectType)
428
429         def OnLulzbotSelect(self, e):
430                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().lulzbotReadyPage)
431
432         def OnOtherSelect(self, e):
433                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().otherMachineSelectPage)
434
435         def AllowNext(self):
436                 wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimaker2ReadyPage)
437                 return True
438
439         def StoreData(self):
440                 profile.putProfileSetting('retraction_enable', 'True')
441                 if self.Ultimaker2Radio.GetValue():
442                         profile.putMachineSetting('machine_width', '230')
443                         profile.putMachineSetting('machine_depth', '225')
444                         profile.putMachineSetting('machine_height', '205')
445                         profile.putMachineSetting('machine_name', 'ultimaker2')
446                         profile.putMachineSetting('machine_type', 'ultimaker2')
447                         profile.putMachineSetting('machine_center_is_zero', 'False')
448                         profile.putMachineSetting('has_heated_bed', 'True')
449                         profile.putMachineSetting('gcode_flavor', 'UltiGCode')
450                         profile.putMachineSetting('extruder_head_size_min_x', '40.0')
451                         profile.putMachineSetting('extruder_head_size_min_y', '10.0')
452                         profile.putMachineSetting('extruder_head_size_max_x', '60.0')
453                         profile.putMachineSetting('extruder_head_size_max_y', '30.0')
454                         profile.putMachineSetting('extruder_head_size_height', '48.0')
455                         profile.putProfileSetting('nozzle_size', '0.4')
456                         profile.putProfileSetting('fan_full_height', '5.0')
457                         profile.putMachineSetting('extruder_offset_x1', '18.0')
458                         profile.putMachineSetting('extruder_offset_y1', '0.0')
459                 elif self.UltimakerRadio.GetValue():
460                         profile.putMachineSetting('machine_width', '205')
461                         profile.putMachineSetting('machine_depth', '205')
462                         profile.putMachineSetting('machine_height', '200')
463                         profile.putMachineSetting('machine_name', 'ultimaker original')
464                         profile.putMachineSetting('machine_type', 'ultimaker')
465                         profile.putMachineSetting('machine_center_is_zero', 'False')
466                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
467                         profile.putProfileSetting('nozzle_size', '0.4')
468                         profile.putMachineSetting('extruder_head_size_min_x', '75.0')
469                         profile.putMachineSetting('extruder_head_size_min_y', '18.0')
470                         profile.putMachineSetting('extruder_head_size_max_x', '18.0')
471                         profile.putMachineSetting('extruder_head_size_max_y', '35.0')
472                         profile.putMachineSetting('extruder_head_size_height', '55.0')
473                 elif self.UltimakerOPRadio.GetValue():
474                         profile.putMachineSetting('machine_width', '205')
475                         profile.putMachineSetting('machine_depth', '205')
476                         profile.putMachineSetting('machine_height', '200')
477                         profile.putMachineSetting('machine_name', 'ultimaker original+')
478                         profile.putMachineSetting('machine_type', 'ultimaker_plus')
479                         profile.putMachineSetting('machine_center_is_zero', 'False')
480                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
481                         profile.putProfileSetting('nozzle_size', '0.4')
482                         profile.putMachineSetting('extruder_head_size_min_x', '75.0')
483                         profile.putMachineSetting('extruder_head_size_min_y', '18.0')
484                         profile.putMachineSetting('extruder_head_size_max_x', '18.0')
485                         profile.putMachineSetting('extruder_head_size_max_y', '35.0')
486                         profile.putMachineSetting('extruder_head_size_height', '55.0')
487                         profile.putMachineSetting('has_heated_bed', 'True')
488                         profile.putMachineSetting('extruder_amount', '1')
489                         profile.putProfileSetting('retraction_enable', 'True')
490                 elif self.LulzbotTazRadio.GetValue() or self.LulzbotMiniRadio.GetValue():
491                         if self.LulzbotTazRadio.GetValue():
492                                 profile.putMachineSetting('machine_width', '298')
493                                 profile.putMachineSetting('machine_depth', '275')
494                                 profile.putMachineSetting('machine_height', '250')
495                                 profile.putProfileSetting('nozzle_size', '0.35')
496                                 profile.putMachineSetting('machine_name', 'Lulzbot TAZ')
497                         else:
498                                 profile.putMachineSetting('machine_width', '160')
499                                 profile.putMachineSetting('machine_depth', '160')
500                                 profile.putMachineSetting('machine_height', '160')
501                                 profile.putProfileSetting('nozzle_size', '0.5')
502                                 profile.putMachineSetting('machine_name', 'Lulzbot Mini')
503                         profile.putMachineSetting('machine_type', 'Aleph Objects')
504                         profile.putMachineSetting('machine_center_is_zero', 'False')
505                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
506                         profile.putMachineSetting('has_heated_bed', 'True')
507                         profile.putMachineSetting('extruder_head_size_min_x', '0.0')
508                         profile.putMachineSetting('extruder_head_size_min_y', '0.0')
509                         profile.putMachineSetting('extruder_head_size_max_x', '0.0')
510                         profile.putMachineSetting('extruder_head_size_max_y', '0.0')
511                         profile.putMachineSetting('extruder_head_size_height', '0.0')
512                 else:
513                         profile.putMachineSetting('machine_width', '80')
514                         profile.putMachineSetting('machine_depth', '80')
515                         profile.putMachineSetting('machine_height', '60')
516                         profile.putMachineSetting('machine_name', 'reprap')
517                         profile.putMachineSetting('machine_type', 'reprap')
518                         profile.putMachineSetting('gcode_flavor', 'RepRap (Marlin/Sprinter)')
519                         profile.putPreference('startMode', 'Normal')
520                         profile.putProfileSetting('nozzle_size', '0.5')
521                 profile.checkAndUpdateMachineName()
522                 profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
523                 if self.SubmitUserStats.GetValue():
524                         profile.putPreference('submit_slice_information', 'True')
525                 else:
526                         profile.putPreference('submit_slice_information', 'False')
527
528
529 class SelectParts(InfoPage):
530         def __init__(self, parent):
531                 super(SelectParts, self).__init__(parent, _("Select upgraded parts you have"))
532                 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."))
533                 self.AddSeperator()
534                 self.springExtruder = self.AddCheckbox(_("Extruder drive upgrade"))
535                 self.heatedBedKit = self.AddCheckbox(_("Heated printer bed (kit)"))
536                 self.heatedBed = self.AddCheckbox(_("Heated printer bed (self built)"))
537                 self.dualExtrusion = self.AddCheckbox(_("Dual extrusion (experimental)"))
538                 self.AddSeperator()
539                 self.AddText(_("If you have an Ultimaker bought after october 2012 you will have the\nExtruder drive upgrade. If you do not have this upgrade,\nit is highly recommended to improve reliability."))
540                 self.AddText(_("This upgrade can be bought from the Ultimaker webshop\nor found on thingiverse as thing:26094"))
541                 self.springExtruder.SetValue(True)
542
543         def StoreData(self):
544                 profile.putMachineSetting('ultimaker_extruder_upgrade', str(self.springExtruder.GetValue()))
545                 if self.heatedBed.GetValue() or self.heatedBedKit.GetValue():
546                         profile.putMachineSetting('has_heated_bed', 'True')
547                 else:
548                         profile.putMachineSetting('has_heated_bed', 'False')
549                 if self.dualExtrusion.GetValue():
550                         profile.putMachineSetting('extruder_amount', '2')
551                         profile.putMachineSetting('machine_depth', '195')
552                 else:
553                         profile.putMachineSetting('extruder_amount', '1')
554                 if profile.getMachineSetting('ultimaker_extruder_upgrade') == 'True':
555                         profile.putProfileSetting('retraction_enable', 'True')
556                 else:
557                         profile.putProfileSetting('retraction_enable', 'False')
558
559
560 class UltimakerFirmwareUpgradePage(InfoPage):
561         def __init__(self, parent):
562                 super(UltimakerFirmwareUpgradePage, self).__init__(parent, _("Upgrade Ultimaker Firmware"))
563                 self.AddText(_("Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work."))
564                 self.AddHiddenSeperator()
565                 self.AddText(_("The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier."))
566                 self.AddHiddenSeperator()
567                 self.AddText(_("Cura requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now."))
568                 upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
569                 upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
570                 skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
571                 self.AddHiddenSeperator()
572                 if profile.getMachineSetting('machine_type') == 'ultimaker':
573                         self.AddText(_("Do not upgrade to this firmware if:"))
574                         self.AddText(_("* You have an older machine based on ATMega1280 (Rev 1 machine)"))
575                         self.AddText(_("* Build your own heated bed"))
576                         self.AddText(_("* Have other changes in the firmware"))
577 #               button = self.AddButton('Goto this page for a custom firmware')
578 #               button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
579
580         def AllowNext(self):
581                 return False
582
583         def OnUpgradeClick(self, e):
584                 if firmwareInstall.InstallFirmware():
585                         self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
586
587         def OnSkipClick(self, e):
588                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
589                 self.GetParent().ShowPage(self.GetNext())
590
591         def OnUrlClick(self, e):
592                 webbrowser.open('http://marlinbuilder.robotfuzz.com/')
593
594 class UltimakerCheckupPage(InfoPage):
595         def __init__(self, parent):
596                 super(UltimakerCheckupPage, self).__init__(parent, _("Ultimaker Checkup"))
597
598                 self.checkBitmap = wx.Bitmap(resources.getPathForImage('checkmark.png'))
599                 self.crossBitmap = wx.Bitmap(resources.getPathForImage('cross.png'))
600                 self.unknownBitmap = wx.Bitmap(resources.getPathForImage('question.png'))
601                 self.endStopNoneBitmap = wx.Bitmap(resources.getPathForImage('endstop_none.png'))
602                 self.endStopXMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmin.png'))
603                 self.endStopXMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_xmax.png'))
604                 self.endStopYMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymin.png'))
605                 self.endStopYMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_ymax.png'))
606                 self.endStopZMinBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmin.png'))
607                 self.endStopZMaxBitmap = wx.Bitmap(resources.getPathForImage('endstop_zmax.png'))
608
609                 self.AddText(
610                         _("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."))
611                 b1, b2 = self.AddDualButton(_("Run checks"), _("Skip checks"))
612                 b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
613                 b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
614                 self.AddSeperator()
615                 self.commState = self.AddCheckmark(_("Communication:"), self.unknownBitmap)
616                 self.tempState = self.AddCheckmark(_("Temperature:"), self.unknownBitmap)
617                 self.stopState = self.AddCheckmark(_("Endstops:"), self.unknownBitmap)
618                 self.AddSeperator()
619                 self.infoBox = self.AddInfoBox()
620                 self.machineState = self.AddText("")
621                 self.temperatureLabel = self.AddText("")
622                 self.errorLogButton = self.AddButton(_("Show error log"))
623                 self.errorLogButton.Show(False)
624                 self.AddSeperator()
625                 self.endstopBitmap = self.AddBitmap(self.endStopNoneBitmap)
626                 self.comm = None
627                 self.xMinStop = False
628                 self.xMaxStop = False
629                 self.yMinStop = False
630                 self.yMaxStop = False
631                 self.zMinStop = False
632                 self.zMaxStop = False
633
634                 self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
635
636         def __del__(self):
637                 if self.comm is not None:
638                         self.comm.close()
639
640         def AllowNext(self):
641                 self.endstopBitmap.Show(False)
642                 return False
643
644         def OnSkipClick(self, e):
645                 self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
646                 self.GetParent().ShowPage(self.GetNext())
647
648         def OnCheckClick(self, e=None):
649                 self.errorLogButton.Show(False)
650                 if self.comm is not None:
651                         self.comm.close()
652                         del self.comm
653                         self.comm = None
654                         wx.CallAfter(self.OnCheckClick)
655                         return
656                 self.infoBox.SetBusy(_("Connecting to machine."))
657                 self.commState.SetBitmap(self.unknownBitmap)
658                 self.tempState.SetBitmap(self.unknownBitmap)
659                 self.stopState.SetBitmap(self.unknownBitmap)
660                 self.checkupState = 0
661                 self.checkExtruderNr = 0
662                 self.comm = machineCom.MachineCom(callbackObject=self)
663
664         def OnErrorLog(self, e):
665                 printWindow.LogWindow('\n'.join(self.comm.getLog()))
666
667         def mcLog(self, message):
668                 pass
669
670         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
671                 if not self.comm.isOperational():
672                         return
673                 if self.checkupState == 0:
674                         self.tempCheckTimeout = 20
675                         if temp[self.checkExtruderNr] > 70:
676                                 self.checkupState = 1
677                                 wx.CallAfter(self.infoBox.SetInfo, _("Cooldown before temperature check."))
678                                 self.comm.sendCommand("M104 S0 T%d" % (self.checkExtruderNr))
679                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
680                         else:
681                                 self.startTemp = temp[self.checkExtruderNr]
682                                 self.checkupState = 2
683                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
684                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
685                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
686                 elif self.checkupState == 1:
687                         if temp[self.checkExtruderNr] < 60:
688                                 self.startTemp = temp[self.checkExtruderNr]
689                                 self.checkupState = 2
690                                 wx.CallAfter(self.infoBox.SetInfo, _("Checking the heater and temperature sensor."))
691                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
692                                 self.comm.sendCommand('M104 S200 T%d' % (self.checkExtruderNr))
693                 elif self.checkupState == 2:
694                         #print "WARNING, TEMPERATURE TEST DISABLED FOR TESTING!"
695                         if temp[self.checkExtruderNr] > self.startTemp + 40:
696                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
697                                 self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
698                                 if self.checkExtruderNr < int(profile.getMachineSetting('extruder_amount')):
699                                         self.checkExtruderNr = 0
700                                         self.checkupState = 3
701                                         wx.CallAfter(self.infoBox.SetAttention, _("Please make sure none of the endstops are pressed."))
702                                         wx.CallAfter(self.endstopBitmap.Show, True)
703                                         wx.CallAfter(self.Layout)
704                                         self.comm.sendCommand('M119')
705                                         wx.CallAfter(self.tempState.SetBitmap, self.checkBitmap)
706                                 else:
707                                         self.checkupState = 0
708                                         self.checkExtruderNr += 1
709                         else:
710                                 self.tempCheckTimeout -= 1
711                                 if self.tempCheckTimeout < 1:
712                                         self.checkupState = -1
713                                         wx.CallAfter(self.tempState.SetBitmap, self.crossBitmap)
714                                         wx.CallAfter(self.infoBox.SetError, _("Temperature measurement FAILED!"), 'http://wiki.ultimaker.com/Cura:_Temperature_measurement_problems')
715                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
716                                         self.comm.sendCommand('M104 S0 T%d' % (self.checkExtruderNr))
717                 elif self.checkupState >= 3 and self.checkupState < 10:
718                         self.comm.sendCommand('M119')
719                 wx.CallAfter(self.temperatureLabel.SetLabel, _("Head temperature: %d") % (temp[self.checkExtruderNr]))
720
721         def mcStateChange(self, state):
722                 if self.comm is None:
723                         return
724                 if self.comm.isOperational():
725                         wx.CallAfter(self.commState.SetBitmap, self.checkBitmap)
726                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
727                 elif self.comm.isError():
728                         wx.CallAfter(self.commState.SetBitmap, self.crossBitmap)
729                         wx.CallAfter(self.infoBox.SetError, _("Failed to establish connection with the printer."), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
730                         wx.CallAfter(self.endstopBitmap.Show, False)
731                         wx.CallAfter(self.machineState.SetLabel, '%s' % (self.comm.getErrorString()))
732                         wx.CallAfter(self.errorLogButton.Show, True)
733                         wx.CallAfter(self.Layout)
734                 else:
735                         wx.CallAfter(self.machineState.SetLabel, _("Communication State: %s") % (self.comm.getStateString()))
736
737         def mcMessage(self, message):
738                 if self.checkupState >= 3 and self.checkupState < 10 and ('_min' in message or '_max' in message):
739                         for data in message.split(' '):
740                                 if ':' in data:
741                                         tag, value = data.split(':', 1)
742                                         if tag == 'x_min':
743                                                 self.xMinStop = (value == 'H' or value == 'TRIGGERED')
744                                         if tag == 'x_max':
745                                                 self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
746                                         if tag == 'y_min':
747                                                 self.yMinStop = (value == 'H' or value == 'TRIGGERED')
748                                         if tag == 'y_max':
749                                                 self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
750                                         if tag == 'z_min':
751                                                 self.zMinStop = (value == 'H' or value == 'TRIGGERED')
752                                         if tag == 'z_max':
753                                                 self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
754                         if ':' in message:
755                                 tag, value = map(str.strip, message.split(':', 1))
756                                 if tag == 'x_min':
757                                         self.xMinStop = (value == 'H' or value == 'TRIGGERED')
758                                 if tag == 'x_max':
759                                         self.xMaxStop = (value == 'H' or value == 'TRIGGERED')
760                                 if tag == 'y_min':
761                                         self.yMinStop = (value == 'H' or value == 'TRIGGERED')
762                                 if tag == 'y_max':
763                                         self.yMaxStop = (value == 'H' or value == 'TRIGGERED')
764                                 if tag == 'z_min':
765                                         self.zMinStop = (value == 'H' or value == 'TRIGGERED')
766                                 if tag == 'z_max':
767                                         self.zMaxStop = (value == 'H' or value == 'TRIGGERED')
768                         if 'z_max' in message:
769                                 self.comm.sendCommand('M119')
770
771                         if self.checkupState == 3:
772                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
773                                         if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
774                                                 self.checkupState = 5
775                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
776                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
777                                         else:
778                                                 self.checkupState = 4
779                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the right X endstop."))
780                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMaxBitmap)
781                         elif self.checkupState == 4:
782                                 if not self.xMinStop and self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
783                                         self.checkupState = 5
784                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the left X endstop."))
785                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopXMinBitmap)
786                         elif self.checkupState == 5:
787                                 if self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
788                                         self.checkupState = 6
789                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the front Y endstop."))
790                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMinBitmap)
791                         elif self.checkupState == 6:
792                                 if not self.xMinStop and not self.xMaxStop and self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
793                                         if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
794                                                 self.checkupState = 8
795                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
796                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
797                                         else:
798                                                 self.checkupState = 7
799                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the back Y endstop."))
800                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopYMaxBitmap)
801                         elif self.checkupState == 7:
802                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and self.yMaxStop and not self.zMinStop and not self.zMaxStop:
803                                         self.checkupState = 8
804                                         wx.CallAfter(self.infoBox.SetAttention, _("Please press the top Z endstop."))
805                                         wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMinBitmap)
806                         elif self.checkupState == 8:
807                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and self.zMinStop and not self.zMaxStop:
808                                         if profile.getMachineSetting('machine_type') == 'ultimaker_plus':
809                                                 self.checkupState = 10
810                                                 self.comm.close()
811                                                 wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
812                                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
813                                                 wx.CallAfter(self.endstopBitmap.Show, False)
814                                                 wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
815                                                 wx.CallAfter(self.OnSkipClick, None)
816                                         else:
817                                                 self.checkupState = 9
818                                                 wx.CallAfter(self.infoBox.SetAttention, _("Please press the bottom Z endstop."))
819                                                 wx.CallAfter(self.endstopBitmap.SetBitmap, self.endStopZMaxBitmap)
820                         elif self.checkupState == 9:
821                                 if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and self.zMaxStop:
822                                         self.checkupState = 10
823                                         self.comm.close()
824                                         wx.CallAfter(self.infoBox.SetInfo, _("Checkup finished"))
825                                         wx.CallAfter(self.infoBox.SetReadyIndicator)
826                                         wx.CallAfter(self.endstopBitmap.Show, False)
827                                         wx.CallAfter(self.stopState.SetBitmap, self.checkBitmap)
828                                         wx.CallAfter(self.OnSkipClick, None)
829
830         def mcProgress(self, lineNr):
831                 pass
832
833         def mcZChange(self, newZ):
834                 pass
835
836
837 class UltimakerCalibrationPage(InfoPage):
838         def __init__(self, parent):
839                 super(UltimakerCalibrationPage, self).__init__(parent, _("Ultimaker Calibration"))
840
841                 self.AddText("Your Ultimaker requires some calibration.")
842                 self.AddText("This calibration is needed for a proper extrusion amount.")
843                 self.AddSeperator()
844                 self.AddText("The following values are needed:")
845                 self.AddText("* Diameter of filament")
846                 self.AddText("* Number of steps per mm of filament extrusion")
847                 self.AddSeperator()
848                 self.AddText("The better you have calibrated these values, the better your prints\nwill become.")
849                 self.AddSeperator()
850                 self.AddText("First we need the diameter of your filament:")
851                 self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
852                 self.AddText(
853                         "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.")
854                 self.AddText("Note: This value can be changed later at any time.")
855
856         def StoreData(self):
857                 profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
858
859
860 class UltimakerCalibrateStepsPerEPage(InfoPage):
861         def __init__(self, parent):
862                 super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, _("Ultimaker Calibration"))
863
864                 #if profile.getMachineSetting('steps_per_e') == '0':
865                 #       profile.putMachineSetting('steps_per_e', '865.888')
866
867                 self.AddText(_("Calibrating the Steps Per E requires some manual actions."))
868                 self.AddText(_("First remove any filament from your machine."))
869                 self.AddText(_("Next put in your filament so the tip is aligned with the\ntop of the extruder drive."))
870                 self.AddText(_("We'll push the filament 100mm"))
871                 self.extrudeButton = self.AddButton(_("Extrude 100mm filament"))
872                 self.AddText(_("Now measure the amount of extruded filament:\n(this can be more or less then 100mm)"))
873                 self.lengthInput, self.saveLengthButton = self.AddTextCtrlButton("100", _("Save"))
874                 self.AddText(_("This results in the following steps per E:"))
875                 self.stepsPerEInput = self.AddTextCtrl(profile.getMachineSetting('steps_per_e'))
876                 self.AddText(_("You can repeat these steps to get better calibration."))
877                 self.AddSeperator()
878                 self.AddText(
879                         _("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:"))
880                 self.heatButton = self.AddButton(_("Heatup for filament removal"))
881
882                 self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
883                 self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
884                 self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
885
886         def OnSaveLengthClick(self, e):
887                 currentEValue = float(self.stepsPerEInput.GetValue())
888                 realExtrudeLength = float(self.lengthInput.GetValue())
889                 newEValue = currentEValue * 100 / realExtrudeLength
890                 self.stepsPerEInput.SetValue(str(newEValue))
891                 self.lengthInput.SetValue("100")
892
893         def OnExtrudeClick(self, e):
894                 t = threading.Thread(target=self.OnExtrudeRun)
895                 t.daemon = True
896                 t.start()
897
898         def OnExtrudeRun(self):
899                 self.heatButton.Enable(False)
900                 self.extrudeButton.Enable(False)
901                 currentEValue = float(self.stepsPerEInput.GetValue())
902                 self.comm = machineCom.MachineCom()
903                 if not self.comm.isOpen():
904                         wx.MessageBox(
905                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
906                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
907                         self.heatButton.Enable(True)
908                         self.extrudeButton.Enable(True)
909                         return
910                 while True:
911                         line = self.comm.readline()
912                         if line == '':
913                                 return
914                         if 'start' in line:
915                                 break
916                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
917                 time.sleep(3)
918
919                 self.sendGCommand('M302') #Disable cold extrusion protection
920                 self.sendGCommand("M92 E%f" % (currentEValue))
921                 self.sendGCommand("G92 E0")
922                 self.sendGCommand("G1 E100 F600")
923                 time.sleep(15)
924                 self.comm.close()
925                 self.extrudeButton.Enable()
926                 self.heatButton.Enable()
927
928         def OnHeatClick(self, e):
929                 t = threading.Thread(target=self.OnHeatRun)
930                 t.daemon = True
931                 t.start()
932
933         def OnHeatRun(self):
934                 self.heatButton.Enable(False)
935                 self.extrudeButton.Enable(False)
936                 self.comm = machineCom.MachineCom()
937                 if not self.comm.isOpen():
938                         wx.MessageBox(
939                                 _("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable"),
940                                 'Printer error', wx.OK | wx.ICON_INFORMATION)
941                         self.heatButton.Enable(True)
942                         self.extrudeButton.Enable(True)
943                         return
944                 while True:
945                         line = self.comm.readline()
946                         if line == '':
947                                 self.heatButton.Enable(True)
948                                 self.extrudeButton.Enable(True)
949                                 return
950                         if 'start' in line:
951                                 break
952                         #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
953                 time.sleep(3)
954
955                 self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
956                 wx.MessageBox(
957                         'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
958                         'Machine heatup', wx.OK | wx.ICON_INFORMATION)
959                 self.sendGCommand('M104 S0')
960                 time.sleep(1)
961                 self.comm.close()
962                 self.heatButton.Enable(True)
963                 self.extrudeButton.Enable(True)
964
965         def sendGCommand(self, cmd):
966                 self.comm.sendCommand(cmd) #Disable cold extrusion protection
967                 while True:
968                         line = self.comm.readline()
969                         if line == '':
970                                 return
971                         if line.startswith('ok'):
972                                 break
973
974         def StoreData(self):
975                 profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
976
977 class Ultimaker2ReadyPage(InfoPage):
978         def __init__(self, parent):
979                 super(Ultimaker2ReadyPage, self).__init__(parent, _("Ultimaker2"))
980                 self.AddText(_('Congratulations on your the purchase of your brand new Ultimaker2.'))
981                 self.AddText(_('Cura is now ready to be used with your Ultimaker2.'))
982                 self.AddSeperator()
983
984 class LulzbotReadyPage(InfoPage):
985         def __init__(self, parent):
986                 super(LulzbotReadyPage, self).__init__(parent, _("Lulzbot TAZ/Mini"))
987                 self.AddText(_('Cura is now ready to be used with your Lulzbot.'))
988                 self.AddSeperator()
989
990 class ConfigWizard(wx.wizard.Wizard):
991         def __init__(self, addNew = False):
992                 super(ConfigWizard, self).__init__(None, -1, _("Configuration Wizard"))
993
994                 self._old_machine_index = int(profile.getPreferenceFloat('active_machine'))
995                 if addNew:
996                         profile.setActiveMachine(profile.getMachineCount())
997
998                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
999                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1000                 self.Bind(wx.wizard.EVT_WIZARD_CANCEL, self.OnCancel)
1001
1002                 self.firstInfoPage = FirstInfoPage(self, addNew)
1003                 self.machineSelectPage = MachineSelectPage(self)
1004                 self.ultimakerSelectParts = SelectParts(self)
1005                 self.ultimakerFirmwareUpgradePage = UltimakerFirmwareUpgradePage(self)
1006                 self.ultimakerCheckupPage = UltimakerCheckupPage(self)
1007                 self.ultimakerCalibrationPage = UltimakerCalibrationPage(self)
1008                 self.ultimakerCalibrateStepsPerEPage = UltimakerCalibrateStepsPerEPage(self)
1009                 self.bedLevelPage = bedLevelWizardMain(self)
1010                 self.headOffsetCalibration = headOffsetCalibrationPage(self)
1011                 self.printrbotSelectType = PrintrbotPage(self)
1012                 self.otherMachineSelectPage = OtherMachineSelectPage(self)
1013                 self.customRepRapInfoPage = CustomRepRapInfoPage(self)
1014                 self.otherMachineInfoPage = OtherMachineInfoPage(self)
1015
1016                 self.ultimaker2ReadyPage = Ultimaker2ReadyPage(self)
1017                 self.lulzbotReadyPage = LulzbotReadyPage(self)
1018
1019                 wx.wizard.WizardPageSimple.Chain(self.firstInfoPage, self.machineSelectPage)
1020                 #wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimaker2ReadyPage)
1021                 wx.wizard.WizardPageSimple.Chain(self.machineSelectPage, self.ultimakerSelectParts)
1022                 wx.wizard.WizardPageSimple.Chain(self.ultimakerSelectParts, self.ultimakerFirmwareUpgradePage)
1023                 wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
1024                 wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.bedLevelPage)
1025                 #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
1026                 wx.wizard.WizardPageSimple.Chain(self.printrbotSelectType, self.otherMachineInfoPage)
1027                 wx.wizard.WizardPageSimple.Chain(self.otherMachineSelectPage, self.customRepRapInfoPage)
1028
1029                 self.FitToPage(self.firstInfoPage)
1030                 self.GetPageAreaSizer().Add(self.firstInfoPage)
1031
1032                 self.RunWizard(self.firstInfoPage)
1033                 self.Destroy()
1034
1035         def OnPageChanging(self, e):
1036                 e.GetPage().StoreData()
1037
1038         def OnPageChanged(self, e):
1039                 if e.GetPage().AllowNext():
1040                         self.FindWindowById(wx.ID_FORWARD).Enable()
1041                 else:
1042                         self.FindWindowById(wx.ID_FORWARD).Disable()
1043                 if e.GetPage().AllowBack():
1044                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1045                 else:
1046                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1047
1048         def OnCancel(self, e):
1049                 profile.setActiveMachine(self._old_machine_index)
1050
1051 class bedLevelWizardMain(InfoPage):
1052         def __init__(self, parent):
1053                 super(bedLevelWizardMain, self).__init__(parent, _("Bed leveling wizard"))
1054
1055                 self.AddText(_('This wizard will help you in leveling your printer bed'))
1056                 self.AddSeperator()
1057                 self.AddText(_('It will do the following steps'))
1058                 self.AddText(_('* Move the printer head to each corner'))
1059                 self.AddText(_('  and let you adjust the height of the bed to the nozzle'))
1060                 self.AddText(_('* Print a line around the bed to check if it is level'))
1061                 self.AddSeperator()
1062
1063                 self.connectButton = self.AddButton(_('Connect to printer'))
1064                 self.comm = None
1065
1066                 self.infoBox = self.AddInfoBox()
1067                 self.resumeButton = self.AddButton(_('Resume'))
1068                 self.upButton, self.downButton = self.AddDualButton(_('Up 0.2mm'), _('Down 0.2mm'))
1069                 self.upButton2, self.downButton2 = self.AddDualButton(_('Up 10mm'), _('Down 10mm'))
1070                 self.resumeButton.Enable(False)
1071
1072                 self.upButton.Enable(False)
1073                 self.downButton.Enable(False)
1074                 self.upButton2.Enable(False)
1075                 self.downButton2.Enable(False)
1076
1077                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1078                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1079                 self.Bind(wx.EVT_BUTTON, self.OnBedUp, self.upButton)
1080                 self.Bind(wx.EVT_BUTTON, self.OnBedDown, self.downButton)
1081                 self.Bind(wx.EVT_BUTTON, self.OnBedUp2, self.upButton2)
1082                 self.Bind(wx.EVT_BUTTON, self.OnBedDown2, self.downButton2)
1083
1084         def OnConnect(self, e = None):
1085                 if self.comm is not None:
1086                         self.comm.close()
1087                         del self.comm
1088                         self.comm = None
1089                         wx.CallAfter(self.OnConnect)
1090                         return
1091                 self.connectButton.Enable(False)
1092                 self.comm = machineCom.MachineCom(callbackObject=self)
1093                 self.infoBox.SetBusy(_('Connecting to machine.'))
1094                 self._wizardState = 0
1095
1096         def OnBedUp(self, e):
1097                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1098                 self.comm.sendCommand('G92 Z10')
1099                 self.comm.sendCommand('G1 Z9.8 F%d' % (feedZ))
1100                 self.comm.sendCommand('M400')
1101
1102         def OnBedDown(self, e):
1103                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1104                 self.comm.sendCommand('G92 Z10')
1105                 self.comm.sendCommand('G1 Z10.2 F%d' % (feedZ))
1106                 self.comm.sendCommand('M400')
1107
1108         def OnBedUp2(self, e):
1109                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1110                 self.comm.sendCommand('G92 Z10')
1111                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1112                 self.comm.sendCommand('M400')
1113
1114         def OnBedDown2(self, e):
1115                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1116                 self.comm.sendCommand('G92 Z10')
1117                 self.comm.sendCommand('G1 Z20 F%d' % (feedZ))
1118                 self.comm.sendCommand('M400')
1119
1120         def AllowNext(self):
1121                 if self.GetParent().headOffsetCalibration is not None and int(profile.getMachineSetting('extruder_amount')) > 1:
1122                         wx.wizard.WizardPageSimple.Chain(self, self.GetParent().headOffsetCalibration)
1123                 return True
1124
1125         def OnResume(self, e):
1126                 feedZ = profile.getProfileSettingFloat('print_speed') * 60
1127                 feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1128                 if self._wizardState == -1:
1129                         wx.CallAfter(self.infoBox.SetInfo, _('Homing printer...'))
1130                         wx.CallAfter(self.upButton.Enable, False)
1131                         wx.CallAfter(self.downButton.Enable, False)
1132                         wx.CallAfter(self.upButton2.Enable, False)
1133                         wx.CallAfter(self.downButton2.Enable, False)
1134                         self.comm.sendCommand('M105')
1135                         self.comm.sendCommand('G28')
1136                         self._wizardState = 1
1137                 elif self._wizardState == 2:
1138                         if profile.getMachineSetting('has_heated_bed') == 'True':
1139                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back center...'))
1140                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1141                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') / 2.0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1142                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1143                                 self.comm.sendCommand('M400')
1144                                 self._wizardState = 3
1145                         else:
1146                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back left corner...'))
1147                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1148                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (0, profile.getMachineSettingFloat('machine_depth'), feedTravel))
1149                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1150                                 self.comm.sendCommand('M400')
1151                                 self._wizardState = 3
1152                 elif self._wizardState == 4:
1153                         if profile.getMachineSetting('has_heated_bed') == 'True':
1154                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1155                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1156                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 5, feedTravel))
1157                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1158                                 self.comm.sendCommand('M400')
1159                                 self._wizardState = 7
1160                         else:
1161                                 wx.CallAfter(self.infoBox.SetBusy, _('Moving head to back right corner...'))
1162                                 self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1163                                 self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, profile.getMachineSettingFloat('machine_depth') - 25, feedTravel))
1164                                 self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1165                                 self.comm.sendCommand('M400')
1166                                 self._wizardState = 5
1167                 elif self._wizardState == 6:
1168                         wx.CallAfter(self.infoBox.SetBusy, _('Moving head to front right corner...'))
1169                         self.comm.sendCommand('G1 Z3 F%d' % (feedZ))
1170                         self.comm.sendCommand('G1 X%d Y%d F%d' % (profile.getMachineSettingFloat('machine_width') - 5.0, 20, feedTravel))
1171                         self.comm.sendCommand('G1 Z0 F%d' % (feedZ))
1172                         self.comm.sendCommand('M400')
1173                         self._wizardState = 7
1174                 elif self._wizardState == 8:
1175                         wx.CallAfter(self.infoBox.SetBusy, _('Heating up printer...'))
1176                         self.comm.sendCommand('G1 Z15 F%d' % (feedZ))
1177                         self.comm.sendCommand('M104 S%d' % (profile.getProfileSettingFloat('print_temperature')))
1178                         self.comm.sendCommand('G1 X%d Y%d F%d' % (0, 0, feedTravel))
1179                         self._wizardState = 9
1180                 elif self._wizardState == 10:
1181                         self._wizardState = 11
1182                         wx.CallAfter(self.infoBox.SetInfo, _('Printing a square on the printer bed at 0.3mm height.'))
1183                         feedZ = profile.getProfileSettingFloat('print_speed') * 60
1184                         feedPrint = profile.getProfileSettingFloat('print_speed') * 60
1185                         feedTravel = profile.getProfileSettingFloat('travel_speed') * 60
1186                         w = profile.getMachineSettingFloat('machine_width') - 10
1187                         d = profile.getMachineSettingFloat('machine_depth')
1188                         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1189                         filamentArea = math.pi * filamentRadius * filamentRadius
1190                         ePerMM = (profile.calculateEdgeWidth() * 0.3) / filamentArea
1191                         eValue = 0.0
1192
1193                         gcodeList = [
1194                                 'G1 Z2 F%d' % (feedZ),
1195                                 'G92 E0',
1196                                 'G1 X%d Y%d F%d' % (5, 5, feedTravel),
1197                                 'G1 Z0.3 F%d' % (feedZ)]
1198                         eValue += 5.0
1199                         gcodeList.append('G1 E%f F%d' % (eValue, profile.getProfileSettingFloat('retraction_speed') * 60))
1200
1201                         for i in xrange(0, 3):
1202                                 dist = 5.0 + 0.4 * float(i)
1203                                 eValue += (d - 2.0*dist) * ePerMM
1204                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, d - dist, eValue, feedPrint))
1205                                 eValue += (w - 2.0*dist) * ePerMM
1206                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, d - dist, eValue, feedPrint))
1207                                 eValue += (d - 2.0*dist) * ePerMM
1208                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (w - dist, dist, eValue, feedPrint))
1209                                 eValue += (w - 2.0*dist) * ePerMM
1210                                 gcodeList.append('G1 X%f Y%f E%f F%d' % (dist, dist, eValue, feedPrint))
1211
1212                         gcodeList.append('M400')
1213                         self.comm.printGCode(gcodeList)
1214                 self.resumeButton.Enable(False)
1215
1216         def mcLog(self, message):
1217                 print 'Log:', message
1218
1219         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1220                 if self._wizardState == 1:
1221                         self._wizardState = 2
1222                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front left screw of your printer bed\nSo the nozzle just hits the bed.'))
1223                         wx.CallAfter(self.resumeButton.Enable, True)
1224                 elif self._wizardState == 3:
1225                         self._wizardState = 4
1226                         if profile.getMachineSetting('has_heated_bed') == 'True':
1227                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back screw of your printer bed\nSo the nozzle just hits the bed.'))
1228                         else:
1229                                 wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back left screw of your printer bed\nSo the nozzle just hits the bed.'))
1230                         wx.CallAfter(self.resumeButton.Enable, True)
1231                 elif self._wizardState == 5:
1232                         self._wizardState = 6
1233                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the back right screw of your printer bed\nSo the nozzle just hits the bed.'))
1234                         wx.CallAfter(self.resumeButton.Enable, True)
1235                 elif self._wizardState == 7:
1236                         self._wizardState = 8
1237                         wx.CallAfter(self.infoBox.SetAttention, _('Adjust the front right screw of your printer bed\nSo the nozzle just hits the bed.'))
1238                         wx.CallAfter(self.resumeButton.Enable, True)
1239                 elif self._wizardState == 9:
1240                         if temp[0] < profile.getProfileSettingFloat('print_temperature') - 5:
1241                                 wx.CallAfter(self.infoBox.SetInfo, _('Heating up printer: %d/%d') % (temp[0], profile.getProfileSettingFloat('print_temperature')))
1242                         else:
1243                                 wx.CallAfter(self.infoBox.SetAttention, _('The printer is hot now. Please insert some PLA filament into the printer.'))
1244                                 wx.CallAfter(self.resumeButton.Enable, True)
1245                                 self._wizardState = 10
1246
1247         def mcStateChange(self, state):
1248                 if self.comm is None:
1249                         return
1250                 if self.comm.isOperational():
1251                         if self._wizardState == 0:
1252                                 wx.CallAfter(self.infoBox.SetAttention, _('Use the up/down buttons to move the bed and adjust your Z endstop.'))
1253                                 wx.CallAfter(self.upButton.Enable, True)
1254                                 wx.CallAfter(self.downButton.Enable, True)
1255                                 wx.CallAfter(self.upButton2.Enable, True)
1256                                 wx.CallAfter(self.downButton2.Enable, True)
1257                                 wx.CallAfter(self.resumeButton.Enable, True)
1258                                 self._wizardState = -1
1259                         elif self._wizardState == 11 and not self.comm.isPrinting():
1260                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1261                                 self.comm.sendCommand('G92 E0')
1262                                 self.comm.sendCommand('G1 E-10 F%d' % (profile.getProfileSettingFloat('retraction_speed') * 60))
1263                                 self.comm.sendCommand('M104 S0')
1264                                 wx.CallAfter(self.infoBox.SetInfo, _('Calibration finished.\nThe squares on the bed should slightly touch each other.'))
1265                                 wx.CallAfter(self.infoBox.SetReadyIndicator)
1266                                 wx.CallAfter(self.GetParent().FindWindowById(wx.ID_FORWARD).Enable)
1267                                 wx.CallAfter(self.connectButton.Enable, True)
1268                                 self._wizardState = 12
1269                 elif self.comm.isError():
1270                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1271
1272         def mcMessage(self, message):
1273                 pass
1274
1275         def mcProgress(self, lineNr):
1276                 pass
1277
1278         def mcZChange(self, newZ):
1279                 pass
1280
1281 class headOffsetCalibrationPage(InfoPage):
1282         def __init__(self, parent):
1283                 super(headOffsetCalibrationPage, self).__init__(parent, "Printer head offset calibration")
1284
1285                 self.AddText(_('This wizard will help you in calibrating the printer head offsets of your dual extrusion machine'))
1286                 self.AddSeperator()
1287
1288                 self.connectButton = self.AddButton(_('Connect to printer'))
1289                 self.comm = None
1290
1291                 self.infoBox = self.AddInfoBox()
1292                 self.textEntry = self.AddTextCtrl('')
1293                 self.textEntry.Enable(False)
1294                 self.resumeButton = self.AddButton(_('Resume'))
1295                 self.resumeButton.Enable(False)
1296
1297                 self.Bind(wx.EVT_BUTTON, self.OnConnect, self.connectButton)
1298                 self.Bind(wx.EVT_BUTTON, self.OnResume, self.resumeButton)
1299
1300         def AllowBack(self):
1301                 return True
1302
1303         def OnConnect(self, e = None):
1304                 if self.comm is not None:
1305                         self.comm.close()
1306                         del self.comm
1307                         self.comm = None
1308                         wx.CallAfter(self.OnConnect)
1309                         return
1310                 self.connectButton.Enable(False)
1311                 self.comm = machineCom.MachineCom(callbackObject=self)
1312                 self.infoBox.SetBusy(_('Connecting to machine.'))
1313                 self._wizardState = 0
1314
1315         def OnResume(self, e):
1316                 if self._wizardState == 2:
1317                         self._wizardState = 3
1318                         wx.CallAfter(self.infoBox.SetBusy, _('Printing initial calibration cross'))
1319
1320                         w = profile.getMachineSettingFloat('machine_width')
1321                         d = profile.getMachineSettingFloat('machine_depth')
1322
1323                         gcode = gcodeGenerator.gcodeGenerator()
1324                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1325                         gcode.setPrintSpeed(profile.getProfileSettingFloat('bottom_layer_speed'))
1326                         gcode.addCmd('T0')
1327                         gcode.addPrime(15)
1328                         gcode.addCmd('T1')
1329                         gcode.addPrime(15)
1330
1331                         gcode.addCmd('T0')
1332                         gcode.addMove(w/2, 5)
1333                         gcode.addMove(z=0.2)
1334                         gcode.addPrime()
1335                         gcode.addExtrude(w/2, d-5.0)
1336                         gcode.addRetract()
1337                         gcode.addMove(5, d/2)
1338                         gcode.addPrime()
1339                         gcode.addExtrude(w-5.0, d/2)
1340                         gcode.addRetract(15)
1341
1342                         gcode.addCmd('T1')
1343                         gcode.addMove(w/2, 5)
1344                         gcode.addPrime()
1345                         gcode.addExtrude(w/2, d-5.0)
1346                         gcode.addRetract()
1347                         gcode.addMove(5, d/2)
1348                         gcode.addPrime()
1349                         gcode.addExtrude(w-5.0, d/2)
1350                         gcode.addRetract(15)
1351                         gcode.addCmd('T0')
1352
1353                         gcode.addMove(z=25)
1354                         gcode.addMove(0, 0)
1355                         gcode.addCmd('M400')
1356
1357                         self.comm.printGCode(gcode.list())
1358                         self.resumeButton.Enable(False)
1359                 elif self._wizardState == 4:
1360                         try:
1361                                 float(self.textEntry.GetValue())
1362                         except ValueError:
1363                                 return
1364                         profile.putPreference('extruder_offset_x1', self.textEntry.GetValue())
1365                         self._wizardState = 5
1366                         self.infoBox.SetAttention(_('Please measure the distance between the horizontal lines in millimeters.'))
1367                         self.textEntry.SetValue('0.0')
1368                         self.textEntry.Enable(True)
1369                 elif self._wizardState == 5:
1370                         try:
1371                                 float(self.textEntry.GetValue())
1372                         except ValueError:
1373                                 return
1374                         profile.putPreference('extruder_offset_y1', self.textEntry.GetValue())
1375                         self._wizardState = 6
1376                         self.infoBox.SetBusy(_('Printing the fine calibration lines.'))
1377                         self.textEntry.SetValue('')
1378                         self.textEntry.Enable(False)
1379                         self.resumeButton.Enable(False)
1380
1381                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1382                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1383                         gcode = gcodeGenerator.gcodeGenerator()
1384                         gcode.setExtrusionRate(profile.getProfileSettingFloat('nozzle_size') * 1.5, 0.2)
1385                         gcode.setPrintSpeed(25)
1386                         gcode.addHome()
1387                         gcode.addCmd('T0')
1388                         gcode.addMove(50, 40, 0.2)
1389                         gcode.addPrime(15)
1390                         for n in xrange(0, 10):
1391                                 gcode.addExtrude(50 + n * 10, 150)
1392                                 gcode.addExtrude(50 + n * 10 + 5, 150)
1393                                 gcode.addExtrude(50 + n * 10 + 5, 40)
1394                                 gcode.addExtrude(50 + n * 10 + 10, 40)
1395                         gcode.addMove(40, 50)
1396                         for n in xrange(0, 10):
1397                                 gcode.addExtrude(150, 50 + n * 10)
1398                                 gcode.addExtrude(150, 50 + n * 10 + 5)
1399                                 gcode.addExtrude(40, 50 + n * 10 + 5)
1400                                 gcode.addExtrude(40, 50 + n * 10 + 10)
1401                         gcode.addRetract(15)
1402
1403                         gcode.addCmd('T1')
1404                         gcode.addMove(50 - x, 30 - y, 0.2)
1405                         gcode.addPrime(15)
1406                         for n in xrange(0, 10):
1407                                 gcode.addExtrude(50 + n * 10.2 - 1.0 - x, 140 - y)
1408                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 140 - y)
1409                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 5.1 - x, 30 - y)
1410                                 gcode.addExtrude(50 + n * 10.2 - 1.0 + 10 - x, 30 - y)
1411                         gcode.addMove(30 - x, 50 - y, 0.2)
1412                         for n in xrange(0, 10):
1413                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 - y)
1414                                 gcode.addExtrude(160 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1415                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 5.1 - y)
1416                                 gcode.addExtrude(30 - x, 50 + n * 10.2 - 1.0 + 10 - y)
1417                         gcode.addRetract(15)
1418                         gcode.addMove(z=15)
1419                         gcode.addCmd('M400')
1420                         gcode.addCmd('M104 T0 S0')
1421                         gcode.addCmd('M104 T1 S0')
1422                         self.comm.printGCode(gcode.list())
1423                 elif self._wizardState == 7:
1424                         try:
1425                                 n = int(self.textEntry.GetValue()) - 1
1426                         except:
1427                                 return
1428                         x = profile.getMachineSettingFloat('extruder_offset_x1')
1429                         x += -1.0 + n * 0.1
1430                         profile.putPreference('extruder_offset_x1', '%0.2f' % (x))
1431                         self.infoBox.SetAttention(_('Which horizontal line number lays perfect on top of each other? Front most line is zero.'))
1432                         self.textEntry.SetValue('10')
1433                         self._wizardState = 8
1434                 elif self._wizardState == 8:
1435                         try:
1436                                 n = int(self.textEntry.GetValue()) - 1
1437                         except:
1438                                 return
1439                         y = profile.getMachineSettingFloat('extruder_offset_y1')
1440                         y += -1.0 + n * 0.1
1441                         profile.putPreference('extruder_offset_y1', '%0.2f' % (y))
1442                         self.infoBox.SetInfo(_('Calibration finished. Offsets are: %s %s') % (profile.getMachineSettingFloat('extruder_offset_x1'), profile.getMachineSettingFloat('extruder_offset_y1')))
1443                         self.infoBox.SetReadyIndicator()
1444                         self._wizardState = 8
1445                         self.comm.close()
1446                         self.resumeButton.Enable(False)
1447
1448         def mcLog(self, message):
1449                 print 'Log:', message
1450
1451         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
1452                 if self._wizardState == 1:
1453                         if temp[0] >= 210 and temp[1] >= 210:
1454                                 self._wizardState = 2
1455                                 wx.CallAfter(self.infoBox.SetAttention, _('Please load both extruders with PLA.'))
1456                                 wx.CallAfter(self.resumeButton.Enable, True)
1457                                 wx.CallAfter(self.resumeButton.SetFocus)
1458
1459         def mcStateChange(self, state):
1460                 if self.comm is None:
1461                         return
1462                 if self.comm.isOperational():
1463                         if self._wizardState == 0:
1464                                 wx.CallAfter(self.infoBox.SetInfo, _('Homing printer and heating up both extruders.'))
1465                                 self.comm.sendCommand('M105')
1466                                 self.comm.sendCommand('M104 S220 T0')
1467                                 self.comm.sendCommand('M104 S220 T1')
1468                                 self.comm.sendCommand('G28')
1469                                 self.comm.sendCommand('G1 Z15 F%d' % (profile.getProfileSettingFloat('print_speed') * 60))
1470                                 self._wizardState = 1
1471                         if not self.comm.isPrinting():
1472                                 if self._wizardState == 3:
1473                                         self._wizardState = 4
1474                                         wx.CallAfter(self.infoBox.SetAttention, _('Please measure the distance between the vertical lines in millimeters.'))
1475                                         wx.CallAfter(self.textEntry.SetValue, '0.0')
1476                                         wx.CallAfter(self.textEntry.Enable, True)
1477                                         wx.CallAfter(self.resumeButton.Enable, True)
1478                                         wx.CallAfter(self.resumeButton.SetFocus)
1479                                 elif self._wizardState == 6:
1480                                         self._wizardState = 7
1481                                         wx.CallAfter(self.infoBox.SetAttention, _('Which vertical line number lays perfect on top of each other? Leftmost line is zero.'))
1482                                         wx.CallAfter(self.textEntry.SetValue, '10')
1483                                         wx.CallAfter(self.textEntry.Enable, True)
1484                                         wx.CallAfter(self.resumeButton.Enable, True)
1485                                         wx.CallAfter(self.resumeButton.SetFocus)
1486
1487                 elif self.comm.isError():
1488                         wx.CallAfter(self.infoBox.SetError, _('Failed to establish connection with the printer.'), 'http://wiki.ultimaker.com/Cura:_Connection_problems')
1489
1490         def mcMessage(self, message):
1491                 pass
1492
1493         def mcProgress(self, lineNr):
1494                 pass
1495
1496         def mcZChange(self, newZ):
1497                 pass
1498
1499 class bedLevelWizard(wx.wizard.Wizard):
1500         def __init__(self):
1501                 super(bedLevelWizard, self).__init__(None, -1, _("Bed leveling wizard"))
1502
1503                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1504                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1505
1506                 self.mainPage = bedLevelWizardMain(self)
1507                 self.headOffsetCalibration = None
1508
1509                 self.FitToPage(self.mainPage)
1510                 self.GetPageAreaSizer().Add(self.mainPage)
1511
1512                 self.RunWizard(self.mainPage)
1513                 self.Destroy()
1514
1515         def OnPageChanging(self, e):
1516                 e.GetPage().StoreData()
1517
1518         def OnPageChanged(self, e):
1519                 if e.GetPage().AllowNext():
1520                         self.FindWindowById(wx.ID_FORWARD).Enable()
1521                 else:
1522                         self.FindWindowById(wx.ID_FORWARD).Disable()
1523                 if e.GetPage().AllowBack():
1524                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1525                 else:
1526                         self.FindWindowById(wx.ID_BACKWARD).Disable()
1527
1528 class headOffsetWizard(wx.wizard.Wizard):
1529         def __init__(self):
1530                 super(headOffsetWizard, self).__init__(None, -1, _("Head offset wizard"))
1531
1532                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
1533                 self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
1534
1535                 self.mainPage = headOffsetCalibrationPage(self)
1536
1537                 self.FitToPage(self.mainPage)
1538                 self.GetPageAreaSizer().Add(self.mainPage)
1539
1540                 self.RunWizard(self.mainPage)
1541                 self.Destroy()
1542
1543         def OnPageChanging(self, e):
1544                 e.GetPage().StoreData()
1545
1546         def OnPageChanged(self, e):
1547                 if e.GetPage().AllowNext():
1548                         self.FindWindowById(wx.ID_FORWARD).Enable()
1549                 else:
1550                         self.FindWindowById(wx.ID_FORWARD).Disable()
1551                 if e.GetPage().AllowBack():
1552                         self.FindWindowById(wx.ID_BACKWARD).Enable()
1553                 else:
1554                         self.FindWindowById(wx.ID_BACKWARD).Disable()