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