1 from __future__ import absolute_import
4 import wx, os, platform, types, webbrowser
6 from newui import configBase
7 from newui import advancedConfig
8 from newui import preview3d
9 from newui import sliceProgessPanel
10 from newui import alterationPanel
11 from newui import validators
12 from newui import preferencesDialog
13 from newui import configWizard
14 from newui import machineCom
15 from newui import profile
16 from newui import printWindow
20 if profile.getPreference('wizardDone') == 'False':
21 if os.name == 'darwin':
22 wx.MessageBox('The MacOS version of Cura is experimental.\nThere are still UI/usability bugs. Check the issue list at:\nhttps://github.com/daid/Cura/issues\nfor details.\nPlease report any extra issue you find.', 'MacOS Warning', wx.OK | wx.ICON_INFORMATION)
23 configWizard.configWizard()
24 profile.putPreference("wizardDone", "True")
28 class mainWindow(configBase.configWindowBase):
29 "Main user interface window"
31 super(mainWindow, self).__init__(title='Cura')
33 wx.EVT_CLOSE(self, self.OnClose)
35 menubar = wx.MenuBar()
37 i = fileMenu.Append(-1, 'Load model file...')
38 self.Bind(wx.EVT_MENU, self.OnLoadModel, i)
39 fileMenu.AppendSeparator()
40 i = fileMenu.Append(-1, 'Open Profile...')
41 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
42 i = fileMenu.Append(-1, 'Save Profile...')
43 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
44 fileMenu.AppendSeparator()
45 i = fileMenu.Append(-1, 'Preferences...')
46 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
47 fileMenu.AppendSeparator()
48 i = fileMenu.Append(wx.ID_EXIT, 'Quit')
49 self.Bind(wx.EVT_MENU, self.OnQuit, i)
50 menubar.Append(fileMenu, '&File')
52 expertMenu = wx.Menu()
53 i = expertMenu.Append(-1, 'Open expert settings...')
54 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
55 expertMenu.AppendSeparator()
56 i = expertMenu.Append(-1, 'Install default Marlin firmware')
57 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
58 i = expertMenu.Append(-1, 'Install custom firmware')
59 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
60 expertMenu.AppendSeparator()
61 i = expertMenu.Append(-1, 'ReRun first run wizard...')
62 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
63 menubar.Append(expertMenu, 'Expert')
66 i = helpMenu.Append(-1, 'Online documentation...')
67 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/wiki'), i)
68 i = helpMenu.Append(-1, 'Report a problem...')
69 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
70 menubar.Append(helpMenu, 'Help')
71 self.SetMenuBar(menubar)
74 self.filename = profile.getPreference('lastFile')
75 self.progressPanelList = []
78 self.preview3d = preview3d.previewPanel(self)
81 nb = wx.Notebook(self)
83 (left, right) = self.CreateConfigTab(nb, 'Print config')
85 configBase.TitleRow(left, "Accuracy")
86 c = configBase.SettingRow(left, "Layer height (mm)", 'layer_height', '0.2', 'Layer height in millimeters.\n0.2 is a good value for quick prints.\n0.1 gives high quality prints.')
87 validators.validFloat(c, 0.0)
88 validators.warningAbove(c, lambda : (float(profile.getProfileSetting('nozzle_size')) * 80 / 100), "Thicker layers then %.2fmm (80%% nozzle size) usually give bad results and are not recommended.")
89 c = configBase.SettingRow(left, "Wall thickness (mm)", 'wall_thickness', '0.8', 'Thickness of the walls.\nThis is used in combination with the nozzle size to define the number\nof perimeter lines and the thickness of those perimeter lines.')
90 validators.validFloat(c, 0.0)
91 validators.wallThicknessValidator(c)
93 configBase.TitleRow(left, "Fill")
94 c = configBase.SettingRow(left, "Bottom/Top thickness (mm)", 'solid_layer_thickness', '0.6', 'This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value.\nHaving this value a multiply of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.')
95 validators.validFloat(c, 0.0)
96 c = configBase.SettingRow(left, "Fill Density (%)", 'fill_density', '20', 'This controls how densily filled the insides of your print will be. For a solid part use 100%, for an empty part use 0%. A value around 20% is usually enough')
97 validators.validFloat(c, 0.0, 100.0)
99 configBase.TitleRow(left, "Skirt")
100 c = configBase.SettingRow(left, "Line count", 'skirt_line_count', '1', 'The skirt is a line drawn around the object at the first layer. This helps to prime your extruder, and to see if the object fits on your platform.\nSetting this to 0 will disable the skirt.')
101 validators.validInt(c, 0, 10)
102 c = configBase.SettingRow(left, "Start distance (mm)", 'skirt_gap', '6.0', 'The distance between the skirt and the first layer.\nThis is the minimal distance, multiple skirt lines will be put outwards from this distance.')
103 validators.validFloat(c, 0.0)
105 configBase.TitleRow(right, "Speed")
106 c = configBase.SettingRow(right, "Print speed (mm/s)", 'print_speed', '50', 'Speed at which printing happens. A well adjusted Ultimaker can reach 150mm/s, but for good quality prints you want to print slower. Printing speed depends on a lot of factors. So you will be experimenting with optimal settings for this.')
107 validators.validFloat(c, 1.0)
108 validators.warningAbove(c, 150.0, "It is highly unlikely that your machine can achieve a printing speed above 150mm/s")
109 validators.printSpeedValidator(c)
111 configBase.TitleRow(right, "Temperature")
112 c = configBase.SettingRow(right, "Printing temperature", 'print_temperature', '0', 'Temperature used for printing. Set at 0 to pre-heat yourself')
113 validators.validFloat(c, 0.0, 340.0)
114 validators.warningAbove(c, 260.0, "Temperatures above 260C could damage your machine, be careful!")
116 configBase.TitleRow(right, "Support")
117 c = configBase.SettingRow(right, "Support type", 'support', ['None', 'Exterior Only', 'Everywhere', 'Empty Layers Only'], 'Type of support structure build.\nNone does not do any support.\nExterior only only creates support on the outside.\nEverywhere creates support even on the insides of the model.\nOnly on empty layers is for stacked objects.')
118 c = configBase.SettingRow(right, "Add raft", 'enable_raft', False, 'A raft is a few layers of lines below the bottom of the object. It prevents warping. Full raft settings can be found in the advanced settings.\nFor PLA this is usually not required. But if you print with ABS it is almost required.')
120 configBase.TitleRow(right, "Filament")
121 c = configBase.SettingRow(right, "Diameter (mm)", 'filament_diameter', '2.89', 'Diameter of your filament, as accurately as possible.\nIf you cannot measure this value you will have to callibrate it, a higher number means less extrusion, a smaller number generates more extrusion.')
122 validators.validFloat(c, 1.0)
123 validators.warningAbove(c, 3.5, "Are you sure your filament is that thick? Normal filament is around 3mm or 1.75mm.")
124 c = configBase.SettingRow(right, "Packing Density", 'filament_density', '1.00', 'Packing density of your filament. This should be 1.00 for PLA and 0.85 for ABS')
125 validators.validFloat(c, 0.5, 1.5)
127 (left, right) = self.CreateConfigTab(nb, 'Advanced config')
129 configBase.TitleRow(left, "Machine size")
130 c = configBase.SettingRow(left, "Nozzle size (mm)", 'nozzle_size', '0.4', 'The nozzle size is very important, this is used to calculate the line width of the infill, and used to calculate the amount of outside wall lines and thickness for the wall thickness you entered in the print settings.')
131 validators.validFloat(c, 0.1, 1.0)
132 c = configBase.SettingRow(left, "Machine center X (mm)", 'machine_center_x', '100', 'The center of your machine, your print will be placed at this location')
133 validators.validInt(c, 10)
134 configBase.settingNotify(c, self.preview3d.updateCenterX)
135 c = configBase.SettingRow(left, "Machine center Y (mm)", 'machine_center_y', '100', 'The center of your machine, your print will be placed at this location')
136 validators.validInt(c, 10)
137 configBase.settingNotify(c, self.preview3d.updateCenterY)
139 configBase.TitleRow(left, "Retraction")
140 c = configBase.SettingRow(left, "Minimal travel (mm)", 'retraction_min_travel', '5.0', 'Minimal amount of travel needed for a retraction to happen at all. To make sure you do not get a lot of retractions in a small area')
141 validators.validFloat(c, 0.0)
142 c = configBase.SettingRow(left, "Speed (mm/s)", 'retraction_speed', '13.5', 'Speed at which the filament is retracted')
143 validators.validFloat(c, 0.1)
144 c = configBase.SettingRow(left, "Distance (mm)", 'retraction_amount', '0.0', 'Amount of retraction, set at 0 for no retraction at all.')
145 validators.validFloat(c, 0.0)
146 c = configBase.SettingRow(left, "Extra length on start (mm)", 'retraction_extra', '0.0', 'Extra extrusion amount when restarting after a retraction, to better "Prime" your extruder')
147 validators.validFloat(c, 0.0)
149 configBase.TitleRow(right, "Speed")
150 c = configBase.SettingRow(right, "Travel speed (mm/s)", 'travel_speed', '150', 'Speed at which travel moves are done')
151 validators.validFloat(c, 1.0)
152 validators.warningAbove(c, 300.0, "It is highly unlikely that your machine can achieve a travel speed above 150mm/s")
153 c = configBase.SettingRow(right, "Max Z speed (mm/s)", 'max_z_speed', '1.0', 'Speed at which Z moves are done.')
154 validators.validFloat(c, 0.5)
155 c = configBase.SettingRow(right, "Bottom layer speed (mm/s)", 'bottom_layer_speed', '25', 'Print speed for the bottom layer, you want to print the first layer slower so it sticks better to the printer bed.')
156 validators.validFloat(c, 0.0)
158 configBase.TitleRow(right, "Cool")
159 c = configBase.SettingRow(right, "Minimal layer time (sec)", 'cool_min_layer_time', '10', 'Minimum time spend in a layer, gives the layer time to cool down before the next layer is put on top. If the layer will be placed down too fast the printer will slow down to make sure it has spend atleast this amount of seconds printing this layer.')
160 validators.validFloat(c, 0.0)
162 nb.AddPage(alterationPanel.alterationPanel(nb), "Start/End-GCode")
164 # load and slice buttons.
165 loadButton = wx.Button(self, -1, 'Load Model')
166 sliceButton = wx.Button(self, -1, 'Slice to GCode')
167 printButton = wx.Button(self, -1, 'Print GCode')
168 self.Bind(wx.EVT_BUTTON, self.OnLoadModel, loadButton)
169 self.Bind(wx.EVT_BUTTON, self.OnSlice, sliceButton)
170 self.Bind(wx.EVT_BUTTON, self.OnPrint, printButton)
171 #Also bind double clicking the 3D preview to load an STL file.
172 self.preview3d.glCanvas.Bind(wx.EVT_LEFT_DCLICK, self.OnLoadModel, self.preview3d.glCanvas)
174 #Main sizer, to position the preview window, buttons and tab control
175 sizer = wx.GridBagSizer()
177 sizer.Add(nb, (0,0), span=(1,1), flag=wx.EXPAND)
178 sizer.Add(self.preview3d, (0,1), span=(1,3), flag=wx.EXPAND)
179 sizer.AddGrowableCol(2)
180 sizer.AddGrowableRow(0)
181 sizer.Add(loadButton, (1,1), flag=wx.RIGHT, border=5)
182 sizer.Add(sliceButton, (1,2), flag=wx.RIGHT, border=5)
183 sizer.Add(printButton, (1,3), flag=wx.RIGHT, border=5)
186 if self.filename != "None":
187 self.preview3d.loadModelFile(self.filename)
188 self.lastPath = os.path.split(self.filename)[0]
190 self.updateProfileToControls()
193 self.SetMinSize(self.GetSize())
197 def OnLoadProfile(self, e):
198 dlg=wx.FileDialog(self, "Select profile file to load", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
199 dlg.SetWildcard("ini files (*.ini)|*.ini")
200 if dlg.ShowModal() == wx.ID_OK:
201 profileFile = dlg.GetPath()
202 self.lastPath = os.path.split(profileFile)[0]
203 profile.loadGlobalProfile(profileFile)
204 self.updateProfileToControls()
207 def OnSaveProfile(self, e):
208 dlg=wx.FileDialog(self, "Select profile file to save", self.lastPath, style=wx.FD_SAVE)
209 dlg.SetWildcard("ini files (*.ini)|*.ini")
210 if dlg.ShowModal() == wx.ID_OK:
211 profileFile = dlg.GetPath()
212 self.lastPath = os.path.split(profileFile)[0]
213 profile.saveGlobalProfile(profileFile)
216 def OnPreferences(self, e):
217 prefDialog = preferencesDialog.preferencesDialog(self)
219 prefDialog.Show(True)
221 def OnDefaultMarlinFirmware(self, e):
222 machineCom.InstallFirmware(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../firmware/default.hex"))
224 def OnCustomFirmware(self, e):
225 dlg=wx.FileDialog(self, "Open firmware to upload", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
226 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
227 if dlg.ShowModal() == wx.ID_OK:
228 filename = dlg.GetPath()
229 if not(os.path.exists(filename)):
231 #For some reason my Ubuntu 10.10 crashes here.
232 machineCom.InstallFirmware(filename)
234 def OnFirstRunWizard(self, e):
235 configWizard.configWizard()
236 self.updateProfileToControls()
238 def OnLoadModel(self, e):
239 dlg=wx.FileDialog(self, "Open file to print", self.lastPath, style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
240 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
241 if dlg.ShowModal() == wx.ID_OK:
242 self.filename=dlg.GetPath()
243 profile.putPreference('lastFile', self.filename)
244 if not(os.path.exists(self.filename)):
246 self.lastPath = os.path.split(self.filename)[0]
247 self.preview3d.loadModelFile(self.filename)
248 self.preview3d.setViewMode("Model - Normal")
251 def OnSlice(self, e):
252 if self.filename == None:
254 profile.saveGlobalProfile(profile.getDefaultProfilePath())
256 #Create a progress panel and add it to the window. The progress panel will start the Skein operation.
257 spp = sliceProgessPanel.sliceProgessPanel(self, self, self.filename)
258 self.sizer.Add(spp, (len(self.progressPanelList)+2,0), span=(1,4), flag=wx.EXPAND)
260 newSize = self.GetSize();
261 newSize.IncBy(0, spp.GetSize().GetHeight())
262 self.SetSize(newSize)
263 self.progressPanelList.append(spp)
265 def OnPrint(self, e):
266 printWindow.printWindow()
268 def OnExpertOpen(self, e):
269 acw = advancedConfig.advancedConfigWindow()
273 def removeSliceProgress(self, spp):
274 self.progressPanelList.remove(spp)
275 newSize = self.GetSize();
276 newSize.IncBy(0, -spp.GetSize().GetHeight())
277 self.SetSize(newSize)
278 self.sizer.Remove(spp)
280 for spp in self.progressPanelList:
281 self.sizer.Remove(spp)
283 for spp in self.progressPanelList:
284 self.sizer.Add(spp, (i,0), span=(1,4), flag=wx.EXPAND)
291 def OnClose(self, e):
292 profile.saveGlobalProfile(profile.getDefaultProfilePath())