chiark / gitweb /
plugins: Support user configuration of default values
[cura.git] / Cura / util / pluginInfo.py
1 """
2 The plugin module contains information about the plugins found for Cura.
3 It keeps track of a list of installed plugins and the information contained within.
4 """
5 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
6
7 import os
8 import sys
9 import traceback
10 import platform
11 import re
12 import tempfile
13 import ConfigParser
14 import cPickle as pickle
15
16 from Cura.util import profile
17 from Cura.util import resources
18
19 _pluginList = None
20
21 class pluginInfo(object):
22         """
23         Plugin information object. Used to keep track of information about the available plugins in this installation of Cura.
24         Each plugin as meta-data associated with it which can be retrieved from this class.
25         """
26         def __init__(self, dirname, filename):
27                 self._dirname = dirname
28                 self._filename = filename
29                 self._name = os.path.splitext(os.path.basename(filename))[0]
30                 self._type = 'unknown'
31                 self._info = ''
32                 self._params = []
33                 with open(os.path.join(dirname, filename), "r") as f:
34                         for line in f:
35                                 line = line.strip()
36                                 if not line.startswith('#'):
37                                         break
38                                 line = line[1:].split(':', 1)
39                                 if len(line) != 2:
40                                         continue
41                                 if line[0].upper() == 'NAME':
42                                         self._name = line[1].strip()
43                                 elif line[0].upper() == 'INFO':
44                                         self._info = line[1].strip()
45                                 elif line[0].upper() == 'TYPE':
46                                         self._type = line[1].strip()
47                                 elif line[0].upper() == 'DEPEND':
48                                         pass
49                                 elif line[0].upper() == 'PARAM':
50                                         m = re.match('([a-zA-Z][a-zA-Z0-9_]*)\(([a-zA-Z_]*)(?::([^\)]*))?\) +(.*)', line[1].strip())
51                                         if m is not None:
52                                                 self._params.append({'name': m.group(1), 'type': m.group(2), 'default': m.group(3), 'description': m.group(4)})
53                                 # else:
54                                 #       print "Unknown item in plugin meta data: %s %s" % (line[0], line[1])
55
56                 self._readSettings(os.path.join(profile.getBasePath()))
57                 for plugindir in getPluginBasePaths():
58                         self._readSettings(plugindir)
59
60         def _readSettings(self, indir):
61                 settings_file = os.path.join(indir, 'plugin_defaults.ini')
62                 plugin_basename = re.sub(r'\.pyc?','',os.path.basename(self._filename))
63                 if not os.path.exists(settings_file):
64                         return
65                 cfg = ConfigParser.ConfigParser()
66                 try:
67                         cfg.read(settings_file)
68                 except:
69                         traceback.print_exc()
70                 if not cfg.has_section(plugin_basename):
71                         return
72                 for param in self._params:
73                         name = param['name']
74                         if cfg.has_option(plugin_basename, name):
75                                 param['default'] = cfg.get(plugin_basename, name)
76
77         def getFilename(self):
78                 return self._filename
79
80         def getFullFilename(self):
81                 return os.path.join(self._dirname, self._filename)
82
83         def getType(self):
84                 return self._type
85
86         def getName(self):
87                 return self._name
88
89         def getInfo(self):
90                 return self._info
91
92         def getParams(self):
93                 return self._params
94
95 def getPostProcessPluginConfig():
96         try:
97                 return pickle.loads(str(profile.getProfileSetting('plugin_config')))
98         except:
99                 return []
100
101 def setPostProcessPluginConfig(config):
102         profile.putProfileSetting('plugin_config', pickle.dumps(config))
103
104 def overridePostProcessPluginConfig(config):
105         profile.setTempOverride('plugin_config', pickle.dumps(config))
106
107 def getPluginBasePaths():
108         ret = []
109         if platform.system() != "Windows":
110                 ret.append(os.path.expanduser('~/.cura/plugins/'))
111         if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
112                 ret.append(os.path.normpath(os.path.join(resources.resourceBasePath, "plugins")))
113         else:
114                 ret.append(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'plugins')))
115         return ret
116
117 def getPluginList(pluginType):
118         global _pluginList
119         if _pluginList is None:
120                 _pluginList = []
121                 for basePath in getPluginBasePaths():
122                         if os.path.isdir(basePath):
123                                 for filename in os.listdir(basePath):
124                                         if filename.startswith('.'):
125                                                 continue
126                                         if filename.startswith('_'):
127                                                 continue
128                                         if os.path.isdir(os.path.join(basePath, filename)):
129                                                 if os.path.exists(os.path.join(basePath, filename, 'script.py')):
130                                                         _pluginList.append(pluginInfo(basePath, os.path.join(filename, 'script.py')))
131                                         elif filename.endswith('.py'):
132                                                 _pluginList.append(pluginInfo(basePath, filename))
133         ret = []
134         for plugin in _pluginList:
135                 if plugin.getType() == pluginType:
136                         ret.append(plugin)
137         return ret
138
139 def runPostProcessingPlugins(engineResult, pluginConfigList):
140         pluginList = getPluginList('postprocess')
141
142         tempfilename = None
143         for pluginConfig in pluginConfigList:
144                 plugin = None
145                 for pluginTest in pluginList:
146                         if pluginTest.getFilename() == pluginConfig['filename']:
147                                 plugin = pluginTest
148                 if plugin is None:
149                         continue
150
151                 pythonFile = plugin.getFullFilename()
152
153                 if tempfilename is None:
154                         f = tempfile.NamedTemporaryFile(prefix='CuraPluginTemp', delete=False)
155                         tempfilename = f.name
156                         gcode = engineResult.getGCode()
157                         while True:
158                                 data = gcode.read(16 * 1024)
159                                 if len(data) == 0:
160                                         break
161                                 f.write(data)
162                         f.close()
163                         del gcode
164
165                 locals = {'filename': tempfilename}
166                 for param in plugin.getParams():
167                         value = param['default']
168                         if param['name'] in pluginConfig['params']:
169                                 value = pluginConfig['params'][param['name']]
170
171                         if param['type'] == 'float':
172                                 try:
173                                         value = float(value)
174                                 except:
175                                         value = float(param['default'])
176
177                         locals[param['name']] = value
178                 try:
179                         execfile(pythonFile, locals)
180                 except:
181                         traceback.print_exc()
182                         locationInfo = traceback.extract_tb(sys.exc_info()[2])[-1]
183                         return "%s: '%s' @ %s:%s:%d" % (str(sys.exc_info()[0].__name__), str(sys.exc_info()[1]), os.path.basename(locationInfo[0]), locationInfo[2], locationInfo[1])
184         if tempfilename is not None:
185                 f = open(tempfilename, "r")
186                 engineResult.setGCode("")
187                 import gc
188                 gc.collect()
189                 data = f.read(4096)
190                 while len(data) > 0:
191                         engineResult._gcodeData.write(data)
192                         data = f.read(4096)
193                 f.close()
194                 os.unlink(tempfilename)
195         return None