chiark / gitweb /
plugins: Support user configuration of default values
[cura.git] / Cura / gui / app.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import sys
4 import os
5 import platform
6 import shutil
7 import glob
8 import subprocess
9 import warnings
10
11 try:
12         #Only try to import the _core to save import time
13         import wx._core
14 except ImportError:
15         import wx
16
17
18 class CuraApp(wx.App):
19         def __init__(self, files):
20                 if platform.system() == "Windows" and not 'PYCHARM_HOSTED' in os.environ:
21                         from Cura.util import profile
22                         super(CuraApp, self).__init__(redirect=True, filename=os.path.join(profile.getBasePath(), 'output_log.txt'))
23                 else:
24                         super(CuraApp, self).__init__(redirect=False)
25
26                 self.mainWindow = None
27                 self.splash = None
28                 self.loadFiles = files
29
30                 if platform.system() == "Darwin":
31                         self.Bind(wx.EVT_ACTIVATE_APP, self.OnActivate)
32
33                 if sys.platform.startswith('win'):
34                         #Check for an already running instance, if another instance is running load files in there
35                         from Cura.util import version
36                         from ctypes import windll
37                         import ctypes
38                         import socket
39                         import threading
40
41                         portNr = 0xCA00 + sum(map(ord, version.getVersion(False)))
42                         if len(files) > 0:
43                                 try:
44                                         other_hwnd = windll.user32.FindWindowA(None, ctypes.c_char_p('Cura - ' + version.getVersion()))
45                                         if other_hwnd != 0:
46                                                 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
47                                                 sock.sendto('\0'.join(files), ("127.0.0.1", portNr))
48
49                                                 windll.user32.SetForegroundWindow(other_hwnd)
50                                                 return
51                                 except:
52                                         pass
53
54                         socketListener = threading.Thread(target=self.Win32SocketListener, args=(portNr,))
55                         socketListener.daemon = True
56                         socketListener.start()
57
58                 if sys.platform.startswith('darwin'):
59                         #Do not show a splashscreen on OSX, as by Apple guidelines
60                         self.afterSplashCallback()
61                 else:
62                         from Cura.gui import splashScreen
63                         self.splash = splashScreen.splashScreen(self.afterSplashCallback)
64
65         def MacOpenFile(self, path):
66                 try:
67                         self.mainWindow.OnDropFiles([path])
68                 except Exception as e:
69                         warnings.warn("File at {p} cannot be read: {e}".format(p=path, e=str(e)))
70
71         def MacReopenApp(self, event):
72                 self.GetTopWindow().Raise()
73
74         def MacHideApp(self, event):
75                 self.GetTopWindow().Show(False)
76
77         def MacNewFile(self):
78                 pass
79
80         def MacPrintFile(self, file_path):
81                 pass
82
83         def OnActivate(self, e):
84                 if e.GetActive():
85                         self.GetTopWindow().Raise()
86                 e.Skip()
87
88         def Win32SocketListener(self, port):
89                 import socket
90                 try:
91                         sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
92                         sock.bind(("127.0.0.1", port))
93                         while True:
94                                 data, addr = sock.recvfrom(2048)
95                                 try:
96                                         wx.CallAfter(self.mainWindow.OnDropFiles, data.split('\0'))
97                                 except Exception as e:
98                                         warnings.warn("File at {p} cannot be read: {e}".format(p=data, e=str(e)))
99                 except:
100                         pass
101
102         def destroySplashScreen(self):
103                 if self.splash is not None:
104                         self.splash.Show(False)
105                         self.splash.Destroy()
106                         self.splash = None
107
108         def afterSplashCallback(self):
109                 #These imports take most of the time and thus should be done after showing the splashscreen
110                 import webbrowser
111                 from Cura.gui import mainWindow
112                 from Cura.gui import configWizard
113                 from Cura.gui import newVersionDialog
114                 from Cura.util import profile
115                 from Cura.util import resources
116                 from Cura.util import version
117
118                 resources.setupLocalization(profile.getPreference('language'))  # it's important to set up localization at very beginning to install _
119
120                 try:
121                         from distutils.version import LooseVersion
122
123                         if LooseVersion(wx.__version__) < LooseVersion('3.0'):
124                                 wx.MessageBox(_("This version of Cura requires WxPython version 3.0 or newer.\nYour current WxPython version is %s.") % wx.__version__,
125                                                           _("WxPython version is too old"), wx.OK | wx.ICON_ERROR)
126                                 return
127                 except:
128                         # distutils not found.. it can happen!
129                         # Only check the first 3 characters of the version string instead
130                         if float(wx.__version__[0:3]) < 3.0:
131                                 wx.MessageBox(_("This version of Cura requires WxPython version 3.0 or newer.\nYour current WxPython version is %s.") % wx.__version__,
132                                                           _("WxPython version is too old"), wx.OK | wx.ICON_ERROR)
133                                 return
134
135                 #If we do not have preferences yet, try to load it from a previous Cura install
136                 if profile.getMachineSetting('machine_type') == 'unknown':
137                         try:
138                                 otherCuraInstalls = profile.getAlternativeBasePaths()
139                                 for path in otherCuraInstalls[::-1]:
140                                         try:
141                                                 print 'Loading old settings from %s' % (path)
142                                                 profile.loadPreferences(os.path.join(path, 'preferences.ini'))
143                                                 profile.loadProfile(os.path.join(path, 'current_profile.ini'))
144                                                 break
145                                         except:
146                                                 import traceback
147                                                 print traceback.print_exc()
148                         except:
149                                 import traceback
150                                 print traceback.print_exc()
151
152                 #If we haven't run it before, run the configuration wizard.
153                 if profile.getMachineSetting('machine_type') == 'unknown':
154                         #Check if we need to copy our examples
155                         exampleFile = os.path.normpath(os.path.join(resources.resourceBasePath, 'example', 'Rocktopus.stl'))
156
157                         self.loadFiles = [exampleFile]
158                         self.destroySplashScreen()
159                         configWizard.ConfigWizard()
160
161                 if profile.getPreference('check_for_updates') == 'True':
162                         newVersion = version.checkForNewerVersion()
163                         if newVersion is not None:
164                                 self.destroySplashScreen()
165                                 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:
166                                         webbrowser.open(newVersion)
167                                         return
168                 if profile.getMachineSetting('machine_name') == '':
169                         return
170                 if profile.getPreference('last_run_version') != version.getVersion(False):
171                         profile.performVersionUpgrade()
172
173                 # Must happen before the main window is created, in case there are changes
174                 # that would affect it (such as machine name changes)
175                 if version.isDevVersion():
176                         profile.performVersionUpgrade()
177
178                 self.mainWindow = mainWindow.mainWindow()
179                 self.destroySplashScreen()
180                 self.SetTopWindow(self.mainWindow)
181                 self.mainWindow.Show()
182                 self.mainWindow.OnDropFiles(self.loadFiles)
183                 setFullScreenCapable(self.mainWindow)
184
185                 if profile.getPreference('last_run_version') != version.getVersion(False):
186                         profile.putPreference('last_run_version', version.getVersion(False))
187                         newVersionDialog.newVersionDialog().Show()
188
189                 # Must come after creating the main window
190                 #if version.isDevVersion():
191                         #import wx.lib.inspection
192                         # Show the WX widget inspection tool
193                         #wx.lib.inspection.InspectionTool().Show()
194
195                 if sys.platform.startswith('darwin'):
196                         wx.CallAfter(self.StupidMacOSWorkaround)
197
198         def StupidMacOSWorkaround(self):
199                 subprocess.Popen(['osascript', '-e', '''\
200                 tell application "System Events"
201                 set procName to name of first process whose unix id is %s
202                 end tell
203                 tell application procName to activate
204                 ''' % os.getpid()])
205
206 if platform.system() == "Darwin": #Mac magic. Dragons live here. THis sets full screen options.
207         try:
208                 import ctypes, objc
209                 _objc = ctypes.PyDLL(objc._objc.__file__)
210
211                 # PyObject *PyObjCObject_New(id objc_object, int flags, int retain)
212                 _objc.PyObjCObject_New.restype = ctypes.py_object
213                 _objc.PyObjCObject_New.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
214
215                 def setFullScreenCapable(frame):
216                         frameobj = _objc.PyObjCObject_New(frame.GetHandle(), 0, 1)
217
218                         NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7
219                         window = frameobj.window()
220                         newBehavior = window.collectionBehavior() | NSWindowCollectionBehaviorFullScreenPrimary
221                         window.setCollectionBehavior_(newBehavior)
222         except:
223                 def setFullScreenCapable(frame):
224                         pass
225
226 else:
227         def setFullScreenCapable(frame):
228                 pass