1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
9 from Cura.gui import configBase
10 from Cura.gui import expertConfig
11 from Cura.gui import alterationPanel
12 from Cura.gui import pluginPanel
13 from Cura.gui import preferencesDialog
14 from Cura.gui import configWizard
15 from Cura.gui import firmwareInstall
16 from Cura.gui import simpleMode
17 from Cura.gui import sceneView
18 from Cura.gui import aboutWindow
19 from Cura.gui.util import dropTarget
20 #from Cura.gui.tools import batchRun
21 from Cura.gui.tools import pidDebugger
22 from Cura.gui.tools import minecraftImport
23 from Cura.util import profile
24 from Cura.util import version
26 from Cura.util import meshLoader
28 from wx.lib.pubsub import Publisher
30 class mainWindow(wx.Frame):
32 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
34 wx.EVT_CLOSE(self, self.OnClose)
36 # allow dropping any file, restrict later
37 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
39 # TODO: wxWidgets 2.9.4 has a bug when NSView does not register for dragged types when wx drop target is set. It was fixed in 2.9.5
40 if sys.platform.startswith('darwin'):
43 nswindow = objc.objc_object(c_void_p=self.MacGetTopLevelWindowRef())
44 view = nswindow.contentView()
45 view.registerForDraggedTypes_([u'NSFilenamesPboardType'])
49 self.normalModeOnlyItems = []
51 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
52 self.config = wx.FileConfig(appName="Cura",
53 localFilename=mruFile,
54 style=wx.CONFIG_USE_LOCAL_FILE)
56 self.ID_MRU_MODEL1, self.ID_MRU_MODEL2, self.ID_MRU_MODEL3, self.ID_MRU_MODEL4, self.ID_MRU_MODEL5, self.ID_MRU_MODEL6, self.ID_MRU_MODEL7, self.ID_MRU_MODEL8, self.ID_MRU_MODEL9, self.ID_MRU_MODEL10 = [wx.NewId() for line in xrange(10)]
57 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
58 self.config.SetPath("/ModelMRU")
59 self.modelFileHistory.Load(self.config)
61 self.ID_MRU_PROFILE1, self.ID_MRU_PROFILE2, self.ID_MRU_PROFILE3, self.ID_MRU_PROFILE4, self.ID_MRU_PROFILE5, self.ID_MRU_PROFILE6, self.ID_MRU_PROFILE7, self.ID_MRU_PROFILE8, self.ID_MRU_PROFILE9, self.ID_MRU_PROFILE10 = [wx.NewId() for line in xrange(10)]
62 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
63 self.config.SetPath("/ProfileMRU")
64 self.profileFileHistory.Load(self.config)
66 self.menubar = wx.MenuBar()
67 self.fileMenu = wx.Menu()
68 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
69 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
70 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
71 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
72 i = self.fileMenu.Append(-1, _("Reload platform\tF5"))
73 self.Bind(wx.EVT_MENU, lambda e: self.scene.reloadScene(e), i)
74 i = self.fileMenu.Append(-1, _("Clear platform"))
75 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
77 self.fileMenu.AppendSeparator()
78 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
79 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnPrintButton(1), i)
80 i = self.fileMenu.Append(-1, _("Save GCode..."))
81 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
82 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
83 self.Bind(wx.EVT_MENU, lambda e: self.scene._showEngineLog(), i)
85 self.fileMenu.AppendSeparator()
86 i = self.fileMenu.Append(-1, _("Open Profile..."))
87 self.normalModeOnlyItems.append(i)
88 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
89 i = self.fileMenu.Append(-1, _("Save Profile..."))
90 self.normalModeOnlyItems.append(i)
91 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
92 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
93 self.normalModeOnlyItems.append(i)
94 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
95 self.fileMenu.AppendSeparator()
96 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
97 self.normalModeOnlyItems.append(i)
98 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
100 self.fileMenu.AppendSeparator()
101 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
102 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
103 i = self.fileMenu.Append(-1, _("Machine settings..."))
104 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
105 self.fileMenu.AppendSeparator()
108 modelHistoryMenu = wx.Menu()
109 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
110 self.modelFileHistory.UseMenu(modelHistoryMenu)
111 self.modelFileHistory.AddFilesToMenu()
112 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
115 profileHistoryMenu = wx.Menu()
116 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
117 self.profileFileHistory.UseMenu(profileHistoryMenu)
118 self.profileFileHistory.AddFilesToMenu()
119 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
121 self.fileMenu.AppendSeparator()
122 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
123 self.Bind(wx.EVT_MENU, self.OnQuit, i)
124 self.menubar.Append(self.fileMenu, '&' + _("File"))
126 toolsMenu = wx.Menu()
127 #i = toolsMenu.Append(-1, 'Batch run...')
128 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
129 #self.normalModeOnlyItems.append(i)
131 if minecraftImport.hasMinecraft():
132 i = toolsMenu.Append(-1, _("Minecraft map import..."))
133 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
135 if version.isDevVersion():
136 i = toolsMenu.Append(-1, _("PID Debugger..."))
137 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
138 i = toolsMenu.Append(-1, _("Auto Firmware Update..."))
139 self.Bind(wx.EVT_MENU, self.OnAutoFirmwareUpdate, i)
141 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
142 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
144 toolsMenu.AppendSeparator()
145 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
146 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
147 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
148 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
149 if profile.getPreference('oneAtATime') == 'True':
150 self.oneAtATime.Check(True)
152 self.allAtOnceItem.Check(True)
154 self.menubar.Append(toolsMenu, _("Tools"))
156 #Machine menu for machine configuration/tooling
157 self.machineMenu = wx.Menu()
158 self.updateMachineMenu()
160 self.menubar.Append(self.machineMenu, _("Machine"))
162 expertMenu = wx.Menu()
163 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
164 self.switchToQuickprintMenuItem = i
165 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
167 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
168 self.switchToNormalMenuItem = i
169 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
170 expertMenu.AppendSeparator()
172 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
173 self.normalModeOnlyItems.append(i)
174 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
175 expertMenu.AppendSeparator()
176 i = expertMenu.Append(-1, _("Run first run wizard..."))
177 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
178 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
179 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
180 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
181 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
183 self.menubar.Append(expertMenu, _("Expert"))
186 i = helpMenu.Append(-1, _("Online documentation..."))
187 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
188 i = helpMenu.Append(-1, _("Report a problem..."))
189 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
190 i = helpMenu.Append(-1, _("Check for update..."))
191 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
192 i = helpMenu.Append(-1, _("Open YouMagine website..."))
193 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
194 i = helpMenu.Append(-1, _("About Cura..."))
195 self.Bind(wx.EVT_MENU, self.OnAbout, i)
196 self.menubar.Append(helpMenu, _("Help"))
197 self.SetMenuBar(self.menubar)
199 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
200 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
201 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
202 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
205 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
206 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
208 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
209 self.leftSizer.Add(self.simpleSettingsPanel, 1)
210 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
211 self.leftPane.SetSizer(self.leftSizer)
214 self.scene = sceneView.SceneView(self.rightPane)
216 #Main sizer, to position the preview window, buttons and tab control
217 sizer = wx.BoxSizer()
218 self.rightPane.SetSizer(sizer)
219 sizer.Add(self.scene, 1, flag=wx.EXPAND)
222 sizer = wx.BoxSizer(wx.VERTICAL)
224 sizer.Add(self.splitter, 1, wx.EXPAND)
228 self.updateProfileToAllControls()
230 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
232 self.simpleSettingsPanel.Show(False)
233 self.normalSettingsPanel.Show(False)
235 # Set default window size & position
236 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
239 #Timer set; used to check if profile is on the clipboard
240 self.timer = wx.Timer(self)
241 self.Bind(wx.EVT_TIMER, self.onTimer)
242 self.timer.Start(1000)
243 self.lastTriedClipboard = profile.getProfileString()
245 # Restore the window position, size & state from the preferences file
247 if profile.getPreference('window_maximized') == 'True':
250 posx = int(profile.getPreference('window_pos_x'))
251 posy = int(profile.getPreference('window_pos_y'))
252 width = int(profile.getPreference('window_width'))
253 height = int(profile.getPreference('window_height'))
254 if posx > 0 or posy > 0:
255 self.SetPosition((posx,posy))
256 if width > 0 and height > 0:
257 self.SetSize((width,height))
259 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
261 self.normalSashPos = 0
263 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
264 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
266 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
268 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
270 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
272 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
273 self.SetSize((800,600))
276 self.updateSliceMode()
277 self.scene.SetFocus()
278 self.dialogframe = None
279 Publisher().subscribe(self.onPluginUpdate, "pluginupdate")
281 def onPluginUpdate(self,msg): #receives commands from the plugin thread
282 cmd = str(msg.data).split(";")
283 if cmd[0] == "OpenPluginProgressWindow":
284 if len(cmd)==1: #no titel received
286 if len(cmd)<3: #no message text received
287 cmd.append("Plugin is executed...")
290 self.dialogframe = wx.Frame(self, -1, cmd[1],pos = ((wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X)-dialogwidth)/2,(wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)-dialogheight)/2), size=(dialogwidth,dialogheight), style = wx.STAY_ON_TOP)
291 self.dialogpanel = wx.Panel(self.dialogframe, -1, pos = (0,0), size = (dialogwidth,dialogheight))
292 self.dlgtext = wx.StaticText(self.dialogpanel, label = cmd[2], pos = (10,10), size = (280,40))
293 self.dlgbar = wx.Gauge(self.dialogpanel,-1, 100, pos = (10,50), size = (280,20), style = wx.GA_HORIZONTAL)
294 self.dialogframe.Show()
296 elif cmd[0] == "Progress":
298 if number <= 100 and self.dialogframe is not None:
299 self.dlgbar.SetValue(number)
301 self.dlgbar.SetValue(100)
302 elif cmd[0] == "ClosePluginProgressWindow":
303 self.dialogframe.Destroy()
304 self.dialogframe=None
306 print "Unknown Plugin update received: " + cmd[0]
308 def onTimer(self, e):
309 #Check if there is something in the clipboard
312 if not wx.TheClipboard.IsOpened():
313 if not wx.TheClipboard.Open():
315 do = wx.TextDataObject()
316 if wx.TheClipboard.GetData(do):
317 profileString = do.GetText()
318 wx.TheClipboard.Close()
320 startTag = "CURA_PROFILE_STRING:"
321 if startTag in profileString:
322 #print "Found correct syntax on clipboard"
323 profileString = profileString.replace("\n","").strip()
324 profileString = profileString[profileString.find(startTag)+len(startTag):]
325 if profileString != self.lastTriedClipboard:
327 self.lastTriedClipboard = profileString
328 profile.setProfileFromString(profileString)
329 self.scene.notification.message("Loaded new profile from clipboard.")
330 self.updateProfileToAllControls()
332 print "Unable to read from clipboard"
335 def updateSliceMode(self):
336 isSimple = profile.getPreference('startMode') == 'Simple'
338 self.normalSettingsPanel.Show(not isSimple)
339 self.simpleSettingsPanel.Show(isSimple)
340 self.leftPane.Layout()
342 for i in self.normalModeOnlyItems:
343 i.Enable(not isSimple)
345 self.switchToQuickprintMenuItem.Check()
347 self.switchToNormalMenuItem.Check()
349 # Set splitter sash position & size
351 # Save normal mode sash
352 self.normalSashPos = self.splitter.GetSashPosition()
354 # Change location of sash to width of quick mode pane
355 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
356 self.splitter.SetSashPosition(width, True)
359 self.splitter.SetSashSize(0)
361 self.splitter.SetSashPosition(self.normalSashPos, True)
363 self.splitter.SetSashSize(4)
364 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
365 if profile.getMachineSetting('machine_type') == 'ultimaker2':
366 self.bedLevelWizardMenuItem.Enable(False)
367 self.headOffsetWizardMenuItem.Enable(False)
368 if int(profile.getMachineSetting('extruder_amount')) < 2:
369 self.headOffsetWizardMenuItem.Enable(False)
370 self.scene.updateProfileToControls()
371 self.scene._scene.pushFree()
373 def onOneAtATimeSwitch(self, e):
374 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
375 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
376 wx.MessageBox(_('For "One at a time" printing, you need to have entered the correct head size and gantry height in the machine settings'), _('One at a time warning'), wx.OK | wx.ICON_WARNING)
377 self.scene.updateProfileToControls()
378 self.scene._scene.pushFree()
379 self.scene.sceneUpdated()
381 def OnPreferences(self, e):
382 prefDialog = preferencesDialog.preferencesDialog(self)
386 wx.CallAfter(prefDialog.Show)
388 def OnMachineSettings(self, e):
389 prefDialog = preferencesDialog.machineSettingsDialog(self)
394 def OnDropFiles(self, files):
396 self.updateProfileToAllControls()
397 self.scene.loadFiles(files)
399 def OnModelMRU(self, e):
400 fileNum = e.GetId() - self.ID_MRU_MODEL1
401 path = self.modelFileHistory.GetHistoryFile(fileNum)
403 self.modelFileHistory.AddFileToHistory(path) # move up the list
404 self.config.SetPath("/ModelMRU")
405 self.modelFileHistory.Save(self.config)
408 profile.putPreference('lastFile', path)
410 self.scene.loadFiles(filelist)
412 def addToModelMRU(self, file):
413 self.modelFileHistory.AddFileToHistory(file)
414 self.config.SetPath("/ModelMRU")
415 self.modelFileHistory.Save(self.config)
418 def OnProfileMRU(self, e):
419 fileNum = e.GetId() - self.ID_MRU_PROFILE1
420 path = self.profileFileHistory.GetHistoryFile(fileNum)
422 self.profileFileHistory.AddFileToHistory(path) # move up the list
423 self.config.SetPath("/ProfileMRU")
424 self.profileFileHistory.Save(self.config)
427 profile.loadProfile(path)
428 self.updateProfileToAllControls()
430 def addToProfileMRU(self, file):
431 self.profileFileHistory.AddFileToHistory(file)
432 self.config.SetPath("/ProfileMRU")
433 self.profileFileHistory.Save(self.config)
436 def updateProfileToAllControls(self):
437 self.scene.updateProfileToControls()
438 self.normalSettingsPanel.updateProfileToControls()
439 self.simpleSettingsPanel.updateProfileToControls()
441 def reloadSettingPanels(self):
442 self.leftSizer.Detach(self.simpleSettingsPanel)
443 self.leftSizer.Detach(self.normalSettingsPanel)
444 self.simpleSettingsPanel.Destroy()
445 self.normalSettingsPanel.Destroy()
446 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
447 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
448 self.leftSizer.Add(self.simpleSettingsPanel, 1)
449 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
450 self.updateSliceMode()
451 self.updateProfileToAllControls()
453 def updateMachineMenu(self):
454 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
455 for item in self.machineMenu.GetMenuItems():
456 self.machineMenu.RemoveItem(item)
458 #Add a menu item for each machine configuration.
459 for n in xrange(0, profile.getMachineCount()):
460 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
461 if n == int(profile.getPreferenceFloat('active_machine')):
463 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
465 self.machineMenu.AppendSeparator()
467 i = self.machineMenu.Append(-1, _("Machine settings..."))
468 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
470 #Add tools for machines.
471 self.machineMenu.AppendSeparator()
473 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
474 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
476 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
477 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
479 def OnLoadProfile(self, e):
480 dlg=wx.FileDialog(self, _("Select profile file to load"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
481 dlg.SetWildcard("ini files (*.ini)|*.ini")
482 if dlg.ShowModal() == wx.ID_OK:
483 profileFile = dlg.GetPath()
484 profile.loadProfile(profileFile)
485 self.updateProfileToAllControls()
487 # Update the Profile MRU
488 self.addToProfileMRU(profileFile)
491 def OnLoadProfileFromGcode(self, e):
492 dlg=wx.FileDialog(self, _("Select gcode file to load profile from"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
493 dlg.SetWildcard("gcode files (*%s)|*%s;*%s" % (profile.getGCodeExtension(), profile.getGCodeExtension(), profile.getGCodeExtension()[0:2]))
494 if dlg.ShowModal() == wx.ID_OK:
495 gcodeFile = dlg.GetPath()
496 f = open(gcodeFile, 'r')
499 if line.startswith(';CURA_PROFILE_STRING:'):
500 profile.setProfileFromString(line[line.find(':')+1:].strip())
501 if ';{profile_string}' not in profile.getAlterationFile('end.gcode'):
502 profile.setAlterationFile('end.gcode', profile.getAlterationFile('end.gcode') + '\n;{profile_string}')
505 self.updateProfileToAllControls()
507 wx.MessageBox(_("No profile found in GCode file.\nThis feature only works with GCode files made by Cura 12.07 or newer."), _("Profile load error"), wx.OK | wx.ICON_INFORMATION)
510 def OnSaveProfile(self, e):
511 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
512 dlg.SetWildcard("ini files (*.ini)|*.ini")
513 if dlg.ShowModal() == wx.ID_OK:
514 profileFile = dlg.GetPath()
515 if not profileFile.lower().endswith('.ini'): #hack for linux, as for some reason the .ini is not appended.
516 profileFile += '.ini'
517 profile.saveProfile(profileFile)
520 def OnResetProfile(self, e):
521 dlg = wx.MessageDialog(self, _("This will reset all profile settings to defaults.\nUnless you have saved your current profile, all settings will be lost!\nDo you really want to reset?"), _("Profile reset"), wx.YES_NO | wx.ICON_QUESTION)
522 result = dlg.ShowModal() == wx.ID_YES
525 profile.resetProfile()
526 self.updateProfileToAllControls()
528 def OnSimpleSwitch(self, e):
529 profile.putPreference('startMode', 'Simple')
530 self.updateSliceMode()
532 def OnNormalSwitch(self, e):
533 profile.putPreference('startMode', 'Normal')
534 self.updateSliceMode()
536 def OnDefaultMarlinFirmware(self, e):
537 firmwareInstall.InstallFirmware(self)
539 def OnCustomFirmware(self, e):
540 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
541 wx.MessageBox(_("Warning: Installing a custom firmware does not guarantee that you machine will function correctly, and could damage your machine."), _("Firmware update"), wx.OK | wx.ICON_EXCLAMATION)
542 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
543 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
544 if dlg.ShowModal() == wx.ID_OK:
545 filename = dlg.GetPath()
547 if not(os.path.exists(filename)):
549 #For some reason my Ubuntu 10.10 crashes here.
550 firmwareInstall.InstallFirmware(self, filename)
552 def OnFirstRunWizard(self, e):
554 configWizard.configWizard()
556 self.reloadSettingPanels()
558 def OnSelectMachine(self, index):
559 profile.setActiveMachine(index)
560 self.reloadSettingPanels()
562 def OnBedLevelWizard(self, e):
563 configWizard.bedLevelWizard()
565 def OnHeadOffsetWizard(self, e):
566 configWizard.headOffsetWizard()
568 def OnExpertOpen(self, e):
569 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
573 def OnMinecraftImport(self, e):
574 mi = minecraftImport.minecraftImportWindow(self)
578 def OnPIDDebugger(self, e):
579 debugger = pidDebugger.debuggerWindow(self)
583 def OnAutoFirmwareUpdate(self, e):
584 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
585 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
586 if dlg.ShowModal() == wx.ID_OK:
587 filename = dlg.GetPath()
589 if not(os.path.exists(filename)):
591 #For some reason my Ubuntu 10.10 crashes here.
592 installer = firmwareInstall.AutoUpdateFirmware(self, filename)
594 def onCopyProfileClipboard(self, e):
596 if not wx.TheClipboard.IsOpened():
597 wx.TheClipboard.Open()
598 clipData = wx.TextDataObject()
599 self.lastTriedClipboard = profile.getProfileString()
600 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
601 clipData.SetText(profileString)
602 wx.TheClipboard.SetData(clipData)
603 wx.TheClipboard.Close()
605 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
607 def OnCheckForUpdate(self, e):
608 newVersion = version.checkForNewerVersion()
609 if newVersion is not None:
610 if wx.MessageBox(_("A new version of Cura is available, would you like to download?"), _("New version available"), wx.YES_NO | wx.ICON_INFORMATION) == wx.YES:
611 webbrowser.open(newVersion)
613 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
615 def OnAbout(self, e):
616 aboutBox = aboutWindow.aboutWindow()
620 def OnClose(self, e):
621 profile.saveProfile(profile.getDefaultProfilePath(), True)
623 # Save the window position, size & state from the preferences file
624 profile.putPreference('window_maximized', self.IsMaximized())
625 if not self.IsMaximized() and not self.IsIconized():
626 (posx, posy) = self.GetPosition()
627 profile.putPreference('window_pos_x', posx)
628 profile.putPreference('window_pos_y', posy)
629 (width, height) = self.GetSize()
630 profile.putPreference('window_width', width)
631 profile.putPreference('window_height', height)
633 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
634 isSimple = profile.getPreference('startMode') == 'Simple'
636 self.normalSashPos = self.splitter.GetSashPosition()
637 profile.putPreference('window_normal_sash', self.normalSashPos)
639 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
641 self.scene.OnPaint = lambda e : e
642 self.scene._engine.cleanup()
648 class normalSettingsPanel(configBase.configPanelBase):
649 "Main user interface window"
650 def __init__(self, parent, callback = None):
651 super(normalSettingsPanel, self).__init__(parent, callback)
654 self.nb = wx.Notebook(self)
655 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
656 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
658 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
659 self._addSettingsToPanels('basic', left, right)
660 self.SizeLabelWidths(left, right)
662 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
663 self._addSettingsToPanels('advanced', left, right)
664 self.SizeLabelWidths(left, right)
667 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
668 self.nb.AddPage(self.pluginPanel, _("Plugins"))
671 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
672 self.alterationPanel = None
674 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
675 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
677 self.Bind(wx.EVT_SIZE, self.OnSize)
679 self.nb.SetSize(self.GetSize())
680 self.UpdateSize(self.printPanel)
681 self.UpdateSize(self.advancedPanel)
683 def _addSettingsToPanels(self, category, left, right):
684 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
688 for title in profile.getSubCategoriesFor(category):
689 n += 1 + len(profile.getSettingsForCategory(category, title))
692 configBase.TitleRow(p, _(title))
693 for s in profile.getSettingsForCategory(category, title):
694 configBase.SettingRow(p, s.getName())
696 def SizeLabelWidths(self, left, right):
697 leftWidth = self.getLabelColumnWidth(left)
698 rightWidth = self.getLabelColumnWidth(right)
699 maxWidth = max(leftWidth, rightWidth)
700 self.setLabelColumnWidth(left, maxWidth)
701 self.setLabelColumnWidth(right, maxWidth)
704 # Make the size of the Notebook control the same size as this control
705 self.nb.SetSize(self.GetSize())
707 # Propegate the OnSize() event (just in case)
710 # Perform out resize magic
711 self.UpdateSize(self.printPanel)
712 self.UpdateSize(self.advancedPanel)
714 def UpdateSize(self, configPanel):
715 sizer = configPanel.GetSizer()
719 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
722 # if width(col1) > (best_width(col1) + best_width(col1)):
723 # switch to horizontal
726 col1 = configPanel.leftPanel
727 colSize1 = col1.GetSize()
728 colBestSize1 = col1.GetBestSize()
729 col2 = configPanel.rightPanel
730 colSize2 = col2.GetSize()
731 colBestSize2 = col2.GetBestSize()
733 orientation = sizer.GetOrientation()
735 if orientation == wx.HORIZONTAL:
736 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
738 sizer = wx.BoxSizer(wx.VERTICAL)
739 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
740 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
741 configPanel.SetSizer(sizer)
747 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
749 sizer = wx.BoxSizer(wx.HORIZONTAL)
750 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
751 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
752 configPanel.SetSizer(sizer)
758 def updateProfileToControls(self):
759 super(normalSettingsPanel, self).updateProfileToControls()
760 if self.alterationPanel is not None:
761 self.alterationPanel.updateProfileToControls()
762 self.pluginPanel.updateProfileToControls()