diff --git a/Randomiser_addon.py b/Randomiser_addon.py index 7b32892..2edf93e 100644 --- a/Randomiser_addon.py +++ b/Randomiser_addon.py @@ -17,49 +17,52 @@ # # ***** END GPL LICENCE BLOCK ***** +import logging import bpy, mathutils, random, operator, string #locale no longer needed! from random import Random from bpy.app.handlers import persistent +log = logging.getLogger(__name__) + ### Properties Classes: # Object Properties: class RandomiserObjectProps (bpy.types.PropertyGroup): - use_randomise = bpy.props.BoolProperty(name = "Randomise") - seed = bpy.props.IntProperty(name = "Seed", default = 0) + use_randomise: bpy.props.BoolProperty(name = "Randomise") + seed: bpy.props.IntProperty(name = "Seed", default = 0) #Update Method Properties: - update_method = bpy.props.EnumProperty(name = "Update Method", items = [ + update_method: bpy.props.EnumProperty(name = "Update Method", items = [ ("freq","Frequency","Automatic Based on frame numbers"), ("man","Manual","Manual, based on a value that can be animated, driven, etc.") ]) - time = bpy.props.IntProperty(name = "Time") - offset = bpy.props.IntProperty(name = "Offset") - period = bpy.props.FloatProperty(name = "Frames per Update", default = 1.0, min = 0.01) + time: bpy.props.IntProperty(name = "Time") + offset: bpy.props.IntProperty(name = "Offset") + period: bpy.props.FloatProperty(name = "Frames per Update", default = 1.0, min = 0.01) #Generate Method Properties: - generate_method = bpy.props.EnumProperty(name = "Generate Method", items = [("ordered","Ordered","ordered"),("random","Random","random")]) - source_group = bpy.props.StringProperty(name = "Group", description = "Name of the Group to use for Randomise.") + generate_method: bpy.props.EnumProperty(name = "Generate Method", items = [("ordered","Ordered","ordered"),("random","Random","random")]) + source_collection: bpy.props.StringProperty(name="Collection", description = "Name of the Collection to use for Randomise.") #Previous choice for random choice selections to avoid repeats: - previous_choice = bpy.props.StringProperty(name = "Previous Choice", description = "Stored value of previous random object choice") - no_repeats = bpy.props.BoolProperty(name = "Avoid Repeats", default = False) + previous_choice: bpy.props.StringProperty(name = "Previous Choice", description = "Stored value of previous random object choice") + no_repeats: bpy.props.BoolProperty(name = "Avoid Repeats", default = False) class RandomiserTextProps (bpy.types.PropertyGroup): - use_randomise = bpy.props.BoolProperty(name = "Randomise") - seed = bpy.props.IntProperty(name = "Seed", default = 0) + use_randomise: bpy.props.BoolProperty(name = "Randomise") + seed: bpy.props.IntProperty(name = "Seed", default = 0) #Update Method props: - update_method = bpy.props.EnumProperty(name = "Update Method", items = [ + update_method: bpy.props.EnumProperty(name = "Update Method", items = [ ("freq","Frequency","Automatic Based on frame numbers"), ("man","Manual","Manual, based on a value that can be animated, driven, etc.") ]) - offset = bpy.props.IntProperty(name = "Offset") - time = bpy.props.IntProperty(name = "Time") - period = bpy.props.FloatProperty(name = "Frames per Update", default = 1.0, min = 0.01) + offset: bpy.props.IntProperty(name = "Offset") + time: bpy.props.IntProperty(name = "Time") + period: bpy.props.FloatProperty(name = "Frames per Update", default = 1.0, min = 0.01) #Generate Method Props: - generate_method = bpy.props.EnumProperty(name = "Generate Method", items = [ + generate_method: bpy.props.EnumProperty(name = "Generate Method", items = [ ("ordered","Pick Ordered","ordered"), ("random","Pick Random","random"), ("grow","Typewriter","Grow sentence letter by letter."), @@ -67,7 +70,7 @@ class RandomiserTextProps (bpy.types.PropertyGroup): ("numeric", "Counting", "Count up or countdown."), ("clock", "Clock", "Gives a clock readout.") ]) - textsource = bpy.props.EnumProperty(name = "Text Source", items = [ + textsource: bpy.props.EnumProperty(name = "Text Source", items = [ ("binary","Binary","binary digits"), ("digits","Digits","random digits"), ("characters","Characters","random characters"), @@ -75,63 +78,63 @@ class RandomiserTextProps (bpy.types.PropertyGroup): ("tblines","Text File, Lines","Random lines from a text block."), ("tbchars","Text File, Characters","Random characters from a text block.") ]) - caps = bpy.props.EnumProperty(name = "Case", items = [ + caps: bpy.props.EnumProperty(name = "Case", items = [ ("lc","Lowercase","lowercase"), ("uc","Uppercase","uppercase"), ("ac","Both","both") ]) - textdata = bpy.props.StringProperty(name = "Text Datablock", description = "Name of Text datablock to use.") + textdata: bpy.props.StringProperty(name = "Text Datablock", description = "Name of Text datablock to use.") #Ticker Properties - ticklength = bpy.props.IntProperty(name = "Scroll length", default = 10) - group_digits = bpy.props.BoolProperty(name = "Group Digits", default = False) + ticklength: bpy.props.IntProperty(name = "Scroll length", default = 10) + group_digits: bpy.props.BoolProperty(name = "Group Digits", default = False) #Time Properties: - clock_minutes = bpy.props.IntProperty(name = "Minutes Offset", default = 0, min = 0, max = 59) - clock_hours = bpy.props.IntProperty(name = "Hours Offset", default = 0, min = 0, max = 23) - clock_24hr = bpy.props.BoolProperty(name = "24 Hour Clock", default = True) + clock_minutes: bpy.props.IntProperty(name = "Minutes Offset", default = 0, min = 0, max = 59) + clock_hours: bpy.props.IntProperty(name = "Hours Offset", default = 0, min = 0, max = 23) + clock_24hr: bpy.props.BoolProperty(name = "24 Hour Clock", default = True) #Previous choice for random choice selections to avoid repeats: - previous_choice = bpy.props.StringProperty(name = "Previous Choice", description = "Stored value of previous randomiser character generated.") - no_repeats = bpy.props.BoolProperty(name = "Avoid Repeats", default = False) + previous_choice: bpy.props.StringProperty(name = "Previous Choice", description = "Stored value of previous randomiser character generated.") + no_repeats: bpy.props.BoolProperty(name = "Avoid Repeats", default = False) #Leader Properties: - leader = bpy.props.EnumProperty(name = "Leader",items = [ + leader: bpy.props.EnumProperty(name = "Leader",items = [ ("none","None","none"), ("random","Noise","Taken from noise source."), ("underscore","Underscore","underscore"), ("flash","Underscore Flash","flash") ]) - leader_period = bpy.props.IntProperty(name = "Leader Period", default = 25) + leader_period: bpy.props.IntProperty(name = "Leader Period", default = 25) # Noise Properties: - use_noise = bpy.props.BoolProperty(name = "Noise") + use_noise: bpy.props.BoolProperty(name = "Noise") #Update Properties: - noise_update_method = bpy.props.EnumProperty(name = "Noise Update Method", items = [ + noise_update_method: bpy.props.EnumProperty(name = "Noise Update Method", items = [ ('copy',"Automatic",'Updates at the same rate as the overall string'), ('man',"Manual",'Updates based on a value that can be animated.'), ('freq',"Frequency",'Updates every x number of frames.') ]) - noise_mask_update_method = bpy.props.EnumProperty(name = "Mask Update Method", items = [ + noise_mask_update_method: bpy.props.EnumProperty(name = "Mask Update Method", items = [ ('copy',"Automatic",'Updates at the same rate as the overall string'), ('man',"Manual",'Updates based on a value that can be animated.'), ('freq',"Frequency",'Updates every x number of frames.') ]) - noise_time = bpy.props.IntProperty(name = "Time", default = 0) - noise_period = bpy.props.FloatProperty(name = "Frames per Update", default = 1.0, min = 0.01) - noise_mask_time = bpy.props.IntProperty(name = "Time", default = 0) - noise_mask_period = bpy.props.FloatProperty(name = "Frames per Update",default = 1.0, min = 0.01) + noise_time: bpy.props.IntProperty(name = "Time", default = 0) + noise_period: bpy.props.FloatProperty(name = "Frames per Update", default = 1.0, min = 0.01) + noise_mask_time: bpy.props.IntProperty(name = "Time", default = 0) + noise_mask_period: bpy.props.FloatProperty(name = "Frames per Update",default = 1.0, min = 0.01) - noise_threshold = bpy.props.FloatProperty(name = "Noise Threshold", min = 0.0, max = 1.0) - noise_mask = bpy.props.StringProperty(name = "Mask (comma delimited)") + noise_threshold: bpy.props.FloatProperty(name = "Noise Threshold", min = 0.0, max = 1.0) + noise_mask: bpy.props.StringProperty(name = "Mask (comma delimited)") #Generate Properties: - noise_method = bpy.props.EnumProperty(name = "Noise Generate Method", items = [ + noise_method: bpy.props.EnumProperty(name = "Noise Generate Method", items = [ ("mask","Mask","mask"), ("random","Random","random") ]) - noise_source = bpy.props.EnumProperty(name = "Noise Source", items = [ + noise_source: bpy.props.EnumProperty(name = "Noise Source", items = [ ("digits","Digits","random digits"), ("characters","Characters","random letters"), ("alphanumeric","Alphanumeric","random letters and numbers"), @@ -139,9 +142,9 @@ class RandomiserTextProps (bpy.types.PropertyGroup): ("tbchars", "Text Block, Characters", "Random characters from a text block.") ]) - noise_textdata = bpy.props.StringProperty(name = "Text Datablock", description = "Name of Text datablock to use for Noise.") - noise_ignore_whitespace = bpy.props.BoolProperty(name = "Ignore WhiteSpace", default = True) - noise_ignore_custom = bpy.props.StringProperty(name = "Ignore Custom", description = "Whitelist of characters not to replace with noise.") + noise_textdata: bpy.props.StringProperty(name = "Text Datablock", description = "Name of Text datablock to use for Noise.") + noise_ignore_whitespace: bpy.props.BoolProperty(name = "Ignore WhiteSpace", default = True) + noise_ignore_custom: bpy.props.StringProperty(name = "Ignore Custom", description = "Whitelist of characters not to replace with noise.") # Functions: @@ -215,7 +218,7 @@ def custom_rand(data, method, noisemode, shift, frame, *args): # *args: required args for the method chosen, given as a list [] x = Random(get_iter(data, noisemode, shift, frame) + data.randomiser.seed) methodtocall = getattr(x,method) - #print(args) + log.debug(args) result = methodtocall(*args) return result @@ -232,7 +235,7 @@ def time_to_clock(time): class RandomiseSpreadSeeds(bpy.types.Operator): bl_idname = 'object.randomise_spread_seeds' bl_label = "Spread Randomiser Seeds" - text = bpy.props.BoolProperty(name = "Text", default = False) + text: bpy.props.BoolProperty(name = "Text", default = False) @classmethod def poll(cls,context): @@ -251,13 +254,13 @@ def execute(self, context): try: ob.data.randomiser.seed = count except AttributeError: - print("Couldn't find a seed value for object: " + ob.name) + log.error("Couldn't find a seed value for object: %s", ob.name) pass else: try: ob.randomiser.seed = count except AttributeError: - print("Couldn't find a seed value for object: " + ob.name) + log.error("Couldn't find a seed value for object: %s", ob.name) pass count += 1000 return {'FINISHED'} @@ -265,7 +268,7 @@ def execute(self, context): class RandomiseCopySettings(bpy.types.Operator): bl_idname = 'object.randomise_copy_settings' bl_label = 'Copy Randomiser Settings from Active' - text = bpy.props.BoolProperty(name = "Text", default = False) + text: bpy.props.BoolProperty(name = "Text", default = False) @classmethod def poll(cls,context): @@ -282,20 +285,22 @@ def execute(self, context): try: ob.data.randomiser.seed = active.data.randomiser.seed except AttributeError: - print("Couldn't find a seed value for object: " + ob.name + " or: " + active.name) + log.error("Couldn't find a seed value for object: %s or: %s", + ob.name, active.name) pass else: try: ob.randomiser.seed = active.randomiser.seed except AttributeError: - ("Couldn't find a seed value for object: " + ob.name + " or: " + active.name) + log.error("Couldn't find a seed value for object: %s or %s", + ob.name, active.name) pass return {'FINISHED'} class RandomiseCopySeed(bpy.types.Operator): bl_idname = 'object.randomise_copy_seed' bl_label = "Copy Randomiser Seed from Active" - text = bpy.props.BoolProperty(name = "Text", default = False) + text: bpy.props.BoolProperty(name = "Text", default = False) @classmethod def poll(cls,context): @@ -315,33 +320,37 @@ def execute(self, context): try: ob.data.randomiser.seed = active.data.randomiser.seed except AttributeError: - print("Couldn't find a seed value for object: " + ob.name + " or: " + active.name) + log.info("Couldn't find a seed value for object: %s or: %s", + ob.name, active.name) pass else: try: ob.randomiser.seed = active.randomiser.seed except AttributeError: - ("Couldn't find a seed value for object: " + ob.name + " or: " + active.name) + log.info("Couldn't find a seed value for object: %s or %s", + ob.name, active.name) pass return {'FINISHED'} class RandomiseObjectData (bpy.types.Operator): bl_idname = 'object.randomise_data' bl_label = "Randomise Object Data" - object_string = bpy.props.StringProperty() - frame = bpy.props.IntProperty() + object_string: bpy.props.StringProperty() + frame: bpy.props.IntProperty() def randomise_data(self, object): randomise = object.randomiser generate_method = randomise.generate_method - group_name = randomise.source_group - group = bpy.data.groups[group_name] + collection_name = randomise.source_collection + collection = bpy.data.collections[collection_name] #Get data source and do sanity check: - #print("Source Group name give is: " + group_name) - data_source = [bpy.data.objects[name] for name in sorted(group.objects.keys()) if bpy.data.objects[name].type == object.type] #Sorted and cleaned list of objects in source group + log.debug("Source collection name give is: " + collection_name) + data_source = [bpy.data.objects[name] for name in sorted(collection.objects.keys()) if + # Sorted and cleaned list of objects in source collection + bpy.data.objects[name].type == object.type] if len(data_source) == 0: - print("Data source list is empty. Skipping.") + log.warning("Data source list is empty. Skipping.") return else: if generate_method == 'ordered': @@ -360,7 +369,7 @@ def randomise_data(self, object): previous = object.data.name randomise.previous_choice = previous #Now remove previous from list_clean: - #Check that none of the group members have previous as their object data and if so remove them too. + #Check that none of the collection members have previous as their object data and if so remove them too. for ob in list_clean: if ob.data.name == previous: list_clean.remove(ob) @@ -374,15 +383,15 @@ def execute(self, context): try: object = bpy.data.objects[self.object_string] except TypeError: - print("Couldnt find object :" + self.object_string) + log.error("Couldnt find object: %s", self.object_string) self.randomise_data(object) return {'FINISHED'} class RandomiseTextData (bpy.types.Operator): bl_idname = 'object.randomise_text' bl_label = 'Randomises an objects text data.' - data_string = bpy.props.StringProperty() - frame = bpy.props.IntProperty() + data_string: bpy.props.StringProperty() + frame: bpy.props.IntProperty() def get_textsource(self, source, generate_method, caps, text_block): text_data = "" @@ -458,7 +467,7 @@ def get_textsource(self, source, generate_method, caps, text_block): return text_data else: - print("Hmm, something should have returned by now, return None so as to give you a useful error...") + log.warning("Hmm, something should have returned by now, return None so as to give you a useful error...") return None @@ -565,10 +574,10 @@ def randomise_text(self, data): text_block = None except KeyError: if randomise.textdata == "": - print("ERROR: No name given for text block.") + log.error("ERROR: No name given for text block.") else: - print("ERROR: Cannot find text block with name: " + randomise.textdata) - print("Tip: The Text Block should contain the name of a text block in the Text Editor, NOT a text object.") + log.error("ERROR: Cannot find text block with name: " + randomise.textdata) + log.info("Tip: The Text Block should contain the name of a text block in the Text Editor, NOT a text object.") text_block = None # First get the source text from which to generate new string from: @@ -690,10 +699,12 @@ def randomise_handler(dummy): try: if object.randomiser.use_randomise == True: try: - if object.randomiser.source_group in bpy.data.groups.keys(): + if object.randomiser.source_collection in bpy.data.collections.keys(): to_randomise.append((object, current_frame + subframe)) - except (KeyError, AttributeError): #Key error for key not found. Attr Error for key not given. - print("ERROR:Group not found for object to pick random data from.") + except (KeyError, AttributeError): + # Key error for key not found. Attr Error for key not given. + log.error("Collection %s not found for object to pick random data from.", + object.randomiser.source_collection) pass except AttributeError: pass @@ -730,10 +741,11 @@ def randomise_handler(dummy): try: if object.randomiser.use_randomise == True: try: - if object.randomiser.source_group in bpy.data.groups.keys(): + if object.randomiser.source_collection in bpy.data.collections.keys(): to_randomise.append((object, frame_scene)) except (KeyError, AttributeError): #Key error for key not found. Attr Error for key not given. - print("ERROR:Group not found for object to pick random data from.") + log.error("Collection not found for object to pick random data from.", + object.randomiser.source_collection) pass except AttributeError: pass diff --git a/__init__.py b/__init__.py index c2cd765..41ca423 100644 --- a/__init__.py +++ b/__init__.py @@ -16,13 +16,17 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ***** END GPL LICENCE BLOCK ***** - +import logging +log = logging.getLogger(__name__) +log.setLevel(logging.WARNING) +# log.setLevel(logging.DEBUG) +log.debug('Initiating') bl_info = { "name": "Randomiser", "author": "Ben Simonds", - "version": (0, 3), - "blender": (2, 7, 0), + "version": (0, 4), + "blender": (2, 80, 0), "location": "Properties > Object Data > Randomise", "description": "Tools for randomising and animating text data (and some limited object data). Website: http://bensimonds.com/2014/04/02/randomiser-add-on/", #"warning": "", @@ -32,12 +36,12 @@ } import bpy -from .Randomiser_addon import * +from Randomiser.Randomiser_addon import * # Randomiser UI: -class RandomiserPanelObject(bpy.types.Panel): +class RANDOMISER_PT_panel_object(bpy.types.Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" @@ -70,13 +74,13 @@ def draw(self, context): row = box.row() row.prop(randomise, "generate_method") row = box.row() - row.prop(randomise, "source_group") + row.prop(randomise, "source_collection") if randomise.generate_method == 'random': row = box.row() row.alignment = 'RIGHT' row.prop(randomise, "no_repeats") -class RandomiserPanelText(bpy.types.Panel): +class RANDOMISER_PT_panel_text(bpy.types.Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "data" @@ -239,6 +243,7 @@ def draw(self, context): #Registration: def register(): + log.debug('register') #Properties: bpy.utils.register_class(RandomiserObjectProps) bpy.utils.register_class(RandomiserTextProps) @@ -252,8 +257,8 @@ def register(): bpy.utils.register_class(RandomiseCopySeed) #UI: - bpy.utils.register_class(RandomiserPanelObject) - bpy.utils.register_class(RandomiserPanelText) + bpy.utils.register_class(RANDOMISER_PT_panel_object) + bpy.utils.register_class(RANDOMISER_PT_panel_text) #Handlers bpy.app.handlers.frame_change_post.append(randomise_handler) @@ -261,6 +266,7 @@ def register(): def unregister(): + log.debug('unregister') #Properties: bpy.utils.unregister_class(RandomiserObjectProps) bpy.utils.unregister_class(RandomiserTextProps) @@ -272,8 +278,8 @@ def unregister(): bpy.utils.unregister_class(RandomiseCopySeed) #UI: - bpy.utils.unregister_class(RandomiserPanelObject) - bpy.utils.unregister_class(RandomiserPanelText) + bpy.utils.unregister_class(RANDOMISER_PT_panel_object) + bpy.utils.unregister_class(RANDOMISER_PT_panel_text) #Handlers: bpy.app.handlers.frame_change_post.remove(randomise_handler)