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
25 from Cura.util import meshLoader
27 class mainWindow(wx.Frame):
29 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
31 wx.EVT_CLOSE(self, self.OnClose)
33 # allow dropping any file, restrict later
34 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
36 # 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
37 if sys.platform.startswith('darwin'):
40 nswindow = objc.objc_object(c_void_p=self.MacGetTopLevelWindowRef())
41 view = nswindow.contentView()
42 view.registerForDraggedTypes_([Cocoa.NSFilenamesPboardType])
44 self.normalModeOnlyItems = []
46 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
47 self.config = wx.FileConfig(appName="Cura",
48 localFilename=mruFile,
49 style=wx.CONFIG_USE_LOCAL_FILE)
51 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)]
52 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
53 self.config.SetPath("/ModelMRU")
54 self.modelFileHistory.Load(self.config)
56 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)]
57 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
58 self.config.SetPath("/ProfileMRU")
59 self.profileFileHistory.Load(self.config)
61 self.menubar = wx.MenuBar()
62 self.fileMenu = wx.Menu()
63 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
64 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
65 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
66 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
67 i = self.fileMenu.Append(-1, _("Clear platform"))
68 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
70 self.fileMenu.AppendSeparator()
71 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
72 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnPrintButton(1), i)
73 i = self.fileMenu.Append(-1, _("Save GCode..."))
74 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
75 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
76 self.Bind(wx.EVT_MENU, lambda e: self.scene._showEngineLog(), i)
78 self.fileMenu.AppendSeparator()
79 i = self.fileMenu.Append(-1, _("Open Profile..."))
80 self.normalModeOnlyItems.append(i)
81 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
82 i = self.fileMenu.Append(-1, _("Save Profile..."))
83 self.normalModeOnlyItems.append(i)
84 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
85 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
86 self.normalModeOnlyItems.append(i)
87 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
88 self.fileMenu.AppendSeparator()
89 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
90 self.normalModeOnlyItems.append(i)
91 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
93 self.fileMenu.AppendSeparator()
94 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
95 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
96 i = self.fileMenu.Append(-1, _("Machine settings..."))
97 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
98 self.fileMenu.AppendSeparator()
101 modelHistoryMenu = wx.Menu()
102 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
103 self.modelFileHistory.UseMenu(modelHistoryMenu)
104 self.modelFileHistory.AddFilesToMenu()
105 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
108 profileHistoryMenu = wx.Menu()
109 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
110 self.profileFileHistory.UseMenu(profileHistoryMenu)
111 self.profileFileHistory.AddFilesToMenu()
112 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
114 self.fileMenu.AppendSeparator()
115 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
116 self.Bind(wx.EVT_MENU, self.OnQuit, i)
117 self.menubar.Append(self.fileMenu, '&' + _("File"))
119 toolsMenu = wx.Menu()
120 #i = toolsMenu.Append(-1, 'Batch run...')
121 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
122 #self.normalModeOnlyItems.append(i)
124 if minecraftImport.hasMinecraft():
125 i = toolsMenu.Append(-1, _("Minecraft map import..."))
126 self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
128 if version.isDevVersion():
129 i = toolsMenu.Append(-1, _("PID Debugger..."))
130 self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
132 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
133 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
135 toolsMenu.AppendSeparator()
136 self.allAtOnceItem = toolsMenu.Append(-1, _("Print all at once"), kind=wx.ITEM_RADIO)
137 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.allAtOnceItem)
138 self.oneAtATime = toolsMenu.Append(-1, _("Print one at a time"), kind=wx.ITEM_RADIO)
139 self.Bind(wx.EVT_MENU, self.onOneAtATimeSwitch, self.oneAtATime)
140 if profile.getPreference('oneAtATime') == 'True':
141 self.oneAtATime.Check(True)
143 self.allAtOnceItem.Check(True)
145 self.menubar.Append(toolsMenu, _("Tools"))
147 #Machine menu for machine configuration/tooling
148 self.machineMenu = wx.Menu()
149 self.updateMachineMenu()
151 self.menubar.Append(self.machineMenu, _("Machine"))
153 expertMenu = wx.Menu()
154 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
155 self.switchToQuickprintMenuItem = i
156 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
158 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
159 self.switchToNormalMenuItem = i
160 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
161 expertMenu.AppendSeparator()
163 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
164 self.normalModeOnlyItems.append(i)
165 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
166 expertMenu.AppendSeparator()
167 i = expertMenu.Append(-1, _("Run first run wizard..."))
168 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
169 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
170 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
171 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
172 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
174 self.menubar.Append(expertMenu, _("Expert"))
177 i = helpMenu.Append(-1, _("Online documentation..."))
178 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
179 i = helpMenu.Append(-1, _("Report a problem..."))
180 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
181 i = helpMenu.Append(-1, _("Check for update..."))
182 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
183 i = helpMenu.Append(-1, _("Open YouMagine website..."))
184 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
185 i = helpMenu.Append(-1, _("About Cura..."))
186 self.Bind(wx.EVT_MENU, self.OnAbout, i)
187 self.menubar.Append(helpMenu, _("Help"))
188 self.SetMenuBar(self.menubar)
190 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
191 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
192 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
193 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
196 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
197 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
199 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
200 self.leftSizer.Add(self.simpleSettingsPanel, 1)
201 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
202 self.leftPane.SetSizer(self.leftSizer)
205 self.scene = sceneView.SceneView(self.rightPane)
207 #Main sizer, to position the preview window, buttons and tab control
208 sizer = wx.BoxSizer()
209 self.rightPane.SetSizer(sizer)
210 sizer.Add(self.scene, 1, flag=wx.EXPAND)
213 sizer = wx.BoxSizer(wx.VERTICAL)
215 sizer.Add(self.splitter, 1, wx.EXPAND)
219 self.updateProfileToAllControls()
221 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
223 self.simpleSettingsPanel.Show(False)
224 self.normalSettingsPanel.Show(False)
226 # Set default window size & position
227 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
230 #Timer set; used to check if profile is on the clipboard
231 self.timer = wx.Timer(self)
232 self.Bind(wx.EVT_TIMER, self.onTimer)
233 self.timer.Start(1000)
234 self.lastTriedClipboard = profile.getProfileString()
236 # Restore the window position, size & state from the preferences file
238 if profile.getPreference('window_maximized') == 'True':
241 posx = int(profile.getPreference('window_pos_x'))
242 posy = int(profile.getPreference('window_pos_y'))
243 width = int(profile.getPreference('window_width'))
244 height = int(profile.getPreference('window_height'))
245 if posx > 0 or posy > 0:
246 self.SetPosition((posx,posy))
247 if width > 0 and height > 0:
248 self.SetSize((width,height))
250 self.normalSashPos = int(profile.getPreference('window_normal_sash'))
252 self.normalSashPos = 0
254 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
255 self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
257 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
259 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
261 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
263 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
264 self.SetSize((800,600))
267 self.updateSliceMode()
268 self.scene.SetFocus()
270 def onTimer(self, e):
271 #Check if there is something in the clipboard
274 if not wx.TheClipboard.IsOpened():
275 if not wx.TheClipboard.Open():
277 do = wx.TextDataObject()
278 if wx.TheClipboard.GetData(do):
279 profileString = do.GetText()
280 wx.TheClipboard.Close()
282 startTag = "CURA_PROFILE_STRING:"
283 if startTag in profileString:
284 #print "Found correct syntax on clipboard"
285 profileString = profileString.replace("\n","").strip()
286 profileString = profileString[profileString.find(startTag)+len(startTag):]
287 if profileString != self.lastTriedClipboard:
289 self.lastTriedClipboard = profileString
290 profile.setProfileFromString(profileString)
291 self.scene.notification.message("Loaded new profile from clipboard.")
292 self.updateProfileToAllControls()
294 print "Unable to read from clipboard"
297 def updateSliceMode(self):
298 isSimple = profile.getPreference('startMode') == 'Simple'
300 self.normalSettingsPanel.Show(not isSimple)
301 self.simpleSettingsPanel.Show(isSimple)
302 self.leftPane.Layout()
304 for i in self.normalModeOnlyItems:
305 i.Enable(not isSimple)
307 self.switchToQuickprintMenuItem.Check()
309 self.switchToNormalMenuItem.Check()
311 # Set splitter sash position & size
313 # Save normal mode sash
314 self.normalSashPos = self.splitter.GetSashPosition()
316 # Change location of sash to width of quick mode pane
317 (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
318 self.splitter.SetSashPosition(width, True)
321 self.splitter.SetSashSize(0)
323 self.splitter.SetSashPosition(self.normalSashPos, True)
325 self.splitter.SetSashSize(4)
326 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
327 if profile.getMachineSetting('machine_type') == 'ultimaker2':
328 self.bedLevelWizardMenuItem.Enable(False)
329 self.headOffsetWizardMenuItem.Enable(False)
330 if int(profile.getMachineSetting('extruder_amount')) < 2:
331 self.headOffsetWizardMenuItem.Enable(False)
332 self.scene.updateProfileToControls()
334 def onOneAtATimeSwitch(self, e):
335 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
336 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
337 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)
338 self.scene.updateProfileToControls()
339 self.scene._scene.pushFree()
340 self.scene.sceneUpdated()
342 def OnPreferences(self, e):
343 prefDialog = preferencesDialog.preferencesDialog(self)
347 wx.CallAfter(prefDialog.Show)
349 def OnMachineSettings(self, e):
350 prefDialog = preferencesDialog.machineSettingsDialog(self)
355 def OnDropFiles(self, files):
357 self.updateProfileToAllControls()
358 self.scene.loadFiles(files)
360 def OnModelMRU(self, e):
361 fileNum = e.GetId() - self.ID_MRU_MODEL1
362 path = self.modelFileHistory.GetHistoryFile(fileNum)
364 self.modelFileHistory.AddFileToHistory(path) # move up the list
365 self.config.SetPath("/ModelMRU")
366 self.modelFileHistory.Save(self.config)
369 profile.putPreference('lastFile', path)
371 self.scene.loadFiles(filelist)
373 def addToModelMRU(self, file):
374 self.modelFileHistory.AddFileToHistory(file)
375 self.config.SetPath("/ModelMRU")
376 self.modelFileHistory.Save(self.config)
379 def OnProfileMRU(self, e):
380 fileNum = e.GetId() - self.ID_MRU_PROFILE1
381 path = self.profileFileHistory.GetHistoryFile(fileNum)
383 self.profileFileHistory.AddFileToHistory(path) # move up the list
384 self.config.SetPath("/ProfileMRU")
385 self.profileFileHistory.Save(self.config)
388 profile.loadProfile(path)
389 self.updateProfileToAllControls()
391 def addToProfileMRU(self, file):
392 self.profileFileHistory.AddFileToHistory(file)
393 self.config.SetPath("/ProfileMRU")
394 self.profileFileHistory.Save(self.config)
397 def updateProfileToAllControls(self):
398 self.scene.updateProfileToControls()
399 self.normalSettingsPanel.updateProfileToControls()
400 self.simpleSettingsPanel.updateProfileToControls()
402 def reloadSettingPanels(self):
403 self.leftSizer.Detach(self.simpleSettingsPanel)
404 self.leftSizer.Detach(self.normalSettingsPanel)
405 self.simpleSettingsPanel.Destroy()
406 self.normalSettingsPanel.Destroy()
407 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
408 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
409 self.leftSizer.Add(self.simpleSettingsPanel, 1)
410 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
411 self.updateSliceMode()
412 self.updateProfileToAllControls()
414 def updateMachineMenu(self):
415 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
416 for item in self.machineMenu.GetMenuItems():
417 self.machineMenu.RemoveItem(item)
419 #Add a menu item for each machine configuration.
420 for n in xrange(0, profile.getMachineCount()):
421 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
422 if n == int(profile.getPreferenceFloat('active_machine')):
424 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
426 self.machineMenu.AppendSeparator()
428 i = self.machineMenu.Append(-1, _("Machine settings..."))
429 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
431 #Add tools for machines.
432 self.machineMenu.AppendSeparator()
434 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
435 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
437 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
438 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
440 def OnLoadProfile(self, e):
441 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)
442 dlg.SetWildcard("ini files (*.ini)|*.ini")
443 if dlg.ShowModal() == wx.ID_OK:
444 profileFile = dlg.GetPath()
445 profile.loadProfile(profileFile)
446 self.updateProfileToAllControls()
448 # Update the Profile MRU
449 self.addToProfileMRU(profileFile)
452 def OnLoadProfileFromGcode(self, e):
453 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)
454 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
455 if dlg.ShowModal() == wx.ID_OK:
456 gcodeFile = dlg.GetPath()
457 f = open(gcodeFile, 'r')
460 if line.startswith(';CURA_PROFILE_STRING:'):
461 profile.setProfileFromString(line[line.find(':')+1:].strip())
464 self.updateProfileToAllControls()
466 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)
469 def OnSaveProfile(self, e):
470 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
471 dlg.SetWildcard("ini files (*.ini)|*.ini")
472 if dlg.ShowModal() == wx.ID_OK:
473 profileFile = dlg.GetPath()
474 profile.saveProfile(profileFile)
477 def OnResetProfile(self, e):
478 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)
479 result = dlg.ShowModal() == wx.ID_YES
482 profile.resetProfile()
483 self.updateProfileToAllControls()
485 def OnSimpleSwitch(self, e):
486 profile.putPreference('startMode', 'Simple')
487 self.updateSliceMode()
489 def OnNormalSwitch(self, e):
490 profile.putPreference('startMode', 'Normal')
491 self.updateSliceMode()
493 def OnDefaultMarlinFirmware(self, e):
494 firmwareInstall.InstallFirmware()
496 def OnCustomFirmware(self, e):
497 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
498 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)
499 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
500 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
501 if dlg.ShowModal() == wx.ID_OK:
502 filename = dlg.GetPath()
503 if not(os.path.exists(filename)):
505 #For some reason my Ubuntu 10.10 crashes here.
506 firmwareInstall.InstallFirmware(filename)
508 def OnFirstRunWizard(self, e):
510 configWizard.configWizard()
512 self.reloadSettingPanels()
514 def OnSelectMachine(self, index):
515 profile.setActiveMachine(index)
516 self.reloadSettingPanels()
518 def OnBedLevelWizard(self, e):
519 configWizard.bedLevelWizard()
521 def OnHeadOffsetWizard(self, e):
522 configWizard.headOffsetWizard()
524 def OnExpertOpen(self, e):
525 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
529 def OnMinecraftImport(self, e):
530 mi = minecraftImport.minecraftImportWindow(self)
534 def OnPIDDebugger(self, e):
535 debugger = pidDebugger.debuggerWindow(self)
539 def onCopyProfileClipboard(self, e):
541 if not wx.TheClipboard.IsOpened():
542 wx.TheClipboard.Open()
543 clipData = wx.TextDataObject()
544 self.lastTriedClipboard = profile.getProfileString()
545 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
546 clipData.SetText(profileString)
547 wx.TheClipboard.SetData(clipData)
548 wx.TheClipboard.Close()
550 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
552 def OnCheckForUpdate(self, e):
553 newVersion = version.checkForNewerVersion()
554 if newVersion is not None:
555 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:
556 webbrowser.open(newVersion)
558 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
560 def OnAbout(self, e):
561 aboutBox = aboutWindow.aboutWindow()
565 def OnClose(self, e):
566 profile.saveProfile(profile.getDefaultProfilePath(), True)
568 # Save the window position, size & state from the preferences file
569 profile.putPreference('window_maximized', self.IsMaximized())
570 if not self.IsMaximized() and not self.IsIconized():
571 (posx, posy) = self.GetPosition()
572 profile.putPreference('window_pos_x', posx)
573 profile.putPreference('window_pos_y', posy)
574 (width, height) = self.GetSize()
575 profile.putPreference('window_width', width)
576 profile.putPreference('window_height', height)
578 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
579 isSimple = profile.getPreference('startMode') == 'Simple'
581 self.normalSashPos = self.splitter.GetSashPosition()
582 profile.putPreference('window_normal_sash', self.normalSashPos)
584 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
586 self.scene.OnPaint = lambda e : e
587 self.scene._engine.cleanup()
593 class normalSettingsPanel(configBase.configPanelBase):
594 "Main user interface window"
595 def __init__(self, parent, callback = None):
596 super(normalSettingsPanel, self).__init__(parent, callback)
599 self.nb = wx.Notebook(self)
600 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
601 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
603 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
604 self._addSettingsToPanels('basic', left, right)
605 self.SizeLabelWidths(left, right)
607 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
608 self._addSettingsToPanels('advanced', left, right)
609 self.SizeLabelWidths(left, right)
612 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
613 self.nb.AddPage(self.pluginPanel, _("Plugins"))
616 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
617 self.alterationPanel = None
619 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
620 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
622 self.Bind(wx.EVT_SIZE, self.OnSize)
624 self.nb.SetSize(self.GetSize())
625 self.UpdateSize(self.printPanel)
626 self.UpdateSize(self.advancedPanel)
628 def _addSettingsToPanels(self, category, left, right):
629 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
633 for title in profile.getSubCategoriesFor(category):
634 n += 1 + len(profile.getSettingsForCategory(category, title))
637 configBase.TitleRow(p, _(title))
638 for s in profile.getSettingsForCategory(category, title):
639 configBase.SettingRow(p, s.getName())
641 def SizeLabelWidths(self, left, right):
642 leftWidth = self.getLabelColumnWidth(left)
643 rightWidth = self.getLabelColumnWidth(right)
644 maxWidth = max(leftWidth, rightWidth)
645 self.setLabelColumnWidth(left, maxWidth)
646 self.setLabelColumnWidth(right, maxWidth)
649 # Make the size of the Notebook control the same size as this control
650 self.nb.SetSize(self.GetSize())
652 # Propegate the OnSize() event (just in case)
655 # Perform out resize magic
656 self.UpdateSize(self.printPanel)
657 self.UpdateSize(self.advancedPanel)
659 def UpdateSize(self, configPanel):
660 sizer = configPanel.GetSizer()
664 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
667 # if width(col1) > (best_width(col1) + best_width(col1)):
668 # switch to horizontal
671 col1 = configPanel.leftPanel
672 colSize1 = col1.GetSize()
673 colBestSize1 = col1.GetBestSize()
674 col2 = configPanel.rightPanel
675 colSize2 = col2.GetSize()
676 colBestSize2 = col2.GetBestSize()
678 orientation = sizer.GetOrientation()
680 if orientation == wx.HORIZONTAL:
681 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
683 sizer = wx.BoxSizer(wx.VERTICAL)
684 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
685 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
686 configPanel.SetSizer(sizer)
692 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
694 sizer = wx.BoxSizer(wx.HORIZONTAL)
695 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
696 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
697 configPanel.SetSizer(sizer)
703 def updateProfileToControls(self):
704 super(normalSettingsPanel, self).updateProfileToControls()
705 if self.alterationPanel is not None:
706 self.alterationPanel.updateProfileToControls()
707 self.pluginPanel.updateProfileToControls()