bugfix [#23194] export UVs miss the extension file

also made all other exporters do this.

Made some internal changes.
- moved path functions from bpy.utils to bpy.path (similar to os.path)
- added functions...
  bpy.path.ensure_ext(path, ".ext", case_sensitive=False) # simple function to ensure the extension is set.
  bpy.path.resolve_ncase(path) # useful for importing scenes made on windows where the path case doesnt match the files.
This commit is contained in:
2010-08-06 01:40:54 +00:00
parent 46d88bb803
commit 30d180ff0d
19 changed files with 273 additions and 188 deletions

View File

@@ -922,7 +922,7 @@ def make_kf_obj_node(obj, name_to_id):
"""
# import BPyMessages
def save_3ds(filename, context):
def write(filename, context):
'''Save the Blender scene to a 3ds file.'''
# Time the export
@@ -1107,12 +1107,7 @@ def save_3ds(filename, context):
#primary.dump()
# if __name__=='__main__':
# if struct:
# Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds'))
# else:
# Blender.Draw.PupMenu("Error%t|This script requires a full python installation")
# # save_3ds('/test_b.3ds')
# # write('/test_b.3ds')
from bpy.props import *
class Export3DS(bpy.types.Operator):
'''Export to 3DS file format (.3ds)'''
@@ -1127,7 +1122,10 @@ class Export3DS(bpy.types.Operator):
check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'})
def execute(self, context):
save_3ds(self.properties.filepath, context)
filepath = self.properties.filepath
filepath = bpy.path.ensure_ext(filepath, ".3ds")
write(filepath, context)
return {'FINISHED'}
def invoke(self, context, event):

View File

@@ -75,7 +75,7 @@ def copy_images(dest_dir, textures):
image_paths = set()
for tex in textures:
image_paths.add(bpy.utils.expandpath(tex.filepath))
image_paths.add(bpy.path.abspath(tex.filepath))
# Now copy images
copyCount = 0
@@ -176,7 +176,7 @@ def sane_name(data, dct):
name = 'unnamed' # blank string, ASKING FOR TROUBLE!
else:
name = bpy.utils.clean_name(name) # use our own
name = bpy.path.clean_name(name) # use our own
while name in iter(dct.values()): name = increment_string(name)
@@ -200,14 +200,14 @@ def sane_groupname(data): return sane_name(data, sane_name_mapping_group)
# FORCE_CWD - dont use the basepath, just add a ./ to the filename.
# use when we know the file will be in the basepath.
# '''
# fname = bpy.utils.expandpath(fname_orig)
# fname = bpy.path.abspath(fname_orig)
# # fname = Blender.sys.expandpath(fname_orig)
# fname_strip = os.path.basename(fname)
# # fname_strip = strip_path(fname)
# if FORCE_CWD:
# fname_rel = '.' + os.sep + fname_strip
# else:
# fname_rel = bpy.utils.relpath(fname, basepath)
# fname_rel = bpy.path.relpath(fname, basepath)
# # fname_rel = Blender.sys.relpath(fname, basepath)
# if fname_rel.startswith('//'): fname_rel = '.' + os.sep + fname_rel[2:]
# return fname, fname_strip, fname_rel
@@ -354,8 +354,8 @@ def write(filename, batch_objects = None, \
new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
for data in data_seq: # scene or group
newname = BATCH_FILE_PREFIX + bpy.utils.clean_name(data.name)
# newname = BATCH_FILE_PREFIX + BPySys.bpy.utils.clean_name(data.name)
newname = BATCH_FILE_PREFIX + bpy.path.clean_name(data.name)
# newname = BATCH_FILE_PREFIX + BPySys.bpy.path.clean_name(data.name)
if BATCH_OWN_DIR:
@@ -1250,7 +1250,7 @@ def write(filename, batch_objects = None, \
file.write('\n\t}')
def copy_image(image):
fn = bpy.utils.expandpath(image.filepath)
fn = bpy.path.abspath(image.filepath)
fn_strip = os.path.basename(fn)
if EXP_IMAGE_COPY:
@@ -3369,13 +3369,16 @@ class ExportFBX(bpy.types.Operator):
if not self.properties.filepath:
raise Exception("filepath not set")
filepath = self.properties.filepath
filepath = bpy.path.ensure_ext(filepath, ".fbx")
GLOBAL_MATRIX = mtx4_identity
GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = self.properties.TX_SCALE
if self.properties.TX_XROT90: GLOBAL_MATRIX = mtx4_x90n * GLOBAL_MATRIX
if self.properties.TX_YROT90: GLOBAL_MATRIX = mtx4_y90n * GLOBAL_MATRIX
if self.properties.TX_ZROT90: GLOBAL_MATRIX = mtx4_z90n * GLOBAL_MATRIX
write(self.properties.filepath,
write(filepath,
None, # XXX
context,
self.properties.EXP_OBS_SELECTED,
@@ -3395,7 +3398,8 @@ class ExportFBX(bpy.types.Operator):
self.properties.BATCH_ENABLE,
self.properties.BATCH_GROUP,
self.properties.BATCH_FILE_PREFIX,
self.properties.BATCH_OWN_DIR)
self.properties.BATCH_OWN_DIR,
)
return {'FINISHED'}
@@ -3413,7 +3417,7 @@ class ExportFBX(bpy.types.Operator):
# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts)
# - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print
# - get rid of bpy.utils.clean_name somehow
# - get rid of bpy.path.clean_name somehow
# + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565
# + get rid of BPyObject_getObjectArmature, move it in RNA?
# - BATCH_ENABLE and BATCH_GROUP options: line 327
@@ -3428,7 +3432,7 @@ class ExportFBX(bpy.types.Operator):
# - bpy.data.remove_scene: line 366
# - bpy.sys.time move to bpy.sys.util?
# - new scene creation, activation: lines 327-342, 368
# - uses bpy.utils.expandpath, *.relpath - replace at least relpath
# - uses bpy.path.abspath, *.relpath - replace at least relpath
# SMALL or COSMETICAL
# - find a way to get blender version, and put it in bpy.util?, old was Blender.Get('version')

View File

@@ -171,10 +171,17 @@ class ExportMDD(bpy.types.Operator):
return (ob and ob.type == 'MESH')
def execute(self, context):
if not self.properties.filepath:
raise Exception("filename not set")
write(self.properties.filepath, context.scene, context.active_object,
self.properties.frame_start, self.properties.frame_end, self.properties.fps)
filepath = self.properties.filepath
filepath = bpy.path.ensure_ext(filepath, ".mdd")
write(filepath,
context.scene,
context.active_object,
self.properties.frame_start,
self.properties.frame_end,
self.properties.fps,
)
return {'FINISHED'}
def invoke(self, context, event):

View File

@@ -66,7 +66,7 @@ def write_mtl(scene, filepath, copy_images, mtl_dict):
dest_dir = os.path.dirname(filepath)
def copy_image(image):
fn = bpy.utils.expandpath(image.filepath)
fn = bpy.path.abspath(image.filepath)
fn_strip = os.path.basename(fn)
if copy_images:
rel = fn_strip
@@ -182,7 +182,7 @@ def copy_images(dest_dir):
copyCount = 0
# for bImage in uniqueImages.values():
# image_path = bpy.utils.expandpath(bImage.filepath)
# image_path = bpy.path.abspath(bImage.filepath)
# if bpy.sys.exists(image_path):
# # Make a name for the target path.
# dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
@@ -790,7 +790,7 @@ def write(filepath, objects, scene,
print("OBJ Export time: %.2f" % (time.clock() - time1))
# print "OBJ Export time: %.2f" % (sys.time() - time1)
def do_export(filepath, context,
def write(filepath, context,
EXPORT_APPLY_MODIFIERS = True, # not used
EXPORT_ROTX90 = True, # wrong
EXPORT_TRI = False, # ok
@@ -837,7 +837,7 @@ def do_export(filepath, context,
orig_frame = scn.frame_current
if EXPORT_ALL_SCENES: # Add scene name into the context_name
context_name[1] = '_%s' % bpy.utils.clean_name(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.
context_name[1] = '_%s' % bpy.path.clean_name(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.
# Export an animation?
if EXPORT_ANIMATION:
@@ -927,27 +927,27 @@ class ExportOBJ(bpy.types.Operator):
def execute(self, context):
filepath = self.properties.filepath
if not filepath.lower().endswith(".obj"):
filepath += ".obj"
filepath = bpy.path.ensure_ext(filepath, ".obj")
do_export(filepath, context,
EXPORT_TRI=self.properties.use_triangles,
EXPORT_EDGES=self.properties.use_edges,
EXPORT_NORMALS=self.properties.use_normals,
EXPORT_NORMALS_HQ=self.properties.use_hq_normals,
EXPORT_UV=self.properties.use_uvs,
EXPORT_MTL=self.properties.use_materials,
EXPORT_COPY_IMAGES=self.properties.copy_images,
EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers,
EXPORT_ROTX90=self.properties.use_rotate90,
EXPORT_BLEN_OBS=self.properties.use_blen_objects,
EXPORT_GROUP_BY_OB=self.properties.group_by_object,
EXPORT_GROUP_BY_MAT=self.properties.group_by_material,
EXPORT_KEEP_VERT_ORDER=self.properties.keep_vertex_order,
EXPORT_POLYGROUPS=self.properties.use_vertex_groups,
EXPORT_CURVE_AS_NURBS=self.properties.use_nurbs,
EXPORT_SEL_ONLY=self.properties.use_selection,
EXPORT_ALL_SCENES=self.properties.use_all_scenes)
write(filepath, context,
EXPORT_TRI=self.properties.use_triangles,
EXPORT_EDGES=self.properties.use_edges,
EXPORT_NORMALS=self.properties.use_normals,
EXPORT_NORMALS_HQ=self.properties.use_hq_normals,
EXPORT_UV=self.properties.use_uvs,
EXPORT_MTL=self.properties.use_materials,
EXPORT_COPY_IMAGES=self.properties.copy_images,
EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers,
EXPORT_ROTX90=self.properties.use_rotate90,
EXPORT_BLEN_OBS=self.properties.use_blen_objects,
EXPORT_GROUP_BY_OB=self.properties.group_by_object,
EXPORT_GROUP_BY_MAT=self.properties.group_by_material,
EXPORT_KEEP_VERT_ORDER=self.properties.keep_vertex_order,
EXPORT_POLYGROUPS=self.properties.use_vertex_groups,
EXPORT_CURVE_AS_NURBS=self.properties.use_nurbs,
EXPORT_SEL_ONLY=self.properties.use_selection,
EXPORT_ALL_SCENES=self.properties.use_all_scenes,
)
return {'FINISHED'}

View File

@@ -280,12 +280,10 @@ class ExportPLY(bpy.types.Operator):
return context.active_object != None
def execute(self, context):
# print("Selected: " + context.active_object.name)
filepath = self.properties.filepath
filepath = bpy.path.ensure_ext(filepath, ".ply")
if not self.properties.filepath:
raise Exception("filename not set")
write(self.properties.filepath, context.scene, context.active_object,\
write(filepath, context.scene, context.active_object,\
EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers,
EXPORT_NORMALS=self.properties.use_normals,
EXPORT_UV=self.properties.use_uvs,

View File

@@ -794,7 +794,7 @@ class x3d_class:
pic = tex.image
# using .expandpath just in case, os.path may not expect //
basename = os.path.basename(bpy.utils.expandpath(pic.filepath))
basename = os.path.basename(bpy.path.abspath(pic.filepath))
pic = alltextures[i].image
# pic = alltextures[i].getImage()
@@ -1140,7 +1140,7 @@ class x3d_class:
# Callbacks, needed before Main
##########################################################
def x3d_export(filename,
def write(filename,
context,
EXPORT_APPLY_MODIFIERS=False,
EXPORT_TRI=False,
@@ -1175,47 +1175,6 @@ def x3d_export(filename,
)
def x3d_export_ui(filename):
if not filename.endswith(extension):
filename += extension
#if _safeOverwrite and sys.exists(filename):
# result = Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
#if(result != 1):
# return
# Get user options
EXPORT_APPLY_MODIFIERS = Draw.Create(1)
EXPORT_TRI = Draw.Create(0)
EXPORT_GZIP = Draw.Create( filename.lower().endswith('.x3dz') )
# Get USER Options
pup_block = [\
('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object.'),\
('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\
('Compress', EXPORT_GZIP, 'GZip the resulting file, requires a full python install'),\
]
if not Draw.PupBlock('Export...', pup_block):
return
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
x3d_export(filename,\
EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val,\
EXPORT_TRI = EXPORT_TRI.val,\
EXPORT_GZIP = EXPORT_GZIP.val\
)
Blender.Window.WaitCursor(0)
#########################################################
# main routine
#########################################################
from bpy.props import *
class ExportX3D(bpy.types.Operator):
@@ -1233,7 +1192,16 @@ class ExportX3D(bpy.types.Operator):
compress = BoolProperty(name="Compress", description="GZip the resulting file, requires a full python install", default=False)
def execute(self, context):
x3d_export(self.properties.filepath, context, self.properties.apply_modifiers, self.properties.triangulate, self.properties.compress)
filepath = self.properties.filepath
filepath = bpy.path.ensure_ext(filepath, ".x3d")
write(filepath,
context,
self.properties.apply_modifiers,
self.properties.triangulate,
self.properties.compress,
)
return {'FINISHED'}
def invoke(self, context, event):

View File

@@ -49,7 +49,7 @@ def addPointCache(job, ob, point_cache, default_path):
if name == "":
name = "".join(["%02X" % ord(c) for c in ob.name])
cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
cache_path = bpy.path.abspath(point_cache.filepath) if point_cache.external else default_path
index = "%02i" % point_cache.index
@@ -113,7 +113,7 @@ def clientSendJob(conn, scene, anim = False):
# LIBRARIES
###########################
for lib in bpy.data.libraries:
file_path = bpy.utils.expandpath(lib.filepath)
file_path = bpy.path.abspath(lib.filepath)
if os.path.exists(file_path):
job.addFile(file_path)
@@ -122,7 +122,7 @@ def clientSendJob(conn, scene, anim = False):
###########################
for image in bpy.data.images:
if image.source == "FILE" and not image.packed_file:
file_path = bpy.utils.expandpath(image.filepath)
file_path = bpy.path.abspath(image.filepath)
if os.path.exists(file_path):
job.addFile(file_path)
@@ -139,7 +139,7 @@ def clientSendJob(conn, scene, anim = False):
for object in bpy.data.objects:
for modifier in object.modifiers:
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
addFluidFiles(job, bpy.path.abspath(modifier.settings.path))
elif modifier.type == "CLOTH":
addPointCache(job, object, modifier.point_cache, default_path)
elif modifier.type == "SOFT_BODY":
@@ -149,7 +149,7 @@ def clientSendJob(conn, scene, anim = False):
if modifier.domain_settings.highres:
addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
elif modifier.type == "MULTIRES" and modifier.external:
file_path = bpy.utils.expandpath(modifier.filepath)
file_path = bpy.path.abspath(modifier.filepath)
job.addFile(file_path)
# particles modifier are stupid and don't contain data

View File

@@ -92,7 +92,7 @@ def process(paths):
# LIBRARIES
###########################
for lib in bpy.data.libraries:
file_path = bpy.utils.expandpath(lib.filepath)
file_path = bpy.path.abspath(lib.filepath)
new_path = path_map.get(os.path.split(file_path)[1], None)
if new_path:
lib.filepath = new_path
@@ -102,7 +102,7 @@ def process(paths):
###########################
for image in bpy.data.images:
if image.source == "FILE" and not image.packed_file:
file_path = bpy.utils.expandpath(image.filepath)
file_path = bpy.path.abspath(image.filepath)
new_path = path_map.get(os.path.split(file_path)[1], None)
if new_path:
image.filepath = new_path
@@ -124,7 +124,7 @@ def process(paths):
if modifier.domain_settings.highres:
processPointCache(modifier.domain_settings.point_cache_high)
elif modifier.type == "MULTIRES" and modifier.external:
file_path = bpy.utils.expandpath(modifier.filepath)
file_path = bpy.path.abspath(modifier.filepath)
new_path = path_map.get(file_path, None)
if new_path:
modifier.filepath = new_path

View File

@@ -26,7 +26,7 @@ data = _bpy.data
context = _bpy.context
# python modules
from bpy import utils
from bpy import utils, path
from bpy import ops as _ops_module

View File

@@ -0,0 +1,173 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
"""
This module has a similar scope to os.path, containing utility
functions for dealing with paths in Blender.
"""
import bpy as _bpy
def expand(path):
"""
Returns the absolute path relative to the current blend file using the "//" prefix.
"""
if path.startswith("//"):
return _os.path.join(_os.path.dirname(_bpy.data.filepath), path[2:])
return path
def relpath(path, start=None):
"""
Returns the path relative to the current blend file using the "//" prefix.
:arg start: Relative to this path, when not set the current filename is used.
:type start: string
"""
if not path.startswith("//"):
if start is None:
start = _os.path.dirname(_bpy.data.filepath)
return "//" + _os.path.relpath(path, start)
return path
def clean_name(name, replace="_"):
"""
Returns a name with characters replaced that may cause problems under various circumstances, such as writing to a file.
All characters besides A-Z/a-z, 0-9 are replaced with "_"
or the replace argument if defined.
"""
unclean_chars = \
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\
\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\
\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\
\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x40\x5b\x5c\x5d\x5e\x60\x7b\
\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\
\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\
\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\
\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\
\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\
\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\
\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\
\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\
\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
for ch in unclean_chars:
name = name.replace(ch, replace)
return name
def display_name(name):
"""
Creates a display string from name to be used menus and the user interface.
Capitalize the first letter in all lowercase names, mixed case names are kept as is.
Intended for use with filenames and module names.
"""
name_base = _os.path.splitext(name)[0]
# string replacements
name_base = name_base.replace("_colon_", ":")
name_base = name_base.replace("_", " ")
if name_base.islower():
return name_base.capitalize()
else:
return name_base
def resolve_ncase(path):
"""
Resolve a case insensitive path on a case sensitive system,
returning a string with the path if found else return the original path.
"""
import os
def _ncase_path_found(path):
if path=='' or os.path.exists(path):
return path, True
filename = os.path.basename(path) # filename may be a directory or a file
dirpath = os.path.dirname(path)
suffix = ""
if not filename: # dir ends with a slash?
if len(dirpath) < len(path):
suffix = path[:len(path)-len(dirpath)]
filename = os.path.basename(dirpath)
dirpath = os.path.dirname(dirpath)
if not os.path.exists(dirpath):
dirpath, found = _ncase_path_found(dirpath)
if not found:
return path, False
# at this point, the directory exists but not the file
# we are expecting 'dirpath' to be a directory, but it could be a file
if os.path.isdir(dirpath):
files = os.listdir(dirpath)
else:
return path, False
filename_low = filename.lower()
f_iter_nocase = None
for f_iter in files:
if f_iter.lower() == filename_low:
f_iter_nocase = f_iter
break
if f_iter_nocase:
return os.path.join(dirpath, f_iter_nocase) + suffix, True
else:
# cant find the right one, just return the path as is.
return path, False
ncase_path, found = _ncase_path_found(path)
return ncase_path if found else path
def ensure_ext(filepath, ext, case_sensitive=False):
"""
Return the path with the extension added its its not alredy set.
:arg ext: The extension to check for.
:type ext: string
:arg case_sensitive: Check for matching case when comparing extensions.
:type case_sensitive: bool
"""
import os
fn_base, fn_ext = os.path.splitext(filepath)
if fn_base and fn_ext:
if (case_sensitive and ext == fn_ext) or (ext.lower() == fn_ext.lower()):
return filepath
else:
return fn_base + ext
else:
return filepath + ext

View File

@@ -216,75 +216,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
_bpy_types._register_immediate = True
def expandpath(path):
"""
Returns the absolute path relative to the current blend file using the "//" prefix.
"""
if path.startswith("//"):
return _os.path.join(_os.path.dirname(_bpy.data.filepath), path[2:])
return path
def relpath(path, start=None):
"""
Returns the path relative to the current blend file using the "//" prefix.
:arg start: Relative to this path, when not set the current filename is used.
:type start: string
"""
if not path.startswith("//"):
if start is None:
start = _os.path.dirname(_bpy.data.filepath)
return "//" + _os.path.relpath(path, start)
return path
def clean_name(name, replace="_"):
"""
Returns a name with characters replaced that may cause problems under various circumstances, such as writing to a file.
All characters besides A-Z/a-z, 0-9 are replaced with "_"
or the replace argument if defined.
"""
unclean_chars = \
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\
\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\
\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\
\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x40\x5b\x5c\x5d\x5e\x60\x7b\
\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\
\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\
\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\
\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\
\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\
\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\
\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\
\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\
\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
for ch in unclean_chars:
name = name.replace(ch, replace)
return name
def display_name(name):
"""
Creates a display string from name to be used menus and the user interface.
Capitalize the first letter in all lowercase names, mixed case names are kept as is.
Intended for use with filenames and module names.
"""
name_base = _os.path.splitext(name)[0]
# string replacements
name_base = name_base.replace("_colon_", ":")
name_base = name_base.replace("_", " ")
if name_base.islower():
return name_base.capitalize()
else:
return name_base
# base scripts

View File

@@ -718,7 +718,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
if f.startswith("."):
continue
preset_name = bpy.utils.display_name(f)
preset_name = bpy.path.display_name(f)
props = layout.operator(operator, text=preset_name)
for attr, value in props_default.items():

View File

@@ -537,7 +537,7 @@ def generate_test_all(context, GRAPH=False):
base_name = os.path.splitext(bpy.data.filepath)[0]
for obj, obj_new in new_objects:
for obj in (obj, obj_new):
fn = base_name + "-" + bpy.utils.clean_name(obj.name)
fn = base_name + "-" + bpy.path.clean_name(obj.name)
path_dot = fn + ".dot"
path_png = fn + ".png"

View File

@@ -58,7 +58,7 @@ class EditExternally(bpy.types.Operator):
def execute(self, context):
import os
import subprocess
filepath = bpy.utils.expandpath(self.properties.filepath)
filepath = bpy.path.abspath(self.properties.filepath)
if not os.path.exists(filepath):
self.report('ERROR', "Image path '%s' not found." % filepath)
@@ -93,7 +93,7 @@ class SaveDirty(bpy.types.Operator):
unique_paths = set()
for image in bpy.data.images:
if image.dirty:
filepath = bpy.utils.expandpath(image.filepath)
filepath = bpy.path.abspath(image.filepath)
if "\\" not in filepath and "/" not in filepath:
self.report({'WARNING'}, "Invalid path: " + filepath)
elif filepath in unique_paths:
@@ -135,7 +135,7 @@ class ProjectEdit(bpy.types.Operator):
filepath = os.path.basename(bpy.data.filepath)
filepath = os.path.splitext(filepath)[0]
# filepath = bpy.utils.clean_name(filepath) # fixes <memory> rubbish, needs checking
# filepath = bpy.path.clean_name(filepath) # fixes <memory> rubbish, needs checking
if filepath.startswith(".") or filepath == "":
# TODO, have a way to check if the file is saved, assume .B25.blend
@@ -147,12 +147,12 @@ class ProjectEdit(bpy.types.Operator):
obj = context.object
if obj:
filepath += "_" + bpy.utils.clean_name(obj.name)
filepath += "_" + bpy.path.clean_name(obj.name)
filepath_final = filepath + "." + EXT
i = 0
while os.path.exists(bpy.utils.expandpath(filepath_final)):
while os.path.exists(bpy.path.abspath(filepath_final)):
filepath_final = filepath + ("%.3d.%s" % (i, EXT))
i += 1

View File

@@ -79,7 +79,7 @@ class PlayRenderedAnim(bpy.types.Operator):
preset = prefs.filepaths.animation_player_preset
player_path = prefs.filepaths.animation_player
file_path = bpy.utils.expandpath(rd.output_path)
file_path = bpy.path.abspath(rd.output_path)
is_movie = rd.is_movie_format
# try and guess a command line if it doesn't exist
@@ -105,7 +105,7 @@ class PlayRenderedAnim(bpy.types.Operator):
# works for movies and images
file = rd.frame_path(frame=scene.frame_start)
file = bpy.utils.expandpath(file) # expand '//'
file = bpy.path.abspath(file) # expand '//'
cmd = [player_path]
# extra options, fps controls etc.

View File

@@ -114,7 +114,9 @@ class ExportUVLayout(bpy.types.Operator):
mode = self.properties.mode
file = open(self.properties.filepath, "w")
filepath = self.properties.filepath
filepath = bpy.path.ensure_ext(filepath, "." + mode.lower())
file = open(filepath, "w")
fw = file.write
if mode == 'SVG':

View File

@@ -478,7 +478,7 @@ class WM_OT_path_open(bpy.types.Operator):
import os
import subprocess
filepath = bpy.utils.expandpath(self.properties.filepath)
filepath = bpy.path.abspath(self.properties.filepath)
filepath = os.path.normpath(filepath)
if not os.path.exists(filepath):

View File

@@ -213,7 +213,7 @@ class Graph(bpy.types.Operator):
import bpy
reload(graphviz_export)
obj = bpy.context.object
path = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.utils.clean_name(obj.name)
path = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.path.clean_name(obj.name)
path_dot = path + ".dot"
path_png = path + ".png"
saved = graphviz_export.graph_armature(bpy.context.object, path_dot, CONSTRAINTS=False, DRIVERS=False)
@@ -250,7 +250,7 @@ class AsScript(bpy.types.Operator):
def invoke(self, context, event):
import os
obj = context.object
self.properties.filepath = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.utils.clean_name(obj.name) + ".py"
self.properties.filepath = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.path.clean_name(obj.name) + ".py"
wm = context.manager
wm.add_fileselect(self)
return {'RUNNING_MODAL'}
@@ -307,7 +307,7 @@ class INFO_MT_armature_metarig_add(bpy.types.Menu):
layout.operator_context = 'INVOKE_REGION_WIN'
for submodule_type in rigify.get_submodule_types():
text = bpy.utils.display_name(submodule_type)
text = bpy.path.display_name(submodule_type)
layout.operator("pose.metarig_sample_add", text=text, icon='OUTLINER_OB_ARMATURE').metarig_type = submodule_type
menu_func = (lambda self, context: self.layout.menu("INFO_MT_armature_metarig_add", icon='OUTLINER_OB_ARMATURE'))

View File

@@ -360,6 +360,7 @@ def rna2sphinx(BASEPATH):
# py modules
fw(" bpy.utils.rst\n\n")
fw(" bpy.path.rst\n\n")
fw(" bpy.app.rst\n\n")
# C modules
@@ -440,6 +441,9 @@ def rna2sphinx(BASEPATH):
from bpy import utils as module
pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities (bpy.utils)")
from bpy import path as module
pymodule2sphinx(BASEPATH, "bpy.path", module, "Path Utilities (bpy.path)")
# C modules
from bpy import app as module
pymodule2sphinx(BASEPATH, "bpy.app", module, "Application Data (bpy.app)")