forked from bartvdbraak/blender
pep8 cleanup
This commit is contained in:
parent
f0eb3b56de
commit
736a7b7a22
@ -21,6 +21,8 @@
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
IGNORE = \
|
||||
"/test/",\
|
||||
"/decimate_glut_test/",\
|
||||
@ -45,6 +47,8 @@ global_c = set()
|
||||
|
||||
import os
|
||||
from os.path import splitext
|
||||
|
||||
|
||||
def source_list(path, filename_check=None):
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
|
||||
@ -56,31 +60,37 @@ def source_list(path, filename_check=None):
|
||||
if filename_check is None or filename_check(filename):
|
||||
yield os.path.join(dirpath, filename)
|
||||
|
||||
|
||||
# extension checking
|
||||
def is_c_header(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in (".h", ".hpp", ".hxx"))
|
||||
|
||||
|
||||
def is_cmake(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext == ".cmake") or (filename == "CMakeLists.txt")
|
||||
|
||||
|
||||
def is_c_header(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in (".h", ".hpp", ".hxx"))
|
||||
|
||||
|
||||
def is_c(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in (".c", ".cpp", ".cxx", ".m", ".mm", ".rc"))
|
||||
|
||||
|
||||
def is_c_any(filename):
|
||||
return is_c(filename) or is_c_header(filename)
|
||||
|
||||
|
||||
def cmake_get_src(f):
|
||||
|
||||
|
||||
sources_h = []
|
||||
sources_c = []
|
||||
|
||||
|
||||
filen = open(f, "r", encoding="utf8")
|
||||
it = iter(filen)
|
||||
found = False
|
||||
@ -101,7 +111,7 @@ def cmake_get_src(f):
|
||||
raise Exception("strict formatting not kept 'set(SRC*' %s:%d" % (f, i))
|
||||
found = True
|
||||
break
|
||||
|
||||
|
||||
if "list(APPEND SRC" in l:
|
||||
if l.endswith(")"):
|
||||
raise Exception("strict formatting not kept 'list(APPEND SRC...)' on 1 line %s:%d" % (f, i))
|
||||
@ -118,11 +128,11 @@ def cmake_get_src(f):
|
||||
except StopIteration:
|
||||
it = None
|
||||
break
|
||||
|
||||
|
||||
l = l.strip()
|
||||
|
||||
if not l.startswith("#"):
|
||||
|
||||
|
||||
if ")" in l:
|
||||
if l.strip() != ")":
|
||||
raise Exception("strict formatting not kept '*)' %s:%d" % (f, i))
|
||||
@ -130,7 +140,6 @@ def cmake_get_src(f):
|
||||
|
||||
# replace dirs
|
||||
l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base)
|
||||
|
||||
|
||||
if not l:
|
||||
pass
|
||||
@ -140,7 +149,7 @@ def cmake_get_src(f):
|
||||
raise Exception("Multi-line define '%s' %s:%d" % (l, f, i))
|
||||
else:
|
||||
new_file = normpath(join(cmake_base, l))
|
||||
|
||||
|
||||
if is_c_header(new_file):
|
||||
sources_h.append(new_file)
|
||||
elif is_c(new_file):
|
||||
@ -168,19 +177,20 @@ def cmake_get_src(f):
|
||||
if ff not in sources_c:
|
||||
print(" missing: " + ff)
|
||||
'''
|
||||
|
||||
|
||||
filen.close()
|
||||
|
||||
|
||||
for cmake in source_list(base, is_cmake):
|
||||
cmake_get_src(cmake)
|
||||
|
||||
|
||||
def is_ignore(f):
|
||||
for ig in IGNORE:
|
||||
if ig in f:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# First do stupid check, do these files exist?
|
||||
for f in (global_h | global_c):
|
||||
if f.endswith("dna.c"):
|
||||
@ -189,7 +199,7 @@ for f in (global_h | global_c):
|
||||
if not os.path.exists(f):
|
||||
raise Exception("CMake referenced file missing: " + f)
|
||||
|
||||
|
||||
|
||||
# now check on files not accounted for.
|
||||
print("\nC/C++ Files CMake doesnt know about...")
|
||||
for cf in sorted(source_list(base, is_c)):
|
||||
|
@ -21,6 +21,8 @@
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import os
|
||||
from os.path import join, dirname, normpath, abspath, splitext, relpath, exists
|
||||
|
||||
@ -28,6 +30,7 @@ base = join(os.path.dirname(__file__), "..", "..")
|
||||
base = normpath(base)
|
||||
base = abspath(base)
|
||||
|
||||
|
||||
def source_list(path, filename_check=None):
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
|
||||
@ -40,33 +43,40 @@ def source_list(path, filename_check=None):
|
||||
if filename_check is None or filename_check(filepath):
|
||||
yield filepath
|
||||
|
||||
|
||||
# extension checking
|
||||
def is_c_header(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in (".h", ".hpp", ".hxx"))
|
||||
|
||||
|
||||
def is_cmake(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext == ".cmake") or (filename == "CMakeLists.txt")
|
||||
|
||||
|
||||
def is_c_header(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in (".h", ".hpp", ".hxx"))
|
||||
|
||||
|
||||
def is_c(filename):
|
||||
ext = splitext(filename)[1]
|
||||
return (ext in (".c", ".cpp", ".cxx", ".m", ".mm", ".rc"))
|
||||
|
||||
|
||||
def is_c_any(filename):
|
||||
return is_c(filename) or is_c_header(filename)
|
||||
|
||||
|
||||
def is_svn_file(filename):
|
||||
dn, fn = os.path.split(filename)
|
||||
filename_svn = join(dn, ".svn", "text-base", "%s.svn-base" % fn)
|
||||
return exists(filename_svn)
|
||||
dn, fn = os.path.split(filename)
|
||||
filename_svn = join(dn, ".svn", "text-base", "%s.svn-base" % fn)
|
||||
return exists(filename_svn)
|
||||
|
||||
|
||||
def is_project_file(filename):
|
||||
return (is_c_any(filename) or is_cmake(filename)) and is_svn_file(filename)
|
||||
return (is_c_any(filename) or is_cmake(filename)) and is_svn_file(filename)
|
||||
|
||||
files = list(source_list(base, filename_check=is_project_file))
|
||||
files_rel = [relpath(f, start=base) for f in files]
|
||||
@ -87,8 +97,8 @@ f.write("[General]\n")
|
||||
|
||||
qtc_cfg = join(base, "%s.config" % PROJECT_NAME)
|
||||
if not exists(qtc_cfg):
|
||||
f = open(qtc_cfg, 'w')
|
||||
f.write("// ADD PREDEFINED MACROS HERE!\n")
|
||||
f = open(qtc_cfg, 'w')
|
||||
f.write("// ADD PREDEFINED MACROS HERE!\n")
|
||||
|
||||
print("Project file written to: %s" % qtc_prj)
|
||||
# --- end
|
||||
|
@ -18,6 +18,8 @@
|
||||
#
|
||||
# #**** END GPL LICENSE BLOCK #****
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
script_help_msg = '''
|
||||
Usage:
|
||||
|
||||
@ -31,11 +33,11 @@ For HTML generation
|
||||
assuming that ./blender.bin is or links to the blender executable
|
||||
|
||||
- Generate html docs by running...
|
||||
|
||||
|
||||
sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
|
||||
|
||||
assuming that you have sphinx 0.6.7 installed
|
||||
|
||||
|
||||
For PDF generation
|
||||
------------------
|
||||
- After you have built doc/python_api/sphinx-in (see above), run:
|
||||
@ -81,10 +83,12 @@ def range_str(val):
|
||||
Converts values to strings for the range directive.
|
||||
(unused function it seems)
|
||||
'''
|
||||
if val < -10000000: return '-inf'
|
||||
if val > 10000000: return 'inf'
|
||||
if type(val)==float:
|
||||
return '%g' % val
|
||||
if val < -10000000:
|
||||
return '-inf'
|
||||
elif val > 10000000:
|
||||
return 'inf'
|
||||
elif type(val) == float:
|
||||
return '%g' % val
|
||||
else:
|
||||
return str(val)
|
||||
|
||||
@ -139,7 +143,7 @@ def pyfunc2sphinx(ident, fw, identifier, py_func, is_class=True):
|
||||
|
||||
if not is_class:
|
||||
func_type = "function"
|
||||
|
||||
|
||||
# ther rest are class methods
|
||||
elif arg_str.startswith("(self, "):
|
||||
arg_str = "(" + arg_str[7:]
|
||||
@ -156,14 +160,14 @@ def pyfunc2sphinx(ident, fw, identifier, py_func, is_class=True):
|
||||
fw("\n")
|
||||
|
||||
|
||||
def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier):
|
||||
def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier):
|
||||
if identifier.startswith("_"):
|
||||
return
|
||||
|
||||
|
||||
doc = descr.__doc__
|
||||
if not doc:
|
||||
doc = undocumented_message(module_name, type_name, identifier)
|
||||
|
||||
|
||||
if type(descr) == GetSetDescriptorType:
|
||||
fw(ident + ".. attribute:: %s\n\n" % identifier)
|
||||
write_indented_lines(ident + " ", fw, doc, False)
|
||||
@ -180,7 +184,7 @@ def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_
|
||||
'''
|
||||
c defined function to sphinx.
|
||||
'''
|
||||
|
||||
|
||||
# dump the docstring, assume its formatted correctly
|
||||
if py_func.__doc__:
|
||||
write_indented_lines(ident, fw, py_func.__doc__, False)
|
||||
@ -208,30 +212,30 @@ def pymodule2sphinx(BASEPATH, module_name, module, title):
|
||||
import types
|
||||
attribute_set = set()
|
||||
filepath = os.path.join(BASEPATH, module_name + ".rst")
|
||||
|
||||
|
||||
file = open(filepath, "w")
|
||||
|
||||
fw = file.write
|
||||
|
||||
|
||||
fw(title + "\n")
|
||||
fw(("=" * len(title)) + "\n\n")
|
||||
|
||||
|
||||
fw(".. module:: %s\n\n" % module_name)
|
||||
|
||||
|
||||
if module.__doc__:
|
||||
# Note, may contain sphinx syntax, dont mangle!
|
||||
fw(module.__doc__.strip())
|
||||
fw("\n\n")
|
||||
|
||||
|
||||
write_example_ref("", fw, module_name)
|
||||
|
||||
|
||||
# write members of the module
|
||||
# only tested with PyStructs which are not exactly modules
|
||||
for key, descr in sorted(type(module).__dict__.items()):
|
||||
if key.startswith("__"):
|
||||
continue
|
||||
# naughty, we also add getset's into PyStructs, this is not typical py but also not incorrect.
|
||||
if type(descr) == types.GetSetDescriptorType: # 'bpy_app_type' name is only used for examples and messages
|
||||
if type(descr) == types.GetSetDescriptorType: # 'bpy_app_type' name is only used for examples and messages
|
||||
py_descr2sphinx("", fw, descr, module_name, "bpy_app_type", key)
|
||||
attribute_set.add(key)
|
||||
for key, descr in sorted(type(module).__dict__.items()):
|
||||
@ -245,7 +249,7 @@ def pymodule2sphinx(BASEPATH, module_name, module, title):
|
||||
attribute_set.add(key)
|
||||
fw("\n")
|
||||
del key, descr
|
||||
|
||||
|
||||
classes = []
|
||||
|
||||
for attribute in sorted(dir(module)):
|
||||
@ -254,16 +258,16 @@ def pymodule2sphinx(BASEPATH, module_name, module, title):
|
||||
if attribute in attribute_set:
|
||||
continue
|
||||
|
||||
if attribute.startswith("n_"): # annoying exception, needed for bpy.app
|
||||
if attribute.startswith("n_"): # annoying exception, needed for bpy.app
|
||||
continue
|
||||
|
||||
|
||||
value = getattr(module, attribute)
|
||||
|
||||
value_type = type(value)
|
||||
|
||||
if value_type == types.FunctionType:
|
||||
pyfunc2sphinx("", fw, attribute, value, is_class=False)
|
||||
elif value_type in (types.BuiltinMethodType, types.BuiltinFunctionType): # both the same at the moment but to be future proof
|
||||
elif value_type in (types.BuiltinMethodType, types.BuiltinFunctionType): # both the same at the moment but to be future proof
|
||||
# note: can't get args from these, so dump the string as is
|
||||
# this means any module used like this must have fully formatted docstrings.
|
||||
py_c_func2sphinx("", fw, module_name, module, attribute, value, is_class=False)
|
||||
@ -310,7 +314,6 @@ def pymodule2sphinx(BASEPATH, module_name, module, title):
|
||||
file.close()
|
||||
|
||||
|
||||
|
||||
def rna2sphinx(BASEPATH):
|
||||
|
||||
structs, funcs, ops, props = rna_info.BuildRNAInfo()
|
||||
@ -325,14 +328,13 @@ def rna2sphinx(BASEPATH):
|
||||
file = open(filepath, "w")
|
||||
fw = file.write
|
||||
|
||||
|
||||
version_string = bpy.app.version_string.split("(")[0]
|
||||
if bpy.app.build_revision != "Unknown":
|
||||
version_string = version_string + " r" + bpy.app.build_revision
|
||||
|
||||
|
||||
# for use with files
|
||||
version_string_fp = "_".join(str(v) for v in bpy.app.version)
|
||||
|
||||
|
||||
fw("project = 'Blender'\n")
|
||||
# fw("master_doc = 'index'\n")
|
||||
fw("copyright = u'Blender Foundation'\n")
|
||||
@ -349,11 +351,11 @@ def rna2sphinx(BASEPATH):
|
||||
fw("latex_paper_size = 'a4paper'\n")
|
||||
file.close()
|
||||
|
||||
|
||||
# main page needed for sphinx (index.html)
|
||||
filepath = os.path.join(BASEPATH, "contents.rst")
|
||||
file = open(filepath, "w")
|
||||
fw = file.write
|
||||
|
||||
|
||||
fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
|
||||
fw(" Blender Documentation contents\n")
|
||||
fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
|
||||
@ -388,15 +390,15 @@ def rna2sphinx(BASEPATH):
|
||||
fw("\n")
|
||||
fw(".. toctree::\n")
|
||||
fw(" :maxdepth: 1\n\n")
|
||||
fw(" bpy.data.rst\n\n") # note: not actually a module
|
||||
fw(" bpy.data.rst\n\n") # note: not actually a module
|
||||
fw(" bpy.ops.rst\n\n")
|
||||
fw(" bpy.types.rst\n\n")
|
||||
|
||||
|
||||
# py modules
|
||||
fw(" bpy.utils.rst\n\n")
|
||||
fw(" bpy.path.rst\n\n")
|
||||
fw(" bpy.app.rst\n\n")
|
||||
|
||||
|
||||
# C modules
|
||||
fw(" bpy.props.rst\n\n")
|
||||
|
||||
@ -407,14 +409,13 @@ def rna2sphinx(BASEPATH):
|
||||
fw(".. toctree::\n")
|
||||
fw(" :maxdepth: 1\n\n")
|
||||
|
||||
|
||||
fw(" mathutils.rst\n\n")
|
||||
fw(" mathutils.geometry.rst\n\n")
|
||||
# XXX TODO
|
||||
#fw(" bgl.rst\n\n")
|
||||
fw(" blf.rst\n\n")
|
||||
fw(" aud.rst\n\n")
|
||||
|
||||
|
||||
# game engine
|
||||
fw("===================\n")
|
||||
fw("Game Engine Modules\n")
|
||||
@ -429,7 +430,6 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
file.close()
|
||||
|
||||
|
||||
# internal modules
|
||||
filepath = os.path.join(BASEPATH, "bpy.ops.rst")
|
||||
file = open(filepath, "w")
|
||||
@ -451,7 +451,6 @@ def rna2sphinx(BASEPATH):
|
||||
fw(" bpy.types.*\n\n")
|
||||
file.close()
|
||||
|
||||
|
||||
# not actually a module, only write this file so we
|
||||
# can reference in the TOC
|
||||
filepath = os.path.join(BASEPATH, "bpy.data.rst")
|
||||
@ -474,7 +473,6 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
EXAMPLE_SET_USED.add("bpy.data")
|
||||
|
||||
|
||||
# python modules
|
||||
from bpy import utils as module
|
||||
pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities (bpy.utils)")
|
||||
@ -488,11 +486,11 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
from bpy import props as module
|
||||
pymodule2sphinx(BASEPATH, "bpy.props", module, "Property Definitions (bpy.props)")
|
||||
|
||||
|
||||
import mathutils as module
|
||||
pymodule2sphinx(BASEPATH, "mathutils", module, "Math Types & Utilities (mathutils)")
|
||||
del module
|
||||
|
||||
|
||||
import mathutils.geometry as module
|
||||
pymodule2sphinx(BASEPATH, "mathutils.geometry", module, "Geometry Utilities (mathutils.geometry)")
|
||||
del module
|
||||
@ -500,7 +498,7 @@ def rna2sphinx(BASEPATH):
|
||||
import blf as module
|
||||
pymodule2sphinx(BASEPATH, "blf", module, "Font Drawing (blf)")
|
||||
del module
|
||||
|
||||
|
||||
# XXX TODO
|
||||
#import bgl as module
|
||||
#pymodule2sphinx(BASEPATH, "bgl", module, "Blender OpenGl wrapper (bgl)")
|
||||
@ -513,17 +511,16 @@ def rna2sphinx(BASEPATH):
|
||||
## game engine
|
||||
import shutil
|
||||
# copy2 keeps time/date stamps
|
||||
shutil.copy2(os.path.join(BASEPATH,"..","rst","bge.types.rst"), BASEPATH)
|
||||
shutil.copy2(os.path.join(BASEPATH,"..","rst","bge.logic.rst"), BASEPATH)
|
||||
shutil.copy2(os.path.join(BASEPATH,"..","rst","bge.render.rst"), BASEPATH)
|
||||
shutil.copy2(os.path.join(BASEPATH,"..","rst","bge.events.rst"), BASEPATH)
|
||||
|
||||
shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.types.rst"), BASEPATH)
|
||||
shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.logic.rst"), BASEPATH)
|
||||
shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.render.rst"), BASEPATH)
|
||||
shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.events.rst"), BASEPATH)
|
||||
|
||||
if 0:
|
||||
filepath = os.path.join(BASEPATH, "bpy.rst")
|
||||
file = open(filepath, "w")
|
||||
fw = file.write
|
||||
|
||||
|
||||
fw("\n")
|
||||
|
||||
title = ":mod:`bpy` --- Blender Python Module"
|
||||
@ -558,7 +555,7 @@ def rna2sphinx(BASEPATH):
|
||||
filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % struct.identifier)
|
||||
file = open(filepath, "w")
|
||||
fw = file.write
|
||||
|
||||
|
||||
base_id = getattr(struct.base, "identifier", "")
|
||||
|
||||
if _BPY_STRUCT_FAKE:
|
||||
@ -571,16 +568,16 @@ def rna2sphinx(BASEPATH):
|
||||
title = struct.identifier
|
||||
|
||||
fw("%s\n%s\n\n" % (title, "=" * len(title)))
|
||||
|
||||
|
||||
fw(".. module:: bpy.types\n\n")
|
||||
|
||||
|
||||
base_ids = [base.identifier for base in struct.get_bases()]
|
||||
|
||||
if _BPY_STRUCT_FAKE:
|
||||
base_ids.append(_BPY_STRUCT_FAKE)
|
||||
|
||||
base_ids.reverse()
|
||||
|
||||
|
||||
if base_ids:
|
||||
if len(base_ids) > 1:
|
||||
fw("base classes --- ")
|
||||
@ -589,13 +586,13 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
fw(", ".join((":class:`%s`" % base_id) for base_id in base_ids))
|
||||
fw("\n\n")
|
||||
|
||||
|
||||
subclass_ids = [s.identifier for s in structs.values() if s.base is struct if not rna_info.rna_id_ignore(s.identifier)]
|
||||
if subclass_ids:
|
||||
fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in subclass_ids) + "\n\n")
|
||||
|
||||
|
||||
base_id = getattr(struct.base, "identifier", "")
|
||||
|
||||
|
||||
if _BPY_STRUCT_FAKE:
|
||||
if not base_id:
|
||||
base_id = _BPY_STRUCT_FAKE
|
||||
@ -606,7 +603,7 @@ def rna2sphinx(BASEPATH):
|
||||
fw(".. class:: %s\n\n" % struct.identifier)
|
||||
|
||||
fw(" %s\n\n" % struct.description)
|
||||
|
||||
|
||||
# properties sorted in alphabetical order
|
||||
sorted_struct_properties = struct.properties[:]
|
||||
sorted_struct_properties.sort(key=lambda prop: prop.identifier)
|
||||
@ -621,7 +618,7 @@ def rna2sphinx(BASEPATH):
|
||||
if prop.description:
|
||||
fw(" %s\n\n" % prop.description)
|
||||
fw(" :type: %s\n\n" % type_descr)
|
||||
|
||||
|
||||
# python attributes
|
||||
py_properties = struct.get_py_properties()
|
||||
py_prop = None
|
||||
@ -634,13 +631,13 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
fw(" .. %s:: %s(%s)\n\n" % ("classmethod" if func.is_classmethod else "method", func.identifier, args_str))
|
||||
fw(" %s\n\n" % func.description)
|
||||
|
||||
|
||||
for prop in func.args:
|
||||
write_param(" ", fw, prop)
|
||||
|
||||
if len(func.return_values) == 1:
|
||||
write_param(" ", fw, func.return_values[0], is_return=True)
|
||||
elif func.return_values: # multiple return values
|
||||
elif func.return_values: # multiple return values
|
||||
fw(" :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values))
|
||||
for prop in func.return_values:
|
||||
type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`")
|
||||
@ -651,11 +648,10 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
fw("\n")
|
||||
|
||||
|
||||
# python methods
|
||||
py_funcs = struct.get_py_functions()
|
||||
py_func = None
|
||||
|
||||
|
||||
for identifier, py_func in py_funcs:
|
||||
pyfunc2sphinx(" ", fw, identifier, py_func, is_class=True)
|
||||
del py_funcs, py_func
|
||||
@ -667,10 +663,10 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
# props
|
||||
lines[:] = []
|
||||
|
||||
|
||||
if _BPY_STRUCT_FAKE:
|
||||
descr_items = [(key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items()) if not key.startswith("__")]
|
||||
|
||||
|
||||
if _BPY_STRUCT_FAKE:
|
||||
for key, descr in descr_items:
|
||||
if type(descr) == GetSetDescriptorType:
|
||||
@ -682,10 +678,10 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
for identifier, py_prop in base.get_py_properties():
|
||||
lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
|
||||
|
||||
|
||||
for identifier, py_prop in base.get_py_properties():
|
||||
lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
|
||||
|
||||
|
||||
if lines:
|
||||
fw(".. rubric:: Inherited Properties\n\n")
|
||||
|
||||
@ -696,7 +692,6 @@ def rna2sphinx(BASEPATH):
|
||||
fw(line)
|
||||
fw("\n")
|
||||
|
||||
|
||||
# funcs
|
||||
lines[:] = []
|
||||
|
||||
@ -720,9 +715,8 @@ def rna2sphinx(BASEPATH):
|
||||
for line in lines:
|
||||
fw(line)
|
||||
fw("\n")
|
||||
|
||||
lines[:] = []
|
||||
|
||||
lines[:] = []
|
||||
|
||||
if struct.references:
|
||||
# use this otherwise it gets in the index for a normal heading.
|
||||
@ -738,13 +732,12 @@ def rna2sphinx(BASEPATH):
|
||||
fw(" * :class:`%s`\n" % ref)
|
||||
fw("\n")
|
||||
|
||||
|
||||
for struct in structs.values():
|
||||
# TODO, rna_info should filter these out!
|
||||
if "_OT_" in struct.identifier:
|
||||
continue
|
||||
write_struct(struct)
|
||||
|
||||
|
||||
# special case, bpy_struct
|
||||
if _BPY_STRUCT_FAKE:
|
||||
filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % _BPY_STRUCT_FAKE)
|
||||
@ -769,41 +762,40 @@ def rna2sphinx(BASEPATH):
|
||||
descr_items = [(key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items()) if not key.startswith("__")]
|
||||
|
||||
for key, descr in descr_items:
|
||||
if type(descr) == MethodDescriptorType: # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
|
||||
if type(descr) == MethodDescriptorType: # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
|
||||
py_descr2sphinx(" ", fw, descr, "bpy.types", _BPY_STRUCT_FAKE, key)
|
||||
|
||||
for key, descr in descr_items:
|
||||
if type(descr) == GetSetDescriptorType:
|
||||
py_descr2sphinx(" ", fw, descr, "bpy.types", _BPY_STRUCT_FAKE, key)
|
||||
|
||||
|
||||
# operators
|
||||
def write_ops():
|
||||
API_BASEURL='https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/scripts'
|
||||
API_BASEURL = "https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/scripts"
|
||||
fw = None
|
||||
last_mod = ''
|
||||
|
||||
|
||||
for op_key in sorted(ops.keys()):
|
||||
op = ops[op_key]
|
||||
|
||||
|
||||
if last_mod != op.module_name:
|
||||
filepath = os.path.join(BASEPATH, "bpy.ops.%s.rst" % op.module_name)
|
||||
file = open(filepath, "w")
|
||||
fw = file.write
|
||||
|
||||
title = "%s Operators" % (op.module_name[0].upper() + op.module_name[1:])
|
||||
|
||||
title = "%s Operators" % (op.module_name[0].upper() + op.module_name[1:])
|
||||
fw("%s\n%s\n\n" % (title, "=" * len(title)))
|
||||
|
||||
|
||||
fw(".. module:: bpy.ops.%s\n\n" % op.module_name)
|
||||
last_mod = op.module_name
|
||||
|
||||
args_str = ", ".join(prop.get_arg_default(force=True) for prop in op.args)
|
||||
fw(".. function:: %s(%s)\n\n" % (op.func_name, args_str))
|
||||
|
||||
# if the description isn't valid, we output the standard warning
|
||||
# if the description isn't valid, we output the standard warning
|
||||
# with a link to the wiki so that people can help
|
||||
if not op.description or op.description == "(undocumented operator)":
|
||||
operator_description = undocumented_message('bpy.ops',op.module_name,op.func_name)
|
||||
operator_description = undocumented_message('bpy.ops', op.module_name, op.func_name)
|
||||
else:
|
||||
operator_description = op.description
|
||||
|
||||
@ -815,12 +807,13 @@ def rna2sphinx(BASEPATH):
|
||||
|
||||
location = op.get_location()
|
||||
if location != (None, None):
|
||||
fw(" :file: `%s <%s/%s>`_:%d\n\n" % (location[0],API_BASEURL,location[0],location[1]))
|
||||
|
||||
fw(" :file: `%s <%s/%s>`_:%d\n\n" % (location[0], API_BASEURL, location[0], location[1]))
|
||||
|
||||
write_ops()
|
||||
|
||||
file.close()
|
||||
|
||||
|
||||
def main():
|
||||
import bpy
|
||||
if 'bpy' not in dir():
|
||||
@ -830,9 +823,9 @@ def main():
|
||||
import shutil
|
||||
|
||||
script_dir = os.path.dirname(__file__)
|
||||
path_in = os.path.join(script_dir,'sphinx-in')
|
||||
path_out = os.path.join(script_dir,'sphinx-out')
|
||||
path_examples = os.path.join(script_dir,'examples')
|
||||
path_in = os.path.join(script_dir, "sphinx-in")
|
||||
path_out = os.path.join(script_dir, "sphinx-out")
|
||||
path_examples = os.path.join(script_dir, "examples")
|
||||
# only for partial updates
|
||||
path_in_tmp = path_in + "-tmp"
|
||||
|
||||
@ -843,7 +836,6 @@ def main():
|
||||
if f.endswith(".py"):
|
||||
EXAMPLE_SET.add(os.path.splitext(f)[0])
|
||||
|
||||
|
||||
# only for full updates
|
||||
if _BPY_FULL_REBUILD:
|
||||
shutil.rmtree(path_in, True)
|
||||
@ -860,7 +852,7 @@ def main():
|
||||
# now move changed files from 'path_in_tmp' --> 'path_in'
|
||||
file_list_path_in = set(os.listdir(path_in))
|
||||
file_list_path_in_tmp = set(os.listdir(path_in_tmp))
|
||||
|
||||
|
||||
# remove deprecated files that have been removed.
|
||||
for f in sorted(file_list_path_in):
|
||||
if f not in file_list_path_in_tmp:
|
||||
@ -876,14 +868,13 @@ def main():
|
||||
if f in file_list_path_in:
|
||||
if filecmp.cmp(f_from, f_to):
|
||||
do_copy = False
|
||||
|
||||
|
||||
if do_copy:
|
||||
print("\tupdating: %s" % f)
|
||||
shutil.copy(f_from, f_to)
|
||||
'''else:
|
||||
print("\tkeeping: %s" % f) # eh, not that useful'''
|
||||
|
||||
|
||||
EXAMPLE_SET_UNUSED = EXAMPLE_SET - EXAMPLE_SET_USED
|
||||
if EXAMPLE_SET_UNUSED:
|
||||
print("\nUnused examples found in '%s'..." % path_examples)
|
||||
|
@ -18,6 +18,8 @@
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
"""
|
||||
Thumbnailer runs with python 2.6 and 3.x.
|
||||
To run automatically with nautilus:
|
||||
|
@ -1,7 +1,29 @@
|
||||
# Built-In Keying Sets
|
||||
# None of these Keying Sets should be removed, as these
|
||||
# are needed by various parts of Blender in order for them
|
||||
# to work correctly.
|
||||
# ##### 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>
|
||||
|
||||
"""
|
||||
Built-In Keying Sets
|
||||
None of these Keying Sets should be removed, as these
|
||||
are needed by various parts of Blender in order for them
|
||||
to work correctly.
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from keyingsets_utils import *
|
||||
@ -9,6 +31,7 @@ from keyingsets_utils import *
|
||||
###############################
|
||||
# Built-In KeyingSets
|
||||
|
||||
|
||||
# Location
|
||||
class BUILTIN_KSI_Location(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Location"
|
||||
@ -19,9 +42,10 @@ class BUILTIN_KSI_Location(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator - use callback for location
|
||||
# generator - use callback for location
|
||||
generate = RKS_GEN_location
|
||||
|
||||
|
||||
# Rotation
|
||||
class BUILTIN_KSI_Rotation(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Rotation"
|
||||
@ -32,9 +56,10 @@ class BUILTIN_KSI_Rotation(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator - use callback for location
|
||||
# generator - use callback for location
|
||||
generate = RKS_GEN_rotation
|
||||
|
||||
|
||||
# Scale
|
||||
class BUILTIN_KSI_Scaling(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Scaling"
|
||||
@ -45,11 +70,12 @@ class BUILTIN_KSI_Scaling(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator - use callback for location
|
||||
# generator - use callback for location
|
||||
generate = RKS_GEN_scaling
|
||||
|
||||
# ------------
|
||||
|
||||
|
||||
# LocRot
|
||||
class BUILTIN_KSI_LocRot(bpy.types.KeyingSetInfo):
|
||||
bl_label = "LocRot"
|
||||
@ -60,13 +86,14 @@ class BUILTIN_KSI_LocRot(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator
|
||||
# generator
|
||||
def generate(self, context, ks, data):
|
||||
# location
|
||||
RKS_GEN_location(self, context, ks, data)
|
||||
# rotation
|
||||
RKS_GEN_rotation(self, context, ks, data)
|
||||
|
||||
|
||||
# LocScale
|
||||
class BUILTIN_KSI_LocScale(bpy.types.KeyingSetInfo):
|
||||
bl_label = "LocScale"
|
||||
@ -77,13 +104,14 @@ class BUILTIN_KSI_LocScale(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator
|
||||
# generator
|
||||
def generate(self, context, ks, data):
|
||||
# location
|
||||
RKS_GEN_location(self, context, ks, data)
|
||||
# scale
|
||||
RKS_GEN_scaling(self, context, ks, data)
|
||||
|
||||
|
||||
# LocRotScale
|
||||
class BUILTIN_KSI_LocRotScale(bpy.types.KeyingSetInfo):
|
||||
bl_label = "LocRotScale"
|
||||
@ -94,7 +122,7 @@ class BUILTIN_KSI_LocRotScale(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator
|
||||
# generator
|
||||
def generate(self, context, ks, data):
|
||||
# location
|
||||
RKS_GEN_location(self, context, ks, data)
|
||||
@ -103,6 +131,7 @@ class BUILTIN_KSI_LocRotScale(bpy.types.KeyingSetInfo):
|
||||
# scale
|
||||
RKS_GEN_scaling(self, context, ks, data)
|
||||
|
||||
|
||||
# RotScale
|
||||
class BUILTIN_KSI_RotScale(bpy.types.KeyingSetInfo):
|
||||
bl_label = "RotScale"
|
||||
@ -113,15 +142,16 @@ class BUILTIN_KSI_RotScale(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator
|
||||
# generator
|
||||
def generate(self, context, ks, data):
|
||||
# rotation
|
||||
RKS_GEN_rotation(self, context, ks, data)
|
||||
# scaling
|
||||
RKS_GEN_scaling(self, context, ks, data)
|
||||
|
||||
|
||||
# ------------
|
||||
|
||||
|
||||
# Location
|
||||
class BUILTIN_KSI_VisualLoc(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Visual Location"
|
||||
@ -134,9 +164,10 @@ class BUILTIN_KSI_VisualLoc(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator - use callback for location
|
||||
# generator - use callback for location
|
||||
generate = RKS_GEN_location
|
||||
|
||||
|
||||
# Rotation
|
||||
class BUILTIN_KSI_VisualRot(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Visual Rotation"
|
||||
@ -149,9 +180,10 @@ class BUILTIN_KSI_VisualRot(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator - use callback for rotation
|
||||
# generator - use callback for rotation
|
||||
generate = RKS_GEN_rotation
|
||||
|
||||
|
||||
# VisualLocRot
|
||||
class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Visual LocRot"
|
||||
@ -164,7 +196,7 @@ class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo):
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator
|
||||
# generator
|
||||
def generate(self, context, ks, data):
|
||||
# location
|
||||
RKS_GEN_location(self, context, ks, data)
|
||||
@ -173,39 +205,41 @@ class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo):
|
||||
|
||||
# ------------
|
||||
|
||||
|
||||
# Available
|
||||
class BUILTIN_KSI_Available(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Available"
|
||||
|
||||
# poll - use predefined callback for selected objects
|
||||
# TODO: this should really check whether the selected object (or datablock)
|
||||
# TODO: this should really check whether the selected object (or datablock)
|
||||
# has any animation data defined yet
|
||||
poll = RKS_POLL_selected_objects
|
||||
|
||||
# iterator - use callback for selected bones/objects
|
||||
iterator = RKS_ITER_selected_item
|
||||
|
||||
# generator - use callback for doing this
|
||||
# generator - use callback for doing this
|
||||
generate = RKS_GEN_available
|
||||
|
||||
###############################
|
||||
###############################
|
||||
|
||||
|
||||
# All properties that are likely to get animated in a character rig
|
||||
class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
|
||||
bl_label = "Whole Character"
|
||||
|
||||
|
||||
# these prefixes should be avoided, as they are not really bones
|
||||
# that animators should be touching (or need to touch)
|
||||
badBonePrefixes = (
|
||||
'DEF',
|
||||
'GEO',
|
||||
'MCH',
|
||||
'DEF',
|
||||
'GEO',
|
||||
'MCH',
|
||||
'ORG',
|
||||
'COR',
|
||||
'VIS',
|
||||
# ... more can be added here as you need in your own rigs ...
|
||||
)
|
||||
|
||||
|
||||
# poll - pose-mode on active object only
|
||||
def poll(ksi, context):
|
||||
return ((context.active_object) and (context.active_object.pose) and
|
||||
@ -221,39 +255,39 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
|
||||
def generate(ksi, context, ks, bone):
|
||||
# loc, rot, scale - only include unlocked ones
|
||||
ksi.doLoc(ks, bone)
|
||||
|
||||
|
||||
if bone.rotation_mode in ('QUATERNION', 'AXIS_ANGLE'):
|
||||
ksi.doRot4d(ks, bone)
|
||||
else:
|
||||
ksi.doRot3d(ks, bone)
|
||||
ksi.doScale(ks, bone)
|
||||
|
||||
|
||||
# custom props?
|
||||
ksi.doCustomProps(ks, bone)
|
||||
|
||||
|
||||
# ----------------
|
||||
|
||||
|
||||
# helper to add some bone's property to the Keying Set
|
||||
def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
|
||||
def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
|
||||
# add the property name to the base path
|
||||
id_path = bone.path_from_id()
|
||||
id_block = bone.id_data
|
||||
|
||||
if prop.startswith('['):
|
||||
|
||||
if prop.startswith('['):
|
||||
# custom properties
|
||||
path = id_path + prop
|
||||
else:
|
||||
else:
|
||||
# standard transforms/properties
|
||||
path = path_add_property(id_path, prop)
|
||||
|
||||
|
||||
# add Keying Set entry for this...
|
||||
if use_groups:
|
||||
ks.paths.add(id_block, path, index, group_method='NAMED', group_name=bone.name)
|
||||
else:
|
||||
ks.paths.add(id_block, path, index)
|
||||
|
||||
|
||||
# ----------------
|
||||
|
||||
|
||||
# location properties
|
||||
def doLoc(ksi, ks, bone):
|
||||
if bone.lock_location == (False, False, False):
|
||||
@ -262,7 +296,7 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
|
||||
for i in range(3):
|
||||
if not bone.lock_location[i]:
|
||||
ksi.addProp(ks, bone, "location", i)
|
||||
|
||||
|
||||
# rotation properties
|
||||
def doRot4d(ksi, ks, bone):
|
||||
# rotation mode affects the property used
|
||||
@ -270,7 +304,7 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
|
||||
prop = "rotation_quaternion"
|
||||
elif bone.rotation_mode == 'AXIS_ANGLE':
|
||||
prop = "rotation_axis_angle"
|
||||
|
||||
|
||||
# add rotation properties if they will
|
||||
if bone.lock_rotations_4d:
|
||||
# can check individually
|
||||
@ -278,16 +312,16 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
|
||||
ksi.addProp(ks, bone, prop)
|
||||
else:
|
||||
if bone.lock_rotation_w == False:
|
||||
ksi.addProp(ks, bone, prop, 0) # w = 0
|
||||
|
||||
ksi.addProp(ks, bone, prop, 0) # w = 0
|
||||
|
||||
for i in range(3):
|
||||
if not bone.lock_rotation[i]:
|
||||
ksi.addProp(ks, bone, prop, i+1) # i+1, since here x,y,z = 1,2,3, and w=0
|
||||
ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x,y,z = 1,2,3, and w=0
|
||||
elif True not in bone.lock_rotation:
|
||||
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
|
||||
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
|
||||
# other than all open unless we keyframe the whole lot
|
||||
ksi.addProp(ks, bone, prop)
|
||||
|
||||
|
||||
def doRot3d(ksi, ks, bone):
|
||||
if bone.lock_rotation == (False, False, False):
|
||||
ksi.addProp(ks, bone, "rotation_euler")
|
||||
@ -295,30 +329,30 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
|
||||
for i in range(3):
|
||||
if not bone.lock_rotation[i]:
|
||||
ksi.addProp(ks, bone, "rotation_euler", i)
|
||||
|
||||
|
||||
# scale properties
|
||||
def doScale(ksi, ks, bone):
|
||||
if bone.lock_scale == (0,0,0):
|
||||
if bone.lock_scale == (0, 0, 0):
|
||||
ksi.addProp(ks, bone, "scale")
|
||||
else:
|
||||
for i in range(3):
|
||||
if not bone.lock_scale[i]:
|
||||
ksi.addProp(ks, bone, "scale", i)
|
||||
|
||||
|
||||
# ----------------
|
||||
|
||||
|
||||
# custom properties
|
||||
def doCustomProps(ksi, ks, bone):
|
||||
# go over all custom properties for bone
|
||||
for prop,val in bone.items():
|
||||
for prop, val in bone.items():
|
||||
# ignore special "_RNA_UI" used for UI editing
|
||||
if prop == "_RNA_UI":
|
||||
continue
|
||||
|
||||
|
||||
# for now, just add all of 'em
|
||||
ksi.addProp(ks, bone, '["%s"]' % (prop))
|
||||
|
||||
###############################
|
||||
###############################
|
||||
|
||||
classes = [
|
||||
BUILTIN_KSI_Location,
|
||||
@ -329,7 +363,7 @@ classes = [
|
||||
BUILTIN_KSI_LocScale,
|
||||
BUILTIN_KSI_LocRotScale,
|
||||
BUILTIN_KSI_RotScale,
|
||||
|
||||
|
||||
BUILTIN_KSI_WholeCharacter,
|
||||
|
||||
BUILTIN_KSI_VisualLoc,
|
||||
@ -354,4 +388,4 @@ def unregister():
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
|
||||
###############################
|
||||
###############################
|
||||
|
@ -1,45 +1,51 @@
|
||||
# This file defines a set of methods that are useful for various
|
||||
# This file defines a set of methods that are useful for various
|
||||
# Relative Keying Set (RKS) related operations, such as: callbacks
|
||||
# for polling, iterator callbacks, and also generate callbacks.
|
||||
# All of these can be used in conjunction with the others.
|
||||
# for polling, iterator callbacks, and also generate callbacks.
|
||||
# All of these can be used in conjunction with the others.
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
|
||||
###########################
|
||||
# General Utilities
|
||||
|
||||
|
||||
# Append the specified property name on the the existing path
|
||||
def path_add_property(path, prop):
|
||||
if len(path):
|
||||
return path + "." + prop;
|
||||
return path + "." + prop
|
||||
else:
|
||||
return prop;
|
||||
return prop
|
||||
|
||||
###########################
|
||||
# Poll Callbacks
|
||||
|
||||
|
||||
# selected objects
|
||||
def RKS_POLL_selected_objects(ksi, context):
|
||||
return context.active_object or len(context.selected_objects);
|
||||
|
||||
return context.active_object or len(context.selected_objects)
|
||||
|
||||
|
||||
# selected bones
|
||||
def RKS_POLL_selected_bones(ksi, context):
|
||||
# we must be in Pose Mode, and there must be some bones selected
|
||||
# we must be in Pose Mode, and there must be some bones selected
|
||||
if (context.active_object) and (context.active_object.mode == 'POSE'):
|
||||
if context.active_pose_bone or len(context.selected_pose_bones):
|
||||
return True;
|
||||
|
||||
# nothing selected
|
||||
return False;
|
||||
return True
|
||||
|
||||
# nothing selected
|
||||
return False
|
||||
|
||||
|
||||
# selected bones or objects
|
||||
def RKS_POLL_selected_items(ksi, context):
|
||||
return RKS_POLL_selected_bones(ksi, context) or RKS_POLL_selected_objects(ksi, context);
|
||||
return RKS_POLL_selected_bones(ksi, context) or RKS_POLL_selected_objects(ksi, context)
|
||||
|
||||
###########################
|
||||
# Iterator Callbacks
|
||||
|
||||
|
||||
# all selected objects or pose bones, depending on which we've got
|
||||
def RKS_ITER_selected_item(ksi, context, ks):
|
||||
if (context.active_object) and (context.active_object.mode == 'POSE'):
|
||||
@ -52,25 +58,26 @@ def RKS_ITER_selected_item(ksi, context, ks):
|
||||
###########################
|
||||
# Generate Callbacks
|
||||
|
||||
|
||||
# 'Available' F-Curves
|
||||
def RKS_GEN_available(ksi, context, ks, data):
|
||||
# try to get the animation data associated with the closest
|
||||
# try to get the animation data associated with the closest
|
||||
# ID-block to the data (neither of which may exist/be easy to find)
|
||||
id_block = data.id_data
|
||||
adt = getattr(id_block, "animation_data", None)
|
||||
|
||||
# there must also be an active action...
|
||||
if adt is None or adt.action is None:
|
||||
return;
|
||||
|
||||
# if we haven't got an ID-block as 'data', try to restrict
|
||||
return
|
||||
|
||||
# if we haven't got an ID-block as 'data', try to restrict
|
||||
# paths added to only those which branch off from here
|
||||
# i.e. for bones
|
||||
if id_block != data:
|
||||
basePath = data.path_from_id()
|
||||
else:
|
||||
basePath = None; # this is not needed...
|
||||
|
||||
basePath = None # this is not needed...
|
||||
|
||||
# for each F-Curve, include a path to key it
|
||||
# NOTE: we don't need to set the group settings here
|
||||
for fcu in adt.action.fcurves:
|
||||
@ -79,19 +86,20 @@ def RKS_GEN_available(ksi, context, ks, data):
|
||||
ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
|
||||
else:
|
||||
ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
|
||||
|
||||
|
||||
# ------
|
||||
|
||||
|
||||
# get ID block and based ID path for transform generators
|
||||
def get_transform_generators_base_info(data):
|
||||
# ID-block for the data
|
||||
# ID-block for the data
|
||||
id_block = data.id_data
|
||||
|
||||
|
||||
# get base path and grouping method/name
|
||||
if isinstance(data, bpy.types.ID):
|
||||
# no path in this case
|
||||
path = ""
|
||||
|
||||
|
||||
# data on ID-blocks directly should get grouped by the KeyingSet
|
||||
grouping = None
|
||||
else:
|
||||
@ -101,29 +109,31 @@ def get_transform_generators_base_info(data):
|
||||
# try to use the name of the data element to group the F-Curve
|
||||
# else fallback on the KeyingSet name
|
||||
grouping = getattr(data, "name", None)
|
||||
|
||||
|
||||
# return the ID-block and the path
|
||||
return id_block, path, grouping
|
||||
|
||||
# Location
|
||||
|
||||
# Location
|
||||
def RKS_GEN_location(ksi, context, ks, data):
|
||||
# get id-block and path info
|
||||
id_block, base_path, grouping = get_transform_generators_base_info(data)
|
||||
|
||||
|
||||
# add the property name to the base path
|
||||
path = path_add_property(base_path, "location")
|
||||
|
||||
|
||||
# add Keying Set entry for this...
|
||||
if grouping:
|
||||
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
|
||||
else:
|
||||
ks.paths.add(id_block, path)
|
||||
|
||||
# Rotation
|
||||
|
||||
# Rotation
|
||||
def RKS_GEN_rotation(ksi, context, ks, data):
|
||||
# get id-block and path info
|
||||
id_block, base_path, grouping= get_transform_generators_base_info(data)
|
||||
|
||||
id_block, base_path, grouping = get_transform_generators_base_info(data)
|
||||
|
||||
# add the property name to the base path
|
||||
# rotation mode affects the property used
|
||||
if data.rotation_mode == 'QUATERNION':
|
||||
@ -132,21 +142,22 @@ def RKS_GEN_rotation(ksi, context, ks, data):
|
||||
path = path_add_property(base_path, "rotation_axis_angle")
|
||||
else:
|
||||
path = path_add_property(base_path, "rotation_euler")
|
||||
|
||||
|
||||
# add Keying Set entry for this...
|
||||
if grouping:
|
||||
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
|
||||
else:
|
||||
ks.paths.add(id_block, path)
|
||||
|
||||
# Scaling
|
||||
|
||||
# Scaling
|
||||
def RKS_GEN_scaling(ksi, context, ks, data):
|
||||
# get id-block and path info
|
||||
id_block, base_path, grouping= get_transform_generators_base_info(data)
|
||||
|
||||
id_block, base_path, grouping = get_transform_generators_base_info(data)
|
||||
|
||||
# add the property name to the base path
|
||||
path = path_add_property(base_path, "scale")
|
||||
|
||||
|
||||
# add Keying Set entry for this...
|
||||
if grouping:
|
||||
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
|
||||
@ -158,6 +169,7 @@ def RKS_GEN_scaling(ksi, context, ks, data):
|
||||
|
||||
classes = []
|
||||
|
||||
|
||||
def register():
|
||||
pass
|
||||
|
||||
|
@ -1,7 +1,28 @@
|
||||
# ##### 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>
|
||||
|
||||
from math import *
|
||||
import bpy
|
||||
from mathutils import *
|
||||
|
||||
|
||||
def main(context):
|
||||
def cleanupEulCurve(fcv):
|
||||
keys = []
|
||||
@ -12,37 +33,38 @@ def main(context):
|
||||
|
||||
for i in range(len(keys)):
|
||||
cur = keys[i]
|
||||
prev = keys[i-1] if i > 0 else None
|
||||
next = keys[i+1] if i < len(keys)-1 else None
|
||||
prev = keys[i - 1] if i > 0 else None
|
||||
next = keys[i + 1] if i < len(keys) - 1 else None
|
||||
|
||||
if prev is None:
|
||||
continue
|
||||
|
||||
th = pi
|
||||
if abs(prev[1][1] - cur[1][1]) >= th: # more than 180 degree jump
|
||||
fac = pi*2
|
||||
if abs(prev[1][1] - cur[1][1]) >= th: # more than 180 degree jump
|
||||
fac = pi * 2.0
|
||||
if prev[1][1] > cur[1][1]:
|
||||
while abs(cur[1][1]-prev[1][1]) >= th: # < prev[1][1]:
|
||||
while abs(cur[1][1] - prev[1][1]) >= th: # < prev[1][1]:
|
||||
cur[0][1] += fac
|
||||
cur[1][1] += fac
|
||||
cur[2][1] += fac
|
||||
elif prev[1][1] < cur[1][1]:
|
||||
while abs(cur[1][1]-prev[1][1]) >= th:
|
||||
while abs(cur[1][1] - prev[1][1]) >= th:
|
||||
cur[0][1] -= fac
|
||||
cur[1][1] -= fac
|
||||
cur[2][1] -= fac
|
||||
|
||||
for i in range(len(keys)):
|
||||
for x in range(2):
|
||||
fcv.keyframe_points[i].handle_left[x] = keys[i][0][x]
|
||||
fcv.keyframe_points[i].co[x] = keys[i][1][x]
|
||||
fcv.keyframe_points[i].handle_right[x] = keys[i][2][x]
|
||||
fcv.keyframe_points[i].handle_left[x] = keys[i][0][x]
|
||||
fcv.keyframe_points[i].co[x] = keys[i][1][x]
|
||||
fcv.keyframe_points[i].handle_right[x] = keys[i][2][x]
|
||||
|
||||
flist = bpy.context.active_object.animation_data.action.fcurves
|
||||
for f in flist:
|
||||
if f.select and f.data_path.endswith("rotation_euler"):
|
||||
cleanupEulCurve(f)
|
||||
|
||||
|
||||
class DiscontFilterOp(bpy.types.Operator):
|
||||
"""Fixes the most common causes of gimbal lock in the fcurves of the active bone"""
|
||||
bl_idname = "graph.euler_filter"
|
||||
@ -56,9 +78,11 @@ class DiscontFilterOp(bpy.types.Operator):
|
||||
main(context)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
pass
|
||||
|
||||
|
||||
def unregister():
|
||||
pass
|
||||
|
||||
|
@ -14,7 +14,7 @@ else:
|
||||
bpy.context.scene.render.ffmpeg_video_bitrate = 6000
|
||||
bpy.context.scene.render.ffmpeg_maxrate = 9000
|
||||
bpy.context.scene.render.ffmpeg_minrate = 0
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224*8
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224 * 8
|
||||
bpy.context.scene.render.ffmpeg_packetsize = 2048
|
||||
bpy.context.scene.render.ffmpeg_muxrate = 10080000
|
||||
|
||||
|
@ -14,7 +14,7 @@ else:
|
||||
bpy.context.scene.render.ffmpeg_video_bitrate = 2040
|
||||
bpy.context.scene.render.ffmpeg_maxrate = 2516
|
||||
bpy.context.scene.render.ffmpeg_minrate = 0
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224*8
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224 * 8
|
||||
bpy.context.scene.render.ffmpeg_packetsize = 2324
|
||||
bpy.context.scene.render.ffmpeg_muxrate = 0
|
||||
|
||||
|
@ -14,7 +14,7 @@ else:
|
||||
bpy.context.scene.render.ffmpeg_video_bitrate = 1150
|
||||
bpy.context.scene.render.ffmpeg_maxrate = 1150
|
||||
bpy.context.scene.render.ffmpeg_minrate = 1150
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 40*8
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 40 * 8
|
||||
bpy.context.scene.render.ffmpeg_packetsize = 2324
|
||||
bpy.context.scene.render.ffmpeg_muxrate = 2352 * 75 * 8
|
||||
|
||||
|
@ -12,6 +12,6 @@ else:
|
||||
bpy.context.scene.render.ffmpeg_video_bitrate = 6000
|
||||
bpy.context.scene.render.ffmpeg_maxrate = 9000
|
||||
bpy.context.scene.render.ffmpeg_minrate = 0
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224*8
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224 * 8
|
||||
bpy.context.scene.render.ffmpeg_packetsize = 2048
|
||||
bpy.context.scene.render.ffmpeg_muxrate = 10080000
|
||||
|
@ -12,6 +12,6 @@ else:
|
||||
bpy.context.scene.render.ffmpeg_video_bitrate = 6000
|
||||
bpy.context.scene.render.ffmpeg_maxrate = 9000
|
||||
bpy.context.scene.render.ffmpeg_minrate = 0
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224*8
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224 * 8
|
||||
bpy.context.scene.render.ffmpeg_packetsize = 2048
|
||||
bpy.context.scene.render.ffmpeg_muxrate = 10080000
|
||||
|
@ -12,6 +12,6 @@ else:
|
||||
bpy.context.scene.render.ffmpeg_video_bitrate = 6000
|
||||
bpy.context.scene.render.ffmpeg_maxrate = 9000
|
||||
bpy.context.scene.render.ffmpeg_minrate = 0
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224*8
|
||||
bpy.context.scene.render.ffmpeg_buffersize = 224 * 8
|
||||
bpy.context.scene.render.ffmpeg_packetsize = 2048
|
||||
bpy.context.scene.render.ffmpeg_muxrate = 10080000
|
||||
|
@ -1,5 +1,5 @@
|
||||
import bpy
|
||||
material = (bpy.context.material.active_node_material if bpy.context.material.active_node_material else bpy.context.material)
|
||||
|
||||
|
||||
material.subsurface_scattering.radius = 11.605, 3.884, 1.754
|
||||
material.subsurface_scattering.color = 0.430, 0.210, 0.168
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class PhysicButtonsPanel():
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
@ -29,7 +30,8 @@ class PhysicButtonsPanel():
|
||||
def poll(cls, context):
|
||||
rd = context.scene.render
|
||||
return (context.object) and (not rd.use_game_engine)
|
||||
|
||||
|
||||
|
||||
def physics_add(self, layout, md, name, type, typeicon, toggles):
|
||||
sub = layout.row(align=True)
|
||||
if md:
|
||||
@ -41,35 +43,36 @@ def physics_add(self, layout, md, name, type, typeicon, toggles):
|
||||
else:
|
||||
sub.operator("object.modifier_add", text=name, icon=typeicon).type = type
|
||||
|
||||
|
||||
class PHYSICS_PT_add(PhysicButtonsPanel, bpy.types.Panel):
|
||||
bl_label = ""
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
ob = context.object
|
||||
|
||||
|
||||
layout = self.layout
|
||||
layout.label("Enable physics for:")
|
||||
split = layout.split()
|
||||
col = split.column()
|
||||
|
||||
|
||||
if(context.object.field.type == 'NONE'):
|
||||
col.operator("object.forcefield_toggle", text="Force Field", icon='FORCE_FORCE')
|
||||
else:
|
||||
col.operator("object.forcefield_toggle", text="Force Field", icon='X')
|
||||
|
||||
|
||||
if(ob.type == 'MESH'):
|
||||
physics_add(self, col, context.collision, "Collision", 'COLLISION', 'MOD_PHYSICS', False);
|
||||
physics_add(self, col, context.cloth, "Cloth", 'CLOTH', 'MOD_CLOTH', True);
|
||||
|
||||
physics_add(self, col, context.collision, "Collision", 'COLLISION', 'MOD_PHYSICS', False)
|
||||
physics_add(self, col, context.cloth, "Cloth", 'CLOTH', 'MOD_CLOTH', True)
|
||||
|
||||
col = split.column()
|
||||
|
||||
|
||||
if(ob.type == 'MESH' or ob.type == 'LATTICE'or ob.type == 'CURVE'):
|
||||
physics_add(self, col, context.soft_body, "Soft Body", 'SOFT_BODY', 'MOD_SOFT', True);
|
||||
|
||||
physics_add(self, col, context.soft_body, "Soft Body", 'SOFT_BODY', 'MOD_SOFT', True)
|
||||
|
||||
if(ob.type == 'MESH'):
|
||||
physics_add(self, col, context.fluid, "Fluid", 'FLUID_SIMULATION', 'MOD_FLUIDSIM', True);
|
||||
physics_add(self, col, context.smoke, "Smoke", 'SMOKE', 'MOD_SMOKE', True);
|
||||
physics_add(self, col, context.fluid, "Fluid", 'FLUID_SIMULATION', 'MOD_FLUIDSIM', True)
|
||||
physics_add(self, col, context.smoke, "Smoke", 'SMOKE', 'MOD_SMOKE', True)
|
||||
|
||||
|
||||
#cachetype can be 'PSYS' 'HAIR' 'SMOKE' etc
|
||||
|
@ -37,7 +37,7 @@ class PhysicButtonsPanel():
|
||||
|
||||
class PHYSICS_PT_field(PhysicButtonsPanel, bpy.types.Panel):
|
||||
bl_label = "Force Fields"
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
|
@ -39,8 +39,8 @@ class RENDER_MT_framerate_presets(bpy.types.Menu):
|
||||
preset_subdir = "framerate"
|
||||
preset_operator = "script.execute_preset"
|
||||
draw = bpy.types.Menu.draw_preset
|
||||
|
||||
|
||||
|
||||
|
||||
class RenderButtonsPanel():
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
@ -566,20 +566,20 @@ class RENDER_PT_dimensions(RenderButtonsPanel, bpy.types.Panel):
|
||||
fps_rate = round(rd.fps / rd.fps_base)
|
||||
else:
|
||||
fps_rate = round(rd.fps / rd.fps_base, 2)
|
||||
|
||||
|
||||
# TODO: Change the following to iterate over existing presets
|
||||
if (fps_rate in (23.98, 24, 25, 29.97, 30, 50, 59.94, 60)):
|
||||
custom_framerate = False
|
||||
else:
|
||||
custom_framerate = True
|
||||
|
||||
|
||||
if custom_framerate == True:
|
||||
fps_label_text = "Custom (" + str(fps_rate) + " fps)"
|
||||
else:
|
||||
fps_label_text = str(fps_rate) + " fps"
|
||||
|
||||
|
||||
sub.menu("RENDER_MT_framerate_presets", text=fps_label_text)
|
||||
|
||||
|
||||
if (bpy.types.RENDER_MT_framerate_presets.bl_label == "Custom") or (custom_framerate == True):
|
||||
sub.prop(rd, "fps")
|
||||
sub.prop(rd, "fps_base", text="/")
|
||||
|
@ -32,10 +32,10 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False):
|
||||
row = layout.row(align=True)
|
||||
row.prop(dopesheet, "show_only_selected", text="")
|
||||
row.prop(dopesheet, "show_hidden", text="")
|
||||
|
||||
|
||||
if genericFiltersOnly:
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(dopesheet, "show_transforms", text="")
|
||||
|
||||
@ -114,8 +114,8 @@ class DOPESHEET_HT_header(bpy.types.Header):
|
||||
if st.mode == 'DOPESHEET':
|
||||
dopesheet_filter(layout, context)
|
||||
elif st.mode == 'ACTION':
|
||||
# 'genericFiltersOnly' limits the options to only the relevant 'generic' subset of
|
||||
# filters which will work here and are useful (especially for character animation)
|
||||
# 'genericFiltersOnly' limits the options to only the relevant 'generic' subset of
|
||||
# filters which will work here and are useful (especially for character animation)
|
||||
dopesheet_filter(layout, context, genericFiltersOnly=True)
|
||||
|
||||
if st.mode in ('ACTION', 'SHAPEKEY'):
|
||||
|
@ -163,8 +163,8 @@ class NODE_MT_node(bpy.types.Menu):
|
||||
layout.operator("node.show_cyclic_dependencies")
|
||||
layout.operator("node.read_renderlayers")
|
||||
layout.operator("node.read_fullsamplelayers")
|
||||
|
||||
|
||||
|
||||
|
||||
# Node Backdrop options
|
||||
class NODE_PT_properties(bpy.types.Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
@ -175,7 +175,7 @@ class NODE_PT_properties(bpy.types.Panel):
|
||||
def poll(cls, context):
|
||||
snode = context.space_data
|
||||
return snode.tree_type == 'COMPOSITING'
|
||||
|
||||
|
||||
def draw_header(self, context):
|
||||
snode = context.space_data
|
||||
self.layout.prop(snode, "show_backdrop", text="")
|
||||
|
@ -34,6 +34,7 @@ def draw_repeat_tools(context, layout):
|
||||
col.operator("screen.repeat_last")
|
||||
col.operator("screen.repeat_history", text="History...")
|
||||
|
||||
|
||||
# Keyframing tools
|
||||
def draw_keyframing_tools(context, layout):
|
||||
col = layout.column(align=True)
|
||||
@ -42,6 +43,7 @@ def draw_keyframing_tools(context, layout):
|
||||
row.operator("anim.keyframe_insert_menu", text="Insert")
|
||||
row.operator("anim.keyframe_delete_v3d", text="Remove")
|
||||
|
||||
|
||||
# Grease Pencil tools
|
||||
def draw_gpencil_tools(context, layout):
|
||||
col = layout.column(align=True)
|
||||
@ -88,9 +90,9 @@ class VIEW3D_PT_tools_objectmode(View3DPanel, bpy.types.Panel):
|
||||
col.label(text="Shading:")
|
||||
col.operator("object.shade_smooth", text="Smooth")
|
||||
col.operator("object.shade_flat", text="Flat")
|
||||
|
||||
|
||||
draw_keyframing_tools(context, layout)
|
||||
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Motion Paths:")
|
||||
col.operator("object.paths_calculate", text="Calculate Paths")
|
||||
@ -406,7 +408,7 @@ class VIEW3D_PT_tools_posemode(View3DPanel, bpy.types.Panel):
|
||||
col.operator("poselib.pose_add", text="Add To Library")
|
||||
|
||||
draw_keyframing_tools(context, layout)
|
||||
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Motion Paths:")
|
||||
col.operator("pose.paths_calculate", text="Calculate Paths")
|
||||
|
@ -50,6 +50,12 @@ def is_pep8(path):
|
||||
print(path)
|
||||
if open(path, 'rb').read(3) == b'\xef\xbb\xbf':
|
||||
print("\nfile contains BOM, remove first 3 bytes: %r\n" % path)
|
||||
|
||||
# templates dont have a header but should be pep8
|
||||
for d in ("presets", "templates", "examples"):
|
||||
if ("%s%s%s" % (os.sep, d, os.sep)) in path:
|
||||
return 1
|
||||
|
||||
f = open(path, 'r', encoding="utf8")
|
||||
for i in range(PEP8_SEEK_COMMENT):
|
||||
line = f.readline()
|
||||
|
Loading…
Reference in New Issue
Block a user