chiark / gitweb /
plugins: Support user configuration of default values
[cura.git] / Cura / util / resources.py
1 #coding:utf8
2 """
3 Helper module to get easy access to the path where resources are stored.
4 This is because the resource location is depended on the packaging method and OS
5 """
6 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
7
8 import os
9 import sys
10 import glob
11 import platform
12 import locale
13
14 import gettext
15 import profile
16 import ConfigParser as configparser
17
18 if sys.platform.startswith('darwin'):
19         try:
20                 #Foundation import can crash on some MacOS installs
21                 from Foundation import *
22         except:
23                 pass
24
25 if sys.platform.startswith('darwin'):
26         if hasattr(sys, 'frozen'):
27                 try:
28                         resourceBasePath = NSBundle.mainBundle().resourcePath()
29                 except:
30                         resourceBasePath = os.path.join(os.path.dirname(__file__), "../../../../../")
31         else:
32                 resourceBasePath = os.path.join(os.path.dirname(__file__), "../../resources")
33 else:
34         resourceBasePath = os.path.join(os.path.dirname(__file__), "../../resources")
35
36 def getPathForResource(dir, subdir, resource_name):
37         assert os.path.isdir(dir), "{p} is not a directory".format(p=dir)
38         path = os.path.normpath(os.path.join(dir, subdir, resource_name))
39         if not os.path.isfile(path):
40                 return None
41         return path
42
43 def getPathForImage(name):
44         return getPathForResource(resourceBasePath, 'images', name)
45
46 def getPathForMesh(name):
47         return getPathForResource(resourceBasePath, 'meshes', name)
48
49 def getPathForFirmware(name):
50         return getPathForResource(resourceBasePath, 'firmware', name)
51
52 def getDefaultMachineProfiles():
53         path = os.path.normpath(os.path.join(resourceBasePath, 'machine_profiles', '*.ini'))
54         return glob.glob(path)
55
56 def setupLocalization(selectedLanguage = None):
57         #Default to english
58         languages = ['en']
59
60         if selectedLanguage is not None:
61                 for item in getLanguageOptions():
62                         if item[1] == selectedLanguage and item[0] is not None:
63                                 languages = [item[0]]
64                                 break
65         if languages[0] == 'AUTO':
66                 languages = ['en']
67                 defaultLocale = getDefaultLocale()
68                 if defaultLocale is not None:
69                         for item in getLanguageOptions():
70                                 if item[0] == 'AUTO':
71                                         continue
72                                 if item[0] is not None and defaultLocale.startswith(item[0]):
73                                         languages = [item[0]]
74
75         locale_path = os.path.normpath(os.path.join(resourceBasePath, 'locale'))
76         translation = gettext.translation('Cura', locale_path, languages, fallback=True)
77         #translation.ugettext = lambda message: u'#' + message
78         translation.install(unicode=True)
79
80 def getLanguageOptions():
81         return [
82                 ['AUTO', 'Autodetect'],
83                 ['en', 'English'],
84                 ['de', 'Deutsch'],
85                 ['fr', 'French'],
86                 ['tr', 'Turkish'],
87                 ['ru', 'Russian'],
88                 # ['ko', 'Korean'],
89                 # ['zh', 'Chinese'],
90                 # ['nl', 'Nederlands'],
91                 # ['es', 'Spanish'],
92                 # ['po', 'Polish']
93         ]
94
95 def getDefaultLocale():
96         defaultLocale = None
97
98         # On Windows, we look for the actual UI language, as someone could have
99         # an english windows but use a non-english locale.
100         if platform.system() == "Windows":
101                 try:
102                         import ctypes
103
104                         windll = ctypes.windll.kernel32
105                         defaultLocale = locale.windows_locale[windll.GetUserDefaultUILanguage()]
106                 except:
107                         pass
108
109         if defaultLocale is None:
110                 try:
111                         defaultLocale = locale.getdefaultlocale()[0]
112                 except:
113                         pass
114
115         return defaultLocale
116
117 class ProfileIni(object):
118         @staticmethod
119         def str2bool(str):
120                 return False if str is None else str.lower() in ['true', 'yes', '1', 'y', 't']
121
122         def __init__(self, ini_file):
123                 self.ini = ini_file
124                 self.path = os.path.split(self.ini)[0]
125                 self.base_name = os.path.splitext(os.path.basename(self.ini))[0]
126                 if self.base_name == 'profile' or self.base_name == 'material':
127                         self.base_name = os.path.basename(self.path)
128                 # Name to show in the UI
129                 self.name = self._getProfileInfo(ini_file, 'name')
130                 if self.name is None:
131                         self.name = self.base_name
132                 self.full_name = self._getProfileInfo(ini_file, 'full_name')
133                 if self.full_name is None:
134                         self.full_name = self.name
135                 # URL for the profile
136                 self.url = self._getProfileInfo(ini_file, 'url')
137                 # Finds the full path to the real profile_file
138                 self.profile_file = self._findProfileFile()
139                 # default = The default profile to select
140                 self.default = self.str2bool(self._getProfileInfo(self.ini, 'default'))
141                 # disabled = do not make available in the UI
142                 self.disabled = self.str2bool(self._getProfileInfo(self.ini, 'disabled'))
143                 # always_visible = Always display in the UI even if it's the only available option
144                 if self._getProfileInfo(self.ini, 'always_visible') is None:
145                         self.always_visible = True
146                 else:
147                         self.always_visible = self.str2bool(self._getProfileInfo(self.ini, 'always_visible'))
148                 try:
149                         self.order = int(self._getProfileInfo(self.ini, 'order'))
150                 except:
151                         self.order = 999
152
153         def _findProfileFile(self):
154                 profile_file = self._getProfileInfo(self.ini, 'profile_file')
155                 if profile_file is None:
156                         return self.ini
157                 else:
158                         if os.path.exists(profile_file):
159                                 return profile_file
160                         elif os.path.exists(os.path.join(self.path, profile_file)):
161                                 return os.path.join(self.path, profile_file)
162                         else:
163                                 return self.ini
164
165         def _getProfileInfo(self, ini_file, key):
166                 cp = configparser.ConfigParser()
167                 cp.read(ini_file)
168                 disabled = False
169                 if cp.has_option('info', key):
170                         return cp.get('info', key)
171                 return None
172
173         def _isInList(self, list):
174                 """ Check if an option with the same base name already exists in the list """
175                 for ini in list:
176                         if ini.base_name == self.base_name:
177                                 return True
178                 return False
179
180         def getProfileDict(self):
181                 profile_dict = {}
182                 cp = configparser.ConfigParser()
183                 cp.read(self.profile_file)
184                 for setting in profile.settingsList:
185                         section = 'profile' if setting.isProfile() else 'alterations'
186                         if setting.isProfile() or setting.isAlteration():
187                                 if cp.has_option(section, setting.getName()):
188                                         profile_dict[setting.getName()] = cp.get(section, setting.getName())
189
190                 return profile_dict
191
192         def __cmp__(self, cmp):
193                 if self.order < cmp.order:
194                         return -1
195                 elif self.order == cmp.order:
196                         return 0
197                 else:
198                         return 1
199
200         def __str__ (self):
201                 return "%s%s: %d" % (self.name, "(disabled)" if self.disabled else "", self.order)
202
203         def __repr__ (self):
204                 return str(self)
205
206 class PrintMaterial(ProfileIni):
207         def __init__(self, ini_file):
208                 super(PrintMaterial, self).__init__(ini_file)
209
210                 self.profiles = []
211                 self.options = []
212                 self.types = []
213                 types = self._getProfileInfo(self.ini, 'material_types')
214
215                 if types != None:
216                         for type in types.split('|'):
217                                 self.types.append(type.strip())
218                 # Comment for the profile
219                 self.description = self._getProfileInfo(ini_file, 'description')
220
221                 self.parseDirectory(self.path)
222
223         def parseDirectory(self, path):
224                 profile_files = sorted(glob.glob(os.path.join(path, '*/profile.ini')))
225                 if len(profile_files) > 0:
226                         for profile_file in profile_files:
227                                 profile_ini = ProfileIni(profile_file)
228                                 if not profile_ini._isInList(self.profiles):
229                                         self.profiles.append(profile_ini)
230
231                 option_files = sorted(glob.glob(os.path.join(path, 'option_*.ini')))
232                 for option_file in option_files:
233                         option = ProfileIni(option_file)
234                         if not option._isInList(self.options):
235                                 self.options.append(option)
236
237                 self.profiles.sort()
238                 self.options.sort()
239
240         def addGlobalOptions(self, global_options):
241                 for option in global_options:
242                         if not option._isInList(self.options):
243                                 self.options.append(option)
244                 self.options.sort()
245
246         def __str__ (self):
247                 return "%s%s: %d - Profiles : %s - Options - %s\n" % (self.name, "(disabled)" if self.disabled else "",
248                                                                                                                           self.order, self.profiles, self.options)
249
250
251 def getSimpleModeMaterials():
252         machine_type = profile.getMachineSetting('machine_type')
253         paths = []
254         paths.append(os.path.normpath(os.path.expanduser(os.path.join('~', '.Cura', 'quickprint', machine_type))))
255         paths.append(os.path.normpath(os.path.expanduser(os.path.join('~', '.Cura', 'quickprint'))))
256         paths.append(os.path.normpath(os.path.join(resourceBasePath, 'quickprint', machine_type)))
257         paths.append(os.path.normpath(os.path.join(resourceBasePath, 'quickprint')))
258
259         materials = []
260         global_options = []
261         for path in paths:
262                 if os.path.isdir(path):
263                         option_files = sorted(glob.glob(os.path.join(path, 'option_*.ini')))
264                         for option_file in option_files:
265                                 option = ProfileIni(option_file)
266                                 if not option._isInList(global_options):
267                                         global_options.append(option)
268
269                         material_files = sorted(glob.glob(os.path.join(path, '*/material.ini')))
270                         if len(material_files) > 0:
271                                 for material_file in material_files:
272                                         material = PrintMaterial(material_file)
273                                         if not material._isInList(materials):
274                                                 materials.append(material)
275                                         else:
276                                                 for ini in materials:
277                                                         if ini.base_name == material.base_name:
278                                                                 ini.parseDirectory(os.path.split(material_file)[0])
279                                                                 break
280
281         materials.sort()
282         for material in materials:
283                 material.addGlobalOptions(global_options)
284
285         #print "Materials found for %s :\n%s" % (machine_type, materials)
286         return materials