Fix for memory leak caused by re-making mipmaps
[blender.git] / source / tests / batch_import.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 """
22 Example Usage:
23
24 ./blender.bin --background --python source/tests/batch_import.py -- \
25     --operator="bpy.ops.import_scene.obj" \
26     --path="/fe/obj" \
27     --match="*.obj" \
28     --start=0 --end=10 \
29     --save_path=/tmp/test
30
31 ./blender.bin --background --python source/tests/batch_import.py -- \
32     --operator="bpy.ops.import_scene.autodesk_3ds" \
33     --path="/fe/" \
34     --match="*.3ds" \
35     --start=0 --end=1000 \
36     --save_path=/tmp/test
37
38 ./blender.bin --background --addons io_curve_svg --python source/tests/batch_import.py -- \
39     --operator="bpy.ops.import_curve.svg" \
40     --path="/usr/" \
41     --match="*.svg" \
42     --start=0 --end=1000 \
43     --save_path=/tmp/test
44
45 """
46
47 import os
48 import sys
49
50
51 def clear_scene():
52     import bpy
53     unique_obs = set()
54     for scene in bpy.data.scenes:
55         for obj in scene.objects[:]:
56             scene.objects.unlink(obj)
57             unique_obs.add(obj)
58
59     # remove obdata, for now only worry about the startup scene
60     for bpy_data_iter in (bpy.data.objects, bpy.data.meshes, bpy.data.lamps, bpy.data.cameras):
61         for id_data in bpy_data_iter:
62             bpy_data_iter.remove(id_data)
63
64
65 def batch_import(operator="",
66                    path="",
67                    save_path="",
68                    match="",
69                    start=0,
70                    end=sys.maxsize,
71                    ):
72     import addon_utils
73     _reset_all = addon_utils.reset_all  # XXX, hack
74
75     import fnmatch
76
77     path = os.path.normpath(path)
78     path = os.path.abspath(path)
79
80     match_upper = match.upper()
81     pattern_match = lambda a: fnmatch.fnmatchcase(a.upper(), match_upper)
82
83     def file_generator(path):
84         for dirpath, dirnames, filenames in os.walk(path):
85
86             # skip '.svn'
87             if dirpath.startswith("."):
88                 continue
89
90             for filename in filenames:
91                 if pattern_match(filename):
92                     yield os.path.join(dirpath, filename)
93
94     print("Collecting %r files in %s" % (match, path), end="")
95
96     files = list(file_generator(path))
97     files_len = len(files)
98     end = min(end, len(files))
99     print(" found %d" % files_len, end="")
100
101     files.sort()
102     files = files[start:end]
103     if len(files) != files_len:
104         print(" using a subset in (%d, %d), total %d" % (start, end, len(files)), end="")
105
106     import bpy
107     op = eval(operator)
108
109     tot_done = 0
110     tot_fail = 0
111
112     for i, f in enumerate(files):
113         print("    %s(filepath=%r) # %d of %d" % (operator, f, i + start, len(files)))
114
115         # hack so loading the new file doesnt undo our loaded addons
116         addon_utils.reset_all = lambda: None  # XXX, hack
117
118         bpy.ops.wm.read_factory_settings()
119
120         addon_utils.reset_all = _reset_all  # XXX, hack
121         clear_scene()
122
123         result = op(filepath=f)
124
125         if 'FINISHED' in result:
126             tot_done += 1
127         else:
128             tot_fail += 1
129
130         if save_path:
131             fout = os.path.join(save_path, os.path.relpath(f, path))
132             fout_blend = os.path.splitext(fout)[0] + ".blend"
133
134             print("\tSaving: %r" % fout_blend)
135
136             fout_dir = os.path.dirname(fout_blend)
137             if not os.path.exists(fout_dir):
138                 os.makedirs(fout_dir)
139
140             bpy.ops.wm.save_as_mainfile(filepath=fout_blend)
141
142     print("finished, done:%d,  fail:%d" % (tot_done, tot_fail))
143
144
145 def main():
146     import optparse
147
148     # get the args passed to blender after "--", all of which are ignored by blender specifically
149     # so python may receive its own arguments
150     argv = sys.argv
151
152     if "--" not in argv:
153         argv = []  # as if no args are passed
154     else:
155         argv = argv[argv.index("--") + 1:]  # get all args after "--"
156
157     # When --help or no args are given, print this help
158     usage_text = "Run blender in background mode with this script:"
159     usage_text += "  blender --background --python " + __file__ + " -- [options]"
160
161     parser = optparse.OptionParser(usage=usage_text)
162
163     # Example background utility, add some text and renders or saves it (with options)
164     # Possible types are: string, int, long, choice, float and complex.
165     parser.add_option("-o", "--operator", dest="operator", help="This text will be used to render an image", type="string")
166     parser.add_option("-p", "--path", dest="path", help="Path to use for searching for files", type='string')
167     parser.add_option("-m", "--match", dest="match", help="Wildcard to match filename", type="string")
168     parser.add_option("-s", "--save_path", dest="save_path", help="Save the input file to a blend file in a new location", metavar='string')
169     parser.add_option("-S", "--start", dest="start", help="From collected files, start with this index", metavar='int')
170     parser.add_option("-E", "--end", dest="end", help="From collected files, end with this index", metavar='int')
171
172     options, args = parser.parse_args(argv)  # In this example we wont use the args
173
174     if not argv:
175         parser.print_help()
176         return
177
178     if not options.operator:
179         print("Error: --operator=\"some string\" argument not given, aborting.")
180         parser.print_help()
181         return
182
183     if options.start is None:
184         options.start = 0
185
186     if options.end is None:
187         options.end = sys.maxsize
188
189     # Run the example function
190     batch_import(operator=options.operator,
191                  path=options.path,
192                  save_path=options.save_path,
193                  match=options.match,
194                  start=int(options.start),
195                  end=int(options.end),
196                  )
197
198     print("batch job finished, exiting")
199
200
201 if __name__ == "__main__":
202     main()