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()
333 self.scene._scene.pushFree()
335 def onOneAtATimeSwitch(self, e):
336 profile.putPreference('oneAtATime', self.oneAtATime.IsChecked())
337 if self.oneAtATime.IsChecked() and profile.getMachineSettingFloat('extruder_head_size_height') < 1:
338 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)
339 self.scene.updateProfileToControls()
340 self.scene._scene.pushFree()
341 self.scene.sceneUpdated()
343 def OnPreferences(self, e):
344 prefDialog = preferencesDialog.preferencesDialog(self)
348 wx.CallAfter(prefDialog.Show)
350 def OnMachineSettings(self, e):
351 prefDialog = preferencesDialog.machineSettingsDialog(self)
356 def OnDropFiles(self, files):
358 self.updateProfileToAllControls()
359 self.scene.loadFiles(files)
361 def OnModelMRU(self, e):
362 fileNum = e.GetId() - self.ID_MRU_MODEL1
363 path = self.modelFileHistory.GetHistoryFile(fileNum)
365 self.modelFileHistory.AddFileToHistory(path) # move up the list
366 self.config.SetPath("/ModelMRU")
367 self.modelFileHistory.Save(self.config)
370 profile.putPreference('lastFile', path)
372 self.scene.loadFiles(filelist)
374 def addToModelMRU(self, file):
375 self.modelFileHistory.AddFileToHistory(file)
376 self.config.SetPath("/ModelMRU")
377 self.modelFileHistory.Save(self.config)
380 def OnProfileMRU(self, e):
381 fileNum = e.GetId() - self.ID_MRU_PROFILE1
382 path = self.profileFileHistory.GetHistoryFile(fileNum)
384 self.profileFileHistory.AddFileToHistory(path) # move up the list
385 self.config.SetPath("/ProfileMRU")
386 self.profileFileHistory.Save(self.config)
389 profile.loadProfile(path)
390 self.updateProfileToAllControls()
392 def addToProfileMRU(self, file):
393 self.profileFileHistory.AddFileToHistory(file)
394 self.config.SetPath("/ProfileMRU")
395 self.profileFileHistory.Save(self.config)
398 def updateProfileToAllControls(self):
399 self.scene.updateProfileToControls()
400 self.normalSettingsPanel.updateProfileToControls()
401 self.simpleSettingsPanel.updateProfileToControls()
403 def reloadSettingPanels(self):
404 self.leftSizer.Detach(self.simpleSettingsPanel)
405 self.leftSizer.Detach(self.normalSettingsPanel)
406 self.simpleSettingsPanel.Destroy()
407 self.normalSettingsPanel.Destroy()
408 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
409 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
410 self.leftSizer.Add(self.simpleSettingsPanel, 1)
411 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
412 self.updateSliceMode()
413 self.updateProfileToAllControls()
415 def updateMachineMenu(self):
416 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
417 for item in self.machineMenu.GetMenuItems():
418 self.machineMenu.RemoveItem(item)
420 #Add a menu item for each machine configuration.
421 for n in xrange(0, profile.getMachineCount()):
422 i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
423 if n == int(profile.getPreferenceFloat('active_machine')):
425 self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
427 self.machineMenu.AppendSeparator()
429 i = self.machineMenu.Append(-1, _("Machine settings..."))
430 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
432 #Add tools for machines.
433 self.machineMenu.AppendSeparator()
435 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
436 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
438 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
439 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
441 def OnLoadProfile(self, e):
442 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)
443 dlg.SetWildcard("ini files (*.ini)|*.ini")
444 if dlg.ShowModal() == wx.ID_OK:
445 profileFile = dlg.GetPath()
446 profile.loadProfile(profileFile)
447 self.updateProfileToAllControls()
449 # Update the Profile MRU
450 self.addToProfileMRU(profileFile)
453 def OnLoadProfileFromGcode(self, e):
454 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)
455 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
456 if dlg.ShowModal() == wx.ID_OK:
457 gcodeFile = dlg.GetPath()
458 f = open(gcodeFile, 'r')
461 if line.startswith(';CURA_PROFILE_STRING:'):
462 profile.setProfileFromString(line[line.find(':')+1:].strip())
465 self.updateProfileToAllControls()
467 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)
470 def OnSaveProfile(self, e):
471 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
472 dlg.SetWildcard("ini files (*.ini)|*.ini")
473 if dlg.ShowModal() == wx.ID_OK:
474 profileFile = dlg.GetPath()
475 profile.saveProfile(profileFile)
478 def OnResetProfile(self, e):
479 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)
480 result = dlg.ShowModal() == wx.ID_YES
483 profile.resetProfile()
484 self.updateProfileToAllControls()
486 def OnSimpleSwitch(self, e):
487 profile.putPreference('startMode', 'Simple')
488 self.updateSliceMode()
490 def OnNormalSwitch(self, e):
491 profile.putPreference('startMode', 'Normal')
492 self.updateSliceMode()
494 def OnDefaultMarlinFirmware(self, e):
495 firmwareInstall.InstallFirmware()
497 def OnCustomFirmware(self, e):
498 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
499 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)
500 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
501 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
502 if dlg.ShowModal() == wx.ID_OK:
503 filename = dlg.GetPath()
504 if not(os.path.exists(filename)):
506 #For some reason my Ubuntu 10.10 crashes here.
507 firmwareInstall.InstallFirmware(filename)
509 def OnFirstRunWizard(self, e):
511 configWizard.configWizard()
513 self.reloadSettingPanels()
515 def OnSelectMachine(self, index):
516 profile.setActiveMachine(index)
517 self.reloadSettingPanels()
519 def OnBedLevelWizard(self, e):
520 configWizard.bedLevelWizard()
522 def OnHeadOffsetWizard(self, e):
523 configWizard.headOffsetWizard()
525 def OnExpertOpen(self, e):
526 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
530 def OnMinecraftImport(self, e):
531 mi = minecraftImport.minecraftImportWindow(self)
535 def OnPIDDebugger(self, e):
536 debugger = pidDebugger.debuggerWindow(self)
540 def onCopyProfileClipboard(self, e):
542 if not wx.TheClipboard.IsOpened():
543 wx.TheClipboard.Open()
544 clipData = wx.TextDataObject()
545 self.lastTriedClipboard = profile.getProfileString()
546 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
547 clipData.SetText(profileString)
548 wx.TheClipboard.SetData(clipData)
549 wx.TheClipboard.Close()
551 print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
553 def OnCheckForUpdate(self, e):
554 newVersion = version.checkForNewerVersion()
555 if newVersion is not None:
556 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:
557 webbrowser.open(newVersion)
559 wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
561 def OnAbout(self, e):
562 aboutBox = aboutWindow.aboutWindow()
566 def OnClose(self, e):
567 profile.saveProfile(profile.getDefaultProfilePath(), True)
569 # Save the window position, size & state from the preferences file
570 profile.putPreference('window_maximized', self.IsMaximized())
571 if not self.IsMaximized() and not self.IsIconized():
572 (posx, posy) = self.GetPosition()
573 profile.putPreference('window_pos_x', posx)
574 profile.putPreference('window_pos_y', posy)
575 (width, height) = self.GetSize()
576 profile.putPreference('window_width', width)
577 profile.putPreference('window_height', height)
579 # Save normal sash position. If in normal mode (!simple mode), get last position of sash before saving it...
580 isSimple = profile.getPreference('startMode') == 'Simple'
582 self.normalSashPos = self.splitter.GetSashPosition()
583 profile.putPreference('window_normal_sash', self.normalSashPos)
585 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
587 self.scene.OnPaint = lambda e : e
588 self.scene._engine.cleanup()
594 class normalSettingsPanel(configBase.configPanelBase):
595 "Main user interface window"
596 def __init__(self, parent, callback = None):
597 super(normalSettingsPanel, self).__init__(parent, callback)
600 self.nb = wx.Notebook(self)
601 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
602 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
604 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
605 self._addSettingsToPanels('basic', left, right)
606 self.SizeLabelWidths(left, right)
608 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
609 self._addSettingsToPanels('advanced', left, right)
610 self.SizeLabelWidths(left, right)
613 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
614 self.nb.AddPage(self.pluginPanel, _("Plugins"))
617 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
618 self.alterationPanel = None
620 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
621 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
623 self.Bind(wx.EVT_SIZE, self.OnSize)
625 self.nb.SetSize(self.GetSize())
626 self.UpdateSize(self.printPanel)
627 self.UpdateSize(self.advancedPanel)
629 def _addSettingsToPanels(self, category, left, right):
630 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
634 for title in profile.getSubCategoriesFor(category):
635 n += 1 + len(profile.getSettingsForCategory(category, title))
638 configBase.TitleRow(p, _(title))
639 for s in profile.getSettingsForCategory(category, title):
640 configBase.SettingRow(p, s.getName())
642 def SizeLabelWidths(self, left, right):
643 leftWidth = self.getLabelColumnWidth(left)
644 rightWidth = self.getLabelColumnWidth(right)
645 maxWidth = max(leftWidth, rightWidth)
646 self.setLabelColumnWidth(left, maxWidth)
647 self.setLabelColumnWidth(right, maxWidth)
650 # Make the size of the Notebook control the same size as this control
651 self.nb.SetSize(self.GetSize())
653 # Propegate the OnSize() event (just in case)
656 # Perform out resize magic
657 self.UpdateSize(self.printPanel)
658 self.UpdateSize(self.advancedPanel)
660 def UpdateSize(self, configPanel):
661 sizer = configPanel.GetSizer()
665 # if width(col1) < best_width(col1) || width(col2) < best_width(col2):
668 # if width(col1) > (best_width(col1) + best_width(col1)):
669 # switch to horizontal
672 col1 = configPanel.leftPanel
673 colSize1 = col1.GetSize()
674 colBestSize1 = col1.GetBestSize()
675 col2 = configPanel.rightPanel
676 colSize2 = col2.GetSize()
677 colBestSize2 = col2.GetBestSize()
679 orientation = sizer.GetOrientation()
681 if orientation == wx.HORIZONTAL:
682 if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
684 sizer = wx.BoxSizer(wx.VERTICAL)
685 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
686 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
687 configPanel.SetSizer(sizer)
693 if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
695 sizer = wx.BoxSizer(wx.HORIZONTAL)
696 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
697 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
698 configPanel.SetSizer(sizer)
704 def updateProfileToControls(self):
705 super(normalSettingsPanel, self).updateProfileToControls()
706 if self.alterationPanel is not None:
707 self.alterationPanel.updateProfileToControls()
708 self.pluginPanel.updateProfileToControls()