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