chiark / gitweb /
Add printWindow2, the new printerConnection based print window.
[cura.git] / Cura / gui / mainWindow.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import wx
5 import os
6 import webbrowser
7
8 from Cura.gui import configBase
9 from Cura.gui import expertConfig
10 from Cura.gui import alterationPanel
11 from Cura.gui import pluginPanel
12 from Cura.gui import preferencesDialog
13 from Cura.gui import configWizard
14 from Cura.gui import firmwareInstall
15 from Cura.gui import simpleMode
16 from Cura.gui import sceneView
17 from Cura.gui import aboutWindow
18 from Cura.gui.util import dropTarget
19 #from Cura.gui.tools import batchRun
20 from Cura.gui.tools import pidDebugger
21 from Cura.gui.tools import minecraftImport
22 from Cura.util import profile
23 from Cura.util import version
24 from Cura.util import meshLoader
25
26 class mainWindow(wx.Frame):
27         def __init__(self):
28                 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
29
30                 wx.EVT_CLOSE(self, self.OnClose)
31
32                 # allow dropping any file, restrict later
33                 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
34
35                 self.normalModeOnlyItems = []
36
37                 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
38                 self.config = wx.FileConfig(appName="Cura",
39                                                 localFilename=mruFile,
40                                                 style=wx.CONFIG_USE_LOCAL_FILE)
41
42                 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)]
43                 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
44                 self.config.SetPath("/ModelMRU")
45                 self.modelFileHistory.Load(self.config)
46
47                 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)]
48                 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
49                 self.config.SetPath("/ProfileMRU")
50                 self.profileFileHistory.Load(self.config)
51
52                 self.menubar = wx.MenuBar()
53                 self.fileMenu = wx.Menu()
54                 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
55                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
56                 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
57                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
58                 i = self.fileMenu.Append(-1, _("Clear platform"))
59                 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
60
61                 self.fileMenu.AppendSeparator()
62                 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
63                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
64                 i = self.fileMenu.Append(-1, _("Save GCode..."))
65                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
66                 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
67                 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
68
69                 self.fileMenu.AppendSeparator()
70                 i = self.fileMenu.Append(-1, _("Open Profile..."))
71                 self.normalModeOnlyItems.append(i)
72                 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
73                 i = self.fileMenu.Append(-1, _("Save Profile..."))
74                 self.normalModeOnlyItems.append(i)
75                 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
76                 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
77                 self.normalModeOnlyItems.append(i)
78                 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
79                 self.fileMenu.AppendSeparator()
80                 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
81                 self.normalModeOnlyItems.append(i)
82                 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
83
84                 self.fileMenu.AppendSeparator()
85                 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
86                 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
87                 i = self.fileMenu.Append(-1, _("Machine settings..."))
88                 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
89                 self.fileMenu.AppendSeparator()
90
91                 # Model MRU list
92                 modelHistoryMenu = wx.Menu()
93                 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
94                 self.modelFileHistory.UseMenu(modelHistoryMenu)
95                 self.modelFileHistory.AddFilesToMenu()
96                 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
97
98                 # Profle MRU list
99                 profileHistoryMenu = wx.Menu()
100                 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
101                 self.profileFileHistory.UseMenu(profileHistoryMenu)
102                 self.profileFileHistory.AddFilesToMenu()
103                 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
104
105                 self.fileMenu.AppendSeparator()
106                 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
107                 self.Bind(wx.EVT_MENU, self.OnQuit, i)
108                 self.menubar.Append(self.fileMenu, '&' + _("File"))
109
110                 toolsMenu = wx.Menu()
111                 #i = toolsMenu.Append(-1, 'Batch run...')
112                 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
113                 #self.normalModeOnlyItems.append(i)
114
115                 if minecraftImport.hasMinecraft():
116                         i = toolsMenu.Append(-1, _("Minecraft map import..."))
117                         self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
118
119                 if version.isDevVersion():
120                         i = toolsMenu.Append(-1, _("PID Debugger..."))
121                         self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
122
123                 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
124                 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
125                 self.menubar.Append(toolsMenu, _("Tools"))
126
127                 #Machine menu for machine configuration/tooling
128                 self.machineMenu = wx.Menu()
129                 self.updateMachineMenu()
130
131                 self.menubar.Append(self.machineMenu, _("Machine"))
132
133                 expertMenu = wx.Menu()
134                 i = expertMenu.Append(-1, _("Switch to quickprint..."), kind=wx.ITEM_RADIO)
135                 self.switchToQuickprintMenuItem = i
136                 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
137
138                 i = expertMenu.Append(-1, _("Switch to full settings..."), kind=wx.ITEM_RADIO)
139                 self.switchToNormalMenuItem = i
140                 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
141                 expertMenu.AppendSeparator()
142
143                 i = expertMenu.Append(-1, _("Open expert settings...\tCTRL+E"))
144                 self.normalModeOnlyItems.append(i)
145                 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
146                 expertMenu.AppendSeparator()
147                 i = expertMenu.Append(-1, _("Run first run wizard..."))
148                 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
149                 self.bedLevelWizardMenuItem = expertMenu.Append(-1, _("Run bed leveling wizard..."))
150                 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, self.bedLevelWizardMenuItem)
151                 self.headOffsetWizardMenuItem = expertMenu.Append(-1, _("Run head offset wizard..."))
152                 self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, self.headOffsetWizardMenuItem)
153
154                 self.menubar.Append(expertMenu, _("Expert"))
155
156                 helpMenu = wx.Menu()
157                 i = helpMenu.Append(-1, _("Online documentation..."))
158                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
159                 i = helpMenu.Append(-1, _("Report a problem..."))
160                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
161                 i = helpMenu.Append(-1, _("Check for update..."))
162                 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
163                 i = helpMenu.Append(-1, _("Open YouMagine website..."))
164                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
165                 i = helpMenu.Append(-1, _("About Cura..."))
166                 self.Bind(wx.EVT_MENU, self.OnAbout, i)
167                 self.menubar.Append(helpMenu, _("Help"))
168                 self.SetMenuBar(self.menubar)
169
170                 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
171                 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
172                 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
173                 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
174
175                 ##Gui components##
176                 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
177                 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
178
179                 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
180                 self.leftSizer.Add(self.simpleSettingsPanel, 1)
181                 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
182                 self.leftPane.SetSizer(self.leftSizer)
183
184                 #Preview window
185                 self.scene = sceneView.SceneView(self.rightPane)
186
187                 #Main sizer, to position the preview window, buttons and tab control
188                 sizer = wx.BoxSizer()
189                 self.rightPane.SetSizer(sizer)
190                 sizer.Add(self.scene, 1, flag=wx.EXPAND)
191
192                 # Main window sizer
193                 sizer = wx.BoxSizer(wx.VERTICAL)
194                 self.SetSizer(sizer)
195                 sizer.Add(self.splitter, 1, wx.EXPAND)
196                 sizer.Layout()
197                 self.sizer = sizer
198
199                 self.updateProfileToAllControls()
200
201                 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
202
203                 self.simpleSettingsPanel.Show(False)
204                 self.normalSettingsPanel.Show(False)
205
206                 # Set default window size & position
207                 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
208                 self.Centre()
209
210                 #Timer set; used to check if profile is on the clipboard
211                 self.timer = wx.Timer(self)
212                 self.Bind(wx.EVT_TIMER, self.onTimer)
213                 self.timer.Start(1000)
214                 self.lastTriedClipboard = profile.getProfileString()
215
216                 # Restore the window position, size & state from the preferences file
217                 try:
218                         if profile.getPreference('window_maximized') == 'True':
219                                 self.Maximize(True)
220                         else:
221                                 posx = int(profile.getPreference('window_pos_x'))
222                                 posy = int(profile.getPreference('window_pos_y'))
223                                 width = int(profile.getPreference('window_width'))
224                                 height = int(profile.getPreference('window_height'))
225                                 if posx > 0 or posy > 0:
226                                         self.SetPosition((posx,posy))
227                                 if width > 0 and height > 0:
228                                         self.SetSize((width,height))
229
230                         self.normalSashPos = int(profile.getPreference('window_normal_sash'))
231                 except:
232                         self.normalSashPos = 0
233                         self.Maximize(True)
234                 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
235                         self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
236
237                 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
238
239                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
240                         self.Centre()
241                 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
242                         self.Centre()
243                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
244                         self.SetSize((800,600))
245                         self.Centre()
246
247                 self.updateSliceMode()
248
249         def onTimer(self, e):
250                 #Check if there is something in the clipboard
251                 profileString = ""
252                 try:
253                         if not wx.TheClipboard.IsOpened():
254                                 if not wx.TheClipboard.Open():
255                                         return
256                                 do = wx.TextDataObject()
257                                 if wx.TheClipboard.GetData(do):
258                                         profileString = do.GetText()
259                                 wx.TheClipboard.Close()
260
261                                 startTag = "CURA_PROFILE_STRING:"
262                                 if startTag in profileString:
263                                         #print "Found correct syntax on clipboard"
264                                         profileString = profileString.replace("\n","").strip()
265                                         profileString = profileString[profileString.find(startTag)+len(startTag):]
266                                         if profileString != self.lastTriedClipboard:
267                                                 print profileString
268                                                 self.lastTriedClipboard = profileString
269                                                 profile.setProfileFromString(profileString)
270                                                 self.scene.notification.message("Loaded new profile from clipboard.")
271                                                 self.updateProfileToAllControls()
272                 except:
273                         print "Unable to read from clipboard"
274
275
276         def updateSliceMode(self):
277                 isSimple = profile.getPreference('startMode') == 'Simple'
278
279                 self.normalSettingsPanel.Show(not isSimple)
280                 self.simpleSettingsPanel.Show(isSimple)
281                 self.leftPane.Layout()
282
283                 for i in self.normalModeOnlyItems:
284                         i.Enable(not isSimple)
285                 if isSimple:
286                         self.switchToQuickprintMenuItem.Check()
287                 else:
288                         self.switchToNormalMenuItem.Check()
289
290                 # Set splitter sash position & size
291                 if isSimple:
292                         # Save normal mode sash
293                         self.normalSashPos = self.splitter.GetSashPosition()
294
295                         # Change location of sash to width of quick mode pane
296                         (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
297                         self.splitter.SetSashPosition(width, True)
298
299                         # Disable sash
300                         self.splitter.SetSashSize(0)
301                 else:
302                         self.splitter.SetSashPosition(self.normalSashPos, True)
303                         # Enabled sash
304                         self.splitter.SetSashSize(4)
305                 self.defaultFirmwareInstallMenuItem.Enable(firmwareInstall.getDefaultFirmware() is not None)
306                 if profile.getMachineSetting('machine_type') == 'ultimaker2':
307                         self.bedLevelWizardMenuItem.Enable(False)
308                         self.headOffsetWizardMenuItem.Enable(False)
309                 if int(profile.getMachineSetting('extruder_amount')) < 2:
310                         self.headOffsetWizardMenuItem.Enable(False)
311                 self.scene.updateProfileToControls()
312
313         def OnPreferences(self, e):
314                 prefDialog = preferencesDialog.preferencesDialog(self)
315                 prefDialog.Centre()
316                 prefDialog.Show()
317                 prefDialog.Raise()
318                 wx.CallAfter(prefDialog.Show)
319
320         def OnMachineSettings(self, e):
321                 prefDialog = preferencesDialog.machineSettingsDialog(self)
322                 prefDialog.Centre()
323                 prefDialog.Show()
324                 prefDialog.Raise()
325
326         def OnDropFiles(self, files):
327                 if len(files) > 0:
328                         profile.setPluginConfig([])
329                         self.updateProfileToAllControls()
330                 self.scene.loadFiles(files)
331
332         def OnModelMRU(self, e):
333                 fileNum = e.GetId() - self.ID_MRU_MODEL1
334                 path = self.modelFileHistory.GetHistoryFile(fileNum)
335                 # Update Model MRU
336                 self.modelFileHistory.AddFileToHistory(path)  # move up the list
337                 self.config.SetPath("/ModelMRU")
338                 self.modelFileHistory.Save(self.config)
339                 self.config.Flush()
340                 # Load Model
341                 profile.putPreference('lastFile', path)
342                 filelist = [ path ]
343                 self.scene.loadFiles(filelist)
344
345         def addToModelMRU(self, file):
346                 self.modelFileHistory.AddFileToHistory(file)
347                 self.config.SetPath("/ModelMRU")
348                 self.modelFileHistory.Save(self.config)
349                 self.config.Flush()
350
351         def OnProfileMRU(self, e):
352                 fileNum = e.GetId() - self.ID_MRU_PROFILE1
353                 path = self.profileFileHistory.GetHistoryFile(fileNum)
354                 # Update Profile MRU
355                 self.profileFileHistory.AddFileToHistory(path)  # move up the list
356                 self.config.SetPath("/ProfileMRU")
357                 self.profileFileHistory.Save(self.config)
358                 self.config.Flush()
359                 # Load Profile
360                 profile.loadProfile(path)
361                 self.updateProfileToAllControls()
362
363         def addToProfileMRU(self, file):
364                 self.profileFileHistory.AddFileToHistory(file)
365                 self.config.SetPath("/ProfileMRU")
366                 self.profileFileHistory.Save(self.config)
367                 self.config.Flush()
368
369         def updateProfileToAllControls(self):
370                 self.scene.updateProfileToControls()
371                 self.normalSettingsPanel.updateProfileToControls()
372                 self.simpleSettingsPanel.updateProfileToControls()
373
374         def reloadSettingPanels(self):
375                 self.leftSizer.Detach(self.simpleSettingsPanel)
376                 self.leftSizer.Detach(self.normalSettingsPanel)
377                 self.simpleSettingsPanel.Destroy()
378                 self.normalSettingsPanel.Destroy()
379                 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
380                 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
381                 self.leftSizer.Add(self.simpleSettingsPanel, 1)
382                 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
383                 self.updateSliceMode()
384                 self.updateProfileToAllControls()
385
386         def updateMachineMenu(self):
387                 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
388                 for item in self.machineMenu.GetMenuItems():
389                         self.machineMenu.RemoveItem(item)
390
391                 #Add a menu item for each machine configuration.
392                 for n in xrange(0, profile.getMachineCount()):
393                         i = self.machineMenu.Append(n + 0x1000, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
394                         if n == int(profile.getPreferenceFloat('active_machine')):
395                                 i.Check(True)
396                         self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId() - 0x1000), i)
397
398                 self.machineMenu.AppendSeparator()
399                 i = self.machineMenu.Append(-1, _("Add new machine..."))
400                 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
401
402                 #Add tools for machines.
403                 self.machineMenu.AppendSeparator()
404
405                 self.defaultFirmwareInstallMenuItem = self.machineMenu.Append(-1, _("Install default firmware..."))
406                 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, self.defaultFirmwareInstallMenuItem)
407
408                 i = self.machineMenu.Append(-1, _("Install custom firmware..."))
409                 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
410
411         def OnLoadProfile(self, e):
412                 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)
413                 dlg.SetWildcard("ini files (*.ini)|*.ini")
414                 if dlg.ShowModal() == wx.ID_OK:
415                         profileFile = dlg.GetPath()
416                         profile.loadProfile(profileFile)
417                         self.updateProfileToAllControls()
418
419                         # Update the Profile MRU
420                         self.addToProfileMRU(profileFile)
421                 dlg.Destroy()
422
423         def OnLoadProfileFromGcode(self, e):
424                 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)
425                 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
426                 if dlg.ShowModal() == wx.ID_OK:
427                         gcodeFile = dlg.GetPath()
428                         f = open(gcodeFile, 'r')
429                         hasProfile = False
430                         for line in f:
431                                 if line.startswith(';CURA_PROFILE_STRING:'):
432                                         profile.setProfileFromString(line[line.find(':')+1:].strip())
433                                         hasProfile = True
434                         if hasProfile:
435                                 self.updateProfileToAllControls()
436                         else:
437                                 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)
438                 dlg.Destroy()
439
440         def OnSaveProfile(self, e):
441                 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
442                 dlg.SetWildcard("ini files (*.ini)|*.ini")
443                 if dlg.ShowModal() == wx.ID_OK:
444                         profileFile = dlg.GetPath()
445                         profile.saveProfile(profileFile)
446                 dlg.Destroy()
447
448         def OnResetProfile(self, e):
449                 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)
450                 result = dlg.ShowModal() == wx.ID_YES
451                 dlg.Destroy()
452                 if result:
453                         profile.resetProfile()
454                         self.updateProfileToAllControls()
455
456         def OnSimpleSwitch(self, e):
457                 profile.putPreference('startMode', 'Simple')
458                 self.updateSliceMode()
459
460         def OnNormalSwitch(self, e):
461                 profile.putPreference('startMode', 'Normal')
462                 self.updateSliceMode()
463
464         def OnDefaultMarlinFirmware(self, e):
465                 firmwareInstall.InstallFirmware()
466
467         def OnCustomFirmware(self, e):
468                 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
469                         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)
470                 dlg=wx.FileDialog(self, _("Open firmware to upload"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
471                 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
472                 if dlg.ShowModal() == wx.ID_OK:
473                         filename = dlg.GetPath()
474                         if not(os.path.exists(filename)):
475                                 return
476                         #For some reason my Ubuntu 10.10 crashes here.
477                         firmwareInstall.InstallFirmware(filename)
478
479         def OnFirstRunWizard(self, e):
480                 self.Hide()
481                 configWizard.configWizard()
482                 self.Show()
483                 self.reloadSettingPanels()
484
485         def OnAddNewMachine(self, e):
486                 self.Hide()
487                 profile.setActiveMachine(profile.getMachineCount())
488                 configWizard.configWizard(True)
489                 self.Show()
490                 self.reloadSettingPanels()
491                 self.updateMachineMenu()
492
493         def OnSelectMachine(self, index):
494                 profile.setActiveMachine(index)
495                 self.reloadSettingPanels()
496
497         def OnBedLevelWizard(self, e):
498                 configWizard.bedLevelWizard()
499
500         def OnHeadOffsetWizard(self, e):
501                 configWizard.headOffsetWizard()
502
503         def OnExpertOpen(self, e):
504                 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
505                 ecw.Centre()
506                 ecw.Show()
507
508         def OnMinecraftImport(self, e):
509                 mi = minecraftImport.minecraftImportWindow(self)
510                 mi.Centre()
511                 mi.Show(True)
512
513         def OnPIDDebugger(self, e):
514                 debugger = pidDebugger.debuggerWindow(self)
515                 debugger.Centre()
516                 debugger.Show(True)
517
518         def onCopyProfileClipboard(self, e):
519                 try:
520                         if not wx.TheClipboard.IsOpened():
521                                 wx.TheClipboard.Open()
522                                 clipData = wx.TextDataObject()
523                                 self.lastTriedClipboard = profile.getProfileString()
524                                 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
525                                 clipData.SetText(profileString)
526                                 wx.TheClipboard.SetData(clipData)
527                                 wx.TheClipboard.Close()
528                 except:
529                         print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
530
531         def OnCheckForUpdate(self, e):
532                 newVersion = version.checkForNewerVersion()
533                 if newVersion is not None:
534                         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:
535                                 webbrowser.open(newVersion)
536                 else:
537                         wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
538
539         def OnAbout(self, e):
540                 aboutBox = aboutWindow.aboutWindow()
541                 aboutBox.Centre()
542                 aboutBox.Show()
543
544         def OnClose(self, e):
545                 profile.saveProfile(profile.getDefaultProfilePath())
546
547                 # Save the window position, size & state from the preferences file
548                 profile.putPreference('window_maximized', self.IsMaximized())
549                 if not self.IsMaximized() and not self.IsIconized():
550                         (posx, posy) = self.GetPosition()
551                         profile.putPreference('window_pos_x', posx)
552                         profile.putPreference('window_pos_y', posy)
553                         (width, height) = self.GetSize()
554                         profile.putPreference('window_width', width)
555                         profile.putPreference('window_height', height)
556
557                         # Save normal sash position.  If in normal mode (!simple mode), get last position of sash before saving it...
558                         isSimple = profile.getPreference('startMode') == 'Simple'
559                         if not isSimple:
560                                 self.normalSashPos = self.splitter.GetSashPosition()
561                         profile.putPreference('window_normal_sash', self.normalSashPos)
562
563                 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
564                 print "Closing down"
565                 self.scene.OnPaint = lambda e : e
566                 self.scene._slicer.cleanup()
567                 self.Destroy()
568
569         def OnQuit(self, e):
570                 self.Close()
571
572 class normalSettingsPanel(configBase.configPanelBase):
573         "Main user interface window"
574         def __init__(self, parent, callback = None):
575                 super(normalSettingsPanel, self).__init__(parent, callback)
576
577                 #Main tabs
578                 self.nb = wx.Notebook(self)
579                 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
580                 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
581
582                 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
583                 self._addSettingsToPanels('basic', left, right)
584                 self.SizeLabelWidths(left, right)
585
586                 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
587                 self._addSettingsToPanels('advanced', left, right)
588                 self.SizeLabelWidths(left, right)
589
590                 #Plugin page
591                 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
592                 self.nb.AddPage(self.pluginPanel, _("Plugins"))
593
594                 #Alteration page
595                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
596                         self.alterationPanel = None
597                 else:
598                         self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
599                         self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
600
601                 self.Bind(wx.EVT_SIZE, self.OnSize)
602
603                 self.nb.SetSize(self.GetSize())
604                 self.UpdateSize(self.printPanel)
605                 self.UpdateSize(self.advancedPanel)
606
607         def _addSettingsToPanels(self, category, left, right):
608                 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
609
610                 p = left
611                 n = 0
612                 for title in profile.getSubCategoriesFor(category):
613                         n += 1 + len(profile.getSettingsForCategory(category, title))
614                         if n > count / 2:
615                                 p = right
616                         configBase.TitleRow(p, _(title))
617                         for s in profile.getSettingsForCategory(category, title):
618                                 configBase.SettingRow(p, s.getName())
619
620         def SizeLabelWidths(self, left, right):
621                 leftWidth = self.getLabelColumnWidth(left)
622                 rightWidth = self.getLabelColumnWidth(right)
623                 maxWidth = max(leftWidth, rightWidth)
624                 self.setLabelColumnWidth(left, maxWidth)
625                 self.setLabelColumnWidth(right, maxWidth)
626
627         def OnSize(self, e):
628                 # Make the size of the Notebook control the same size as this control
629                 self.nb.SetSize(self.GetSize())
630
631                 # Propegate the OnSize() event (just in case)
632                 e.Skip()
633
634                 # Perform out resize magic
635                 self.UpdateSize(self.printPanel)
636                 self.UpdateSize(self.advancedPanel)
637
638         def UpdateSize(self, configPanel):
639                 sizer = configPanel.GetSizer()
640
641                 # Pseudocde
642                 # if horizontal:
643                 #     if width(col1) < best_width(col1) || width(col2) < best_width(col2):
644                 #         switch to vertical
645                 # else:
646                 #     if width(col1) > (best_width(col1) + best_width(col1)):
647                 #         switch to horizontal
648                 #
649
650                 col1 = configPanel.leftPanel
651                 colSize1 = col1.GetSize()
652                 colBestSize1 = col1.GetBestSize()
653                 col2 = configPanel.rightPanel
654                 colSize2 = col2.GetSize()
655                 colBestSize2 = col2.GetBestSize()
656
657                 orientation = sizer.GetOrientation()
658
659                 if orientation == wx.HORIZONTAL:
660                         if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
661                                 configPanel.Freeze()
662                                 sizer = wx.BoxSizer(wx.VERTICAL)
663                                 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
664                                 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
665                                 configPanel.SetSizer(sizer)
666                                 #sizer.Layout()
667                                 configPanel.Layout()
668                                 self.Layout()
669                                 configPanel.Thaw()
670                 else:
671                         if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
672                                 configPanel.Freeze()
673                                 sizer = wx.BoxSizer(wx.HORIZONTAL)
674                                 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
675                                 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
676                                 configPanel.SetSizer(sizer)
677                                 #sizer.Layout()
678                                 configPanel.Layout()
679                                 self.Layout()
680                                 configPanel.Thaw()
681
682         def updateProfileToControls(self):
683                 super(normalSettingsPanel, self).updateProfileToControls()
684                 if self.alterationPanel is not None:
685                         self.alterationPanel.updateProfileToControls()
686                 self.pluginPanel.updateProfileToControls()