]) def _draw_skin(self, scene, layout): box = layout.box() box.label(text="Skin") ASSET_SETTINGS_PROPERTIES.draw_properties( scene, box, ["skin_type", "material_instances"]) def _draw_eyes(self, scene, layout): box = layout.box() box.label(text="Eyes") ASSET_SETTINGS_PROPERTIES.draw_properties(scene, box, ["procedural_eyes"]) def _packs(self, scene, layout): box = layout.box() box.label(text="Asset packs") box.operator("mpfb.load_pack") def draw(self, context): _LOG.enter() layout = self.layout scene = context.scene self._draw_skin(scene, layout) self._draw_eyes(scene, layout) self._draw_mhclo(scene, layout) self._packs(scene, layout) ClassManager.add_class(MPFB_PT_Asset_Settings_Panel)
class MPFB_PT_Importer_Panel(Abstract_Panel): bl_label = "From MakeHuman" bl_category = UiService.get_value("IMPORTERCATEGORY") bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "MPFB_PT_New_Panel" def draw(self, context): _LOG.enter() layout = self.layout scn = context.scene from mpfb.ui.eyesettings.eyesettingspanel import ensure_eye_settings_default_exists ensure_eye_settings_default_exists() if UiService.get_importer_panel_list() is None: UiService.rebuild_importer_panel_list() if UiService.get_importer_enhanced_settings_panel_list() is None: UiService.rebuild_importer_enhanced_settings_panel_list() if UiService.get_importer_eye_settings_panel_list() is None: UiService.rebuild_importer_eye_settings_panel_list() IMPORTER_PROPERTIES.draw_properties(scn, layout, [ "presets_for_import", "skin_settings_for_import", "eye_settings_for_import" ]) layout.operator('mpfb.importer_import_body') ClassManager.add_class(MPFB_PT_Importer_Panel)
_LOG.warn("Copying from", default_json_template) shutil.copy(default_json_template, default_json) else: _LOG.trace("Default enhanced settings exist") if UiService.get_enhanced_settings_panel_list() is None: UiService.rebuild_enhanced_settings_panel_list() layout = self.layout scene = context.scene self._load_save_box( scene, self._create_box(layout, "Load/save presets", "MODIFIER")) @classmethod def poll(cls, context): obj = context.active_object if not obj: return False if ObjectService.object_is_basemesh_or_body_proxy(obj): return True if ObjectService.object_is_skeleton(obj): return True return False ClassManager.add_class(MPFB_PT_Enhanced_Settings_Panel)
"""UI for opening web links.""" bl_label = "Web resources" bl_category = UiService.get_value("DEVELOPERCATEGORY") bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "MPFB_PT_System_Panel" def _url(self, layout, label, url): weblink = layout.operator("mpfb.web_resource", text=label) weblink.url = url def draw(self, context): _LOG.enter() layout = self.layout self._url(layout, "Project home", "https://github.com/makehumancommunity/mpfb2") self._url( layout, "Documentation", "https://github.com/makehumancommunity/mpfb2/blob/master/docs/README.md" ) self._url(layout, "Get support", "http://www.makehumancommunity.org/forum/") self._url(layout, "Report a bug", "https://github.com/makehumancommunity/mpfb2/issues") self._url( layout, "Asset packs", "http://download.tuxfamily.org/makehuman/asset_packs/index.html") ClassManager.add_class(MPFB_PT_Web_Resources_Panel)
_LOG.debug("Not adding leg helpers") if "finger_helpers_type" in settings and settings[ "finger_helpers_type"] and settings[ "finger_helpers_type"] != "NONE": _LOG.debug("Adding finger helpers:", settings["finger_helpers_type"]) self._finger_helpers(armature_object, settings) else: _LOG.debug("Not adding finger helpers") if "eye_ik" in settings and settings["eye_ik"]: _LOG.debug("Adding eye ik") self._eye_helpers(armature_object, settings) else: _LOG.debug("Not adding eye ik") self.report({'INFO'}, "Helpers were added") return {'FINISHED'} @classmethod def poll(cls, context): _LOG.enter() if context.object is None or context.object.type != 'ARMATURE': return False # TODO: check current mode return True ClassManager.add_class(MPFB_OT_AddHelpersOperator)
return True if ObjectService.object_is_skeleton(obj): return True if ObjectService.object_is_eyes(obj): return True return False def ensure_eye_settings_default_exists(): """Check that the json file with the default eye settings exists in the user config dir. If not, copy it from the data directory.""" _LOG.enter() default_json = LocationService.get_user_config("eye_settings.default.json") if not os.path.exists(default_json): _LOG.warn("The default eye settings do not exist. Will create at", default_json) template_settings = LocationService.get_mpfb_data("settings") default_json_template = os.path.join(template_settings, "eye_settings.default.json") if os.path.exists(default_json_template): _LOG.warn("Copying from", default_json_template) shutil.copy(default_json_template, default_json) else: _LOG.trace("Default eye settings exist") ClassManager.add_class(MPFB_PT_Eye_Settings_Panel)
armature_object = context.object basemesh = ObjectService.find_object_of_type_amongst_nearest_relatives(armature_object, mpfb_type_name="Basemesh") if basemesh is None: self.report({'ERROR'}, "Could not find related base mesh. It should have been parent or child of armature object.") return {'FINISHED'} rig = Rig.from_given_basemesh_and_armature_as_active_object(basemesh) _LOG.dump("final rig_definition", rig.rig_definition) unmatched_bone_names = rig.list_unmatched_bones() unmatched = len(unmatched_bone_names) if unmatched > 0: self.report({'WARNING'}, "There were " + str(unmatched) + " bones that could not be matched to cube or vertex") _LOG.warn("Unmatched bone names:", unmatched_bone_names) absolute_file_path = bpy.path.abspath(self.filepath) _LOG.debug("absolute_file_path", absolute_file_path) with open(absolute_file_path, "w") as json_file: json.dump(rig.rig_definition, json_file, indent=4, sort_keys=True) self.report({'INFO'}, "JSON file written to " + absolute_file_path) return {'FINISHED'} ClassManager.add_class(MPFB_OT_Save_Rig_Operator)
return {'FINISHED'} absolute_file_path = bpy.path.abspath(self.filepath) _LOG.debug("absolute_file_path", absolute_file_path) as_dict = dict() with open(absolute_file_path, "r") as json_file: json_data = json_file.read() import time ts = int(time.time()) json_data = str(json_data).replace("$group_name", "node_group." + str(ts)) as_dict = json.loads(json_data) self.report({'INFO'}, "JSON data read") _LOG.dump("loaded json data", as_dict) material = MaterialService.create_empty_material( "nodes_material", blender_object) NodeService.apply_node_tree_from_dict(material.node_tree, as_dict, True) return {'FINISHED'} ClassManager.add_class(MPFB_OT_Load_Nodes_Operator)
# We will import the body information even if we then opt to not create it self._populate_with_initial_import(importer) _LOG.time("Import took:") # This needs some information that was imported, so can't do it before here self._calculate_necessary_derived_settings(importer) self._construct_basemesh_and_or_rig_if_required(importer) if importer["derived_settings"]["import_any_proxy"]: self._prepare_for_importing_proxies(importer) for proxy_info in importer["temporary_entities"]["proxies_info"]: self._import_proxy_if_requested(importer, proxy_info) self._mask_basemesh_if_proxy_is_available(importer) if importer["settings_from_ui"]["feet_on_ground"] and importer["blender_entities"]["parent"]: importer["blender_entities"]["parent"].location[2] = abs(importer["derived_settings"]["lowest_point"]) self._create_material_instances(importer) self._adjust_skin_material_settings(importer) self._adjust_eye_material_settings(importer) _LOG.time("Entire process took:") self.report({'INFO'}, "Mesh imported") return {'FINISHED'} ClassManager.add_class(MPFB_OT_ImportHumanOperator)
bl_options = {'REGISTER'} def execute(self, context): _LOG.enter() name = IMPORTER_PRESETS_PROPERTIES.get_value("name", entity_reference=context.scene) if not name is None: name = str(name).strip() if name == "" or name is None: self.report({'ERROR'}, "A valid name must be given") return {'FINISHED'} if " " in str(name): self.report({'ERROR'}, "Presets names should not contain spaces") return {'FINISHED'} confdir = LocationService.get_user_config() file_name = os.path.join(confdir, "importer_presets." + name + ".json") if os.path.exists(file_name): self.report({'ERROR'}, "Presets with that name already exist") return {'FINISHED'} excludes = ["available_presets", "name"] IMPORTER_PRESETS_PROPERTIES.serialize_to_json(file_name, entity_reference=context.scene, exclude_keys=excludes) UiService.rebuild_importer_presets_panel_list() UiService.rebuild_importer_panel_list() self.report({'INFO'}, "Presets were written to " + file_name) return {'FINISHED'} ClassManager.add_class(MPFB_OT_SaveNewImporterPresetsOperator)
basemesh = ObjectService.find_object_of_type_amongst_nearest_relatives( context.object, "Basemesh") if basemesh is None: self.report( {'ERROR'}, "Could not find basemesh amongst relatives of selected object") return {'FINISHED'} HumanService.serialize_to_json_file(basemesh, file_name, True) self.report({'INFO'}, "Human saved as " + file_name) return {'FINISHED'} @classmethod def poll(cls, context): obj = context.active_object if not obj: return False if ObjectService.object_is_basemesh_or_body_proxy(obj): return True if ObjectService.object_is_skeleton(obj): return True return False ClassManager.add_class(MPFB_OT_Save_New_Presets_Operator)
from mpfb.services.logservice import LogService from mpfb.services.locationservice import LocationService from mpfb.ui.importerpresets.importerpresetspanel import IMPORTER_PRESETS_PROPERTIES from mpfb._classmanager import ClassManager import bpy _LOG = LogService.get_logger("importeroperators.overwritepresets") class MPFB_OT_OverwriteImporterPresetsOperator(bpy.types.Operator): """This will overwrite the importer presets selected in the dropdown above, using values from the fields below""" bl_idname = "mpfb.importerpresets_overwrite_importer_presets" bl_label = "Overwrite selected presets" bl_options = {'REGISTER'} def execute(self, context): _LOG.enter() name = IMPORTER_PRESETS_PROPERTIES.get_value( "available_presets", entity_reference=context.scene) file_name = LocationService.get_user_config("importer_presets." + name + ".json") excludes = ["available_presets", "name"] IMPORTER_PRESETS_PROPERTIES.serialize_to_json( file_name, entity_reference=context.scene, exclude_keys=excludes) self.report({'INFO'}, "Presets were written to " + file_name) return {'FINISHED'} ClassManager.add_class(MPFB_OT_OverwriteImporterPresetsOperator)
box = self._create_box(layout, "Remove", "ARMATURE_DATA") box.operator("mpfb.remove_helpers") def draw(self, context): _LOG.enter() layout = self.layout scene = context.scene if context.object is None or context.object.type != 'ARMATURE': return armature_object = context.object finger_mode = RigHelpersProperties.get_value("finger_mode", entity_reference=armature_object) leg_mode = RigHelpersProperties.get_value("leg_mode", entity_reference=armature_object) arm_mode = RigHelpersProperties.get_value("arm_mode", entity_reference=armature_object) eye_mode = RigHelpersProperties.get_value("eye_mode", entity_reference=armature_object) if finger_mode or leg_mode or arm_mode or eye_mode: self._remove_helpers(scene, layout) else: self._arm_helpers(scene, layout) self._leg_helpers(scene, layout) self._finger_helpers(scene, layout) self._eye_helpers(scene, layout) self._apply_helpers(scene, layout) ClassManager.add_class(MPFB_PT_RigHelpersPanel)
from mpfb.ui.abstractpanel import Abstract_Panel _LOG = LogService.get_logger("ui.rigpanel") class MPFB_PT_Rig_Panel(Abstract_Panel): bl_label = "Rigging" bl_category = UiService.get_value("MODELCATEGORY") def draw(self, context): _LOG.enter() layout = self.layout scn = context.scene @classmethod def poll(cls, context): obj = context.active_object if not obj: return False if ObjectService.object_is_basemesh_or_body_proxy(obj): return True if ObjectService.object_is_skeleton(obj): return True return False ClassManager.add_class(MPFB_PT_Rig_Panel)
from mpfb.services.logservice import LogService from mpfb.services.locationservice import LocationService from mpfb.ui.importerpresets.importerpresetspanel import IMPORTER_PRESETS_PROPERTIES from mpfb._classmanager import ClassManager import bpy _LOG = LogService.get_logger("importeroperators.loadpresets") class MPFB_OT_LoadImporterPresetsOperator(bpy.types.Operator): """This will load the importer presets selected in the dropdown above, and use these presets to populate the fields below""" bl_idname = "mpfb.importerpresets_load_importer_presets" bl_label = "Load selected presets" bl_options = {'REGISTER'} def execute(self, context): _LOG.enter() name = IMPORTER_PRESETS_PROPERTIES.get_value( "available_presets", entity_reference=context.scene) file_name = LocationService.get_user_config("importer_presets." + name + ".json") IMPORTER_PRESETS_PROPERTIES.deserialize_from_json( file_name, entity_reference=context.scene) self.report({'INFO'}, "Presets were loaded from " + file_name) return {'FINISHED'} ClassManager.add_class(MPFB_OT_LoadImporterPresetsOperator)
return False def execute(self, context): _LOG.enter() _LOG.debug("click") blender_object = context.active_object if len(blender_object.material_slots) < 1: self.report({'ERROR'}, "This object does not have any material") return {'FINISHED'} if len(blender_object.material_slots) > 1: self.report({'ERROR'}, "This object has more than one material") return {'FINISHED'} material = MaterialService.get_material(blender_object) _LOG.debug("material", material) as_dict = NodeService.get_node_tree_as_dict(material.node_tree) _LOG.dump("nodes", as_dict) absolute_file_path = bpy.path.abspath(self.filepath) _LOG.debug("absolute_file_path", absolute_file_path) with open(absolute_file_path, "w") as json_file: json.dump(as_dict, json_file, indent=4, sort_keys=True) self.report({'INFO'}, "JSON file written to " + absolute_file_path) return {'FINISHED'} ClassManager.add_class(MPFB_OT_Save_Nodes_Operator)
bl_idname = "mpfb.export_log" bl_label = "Export log" bl_options = {'REGISTER'} filename_ext = '.txt' def execute(self, context): _LOG.enter() loggers = LogService.get_loggers() scene = context.scene from mpfb.ui.developer.developerpanel import LOG_LEVELS_PROPERTIES # pylint: disable=C0415 logger_name = LOG_LEVELS_PROPERTIES.get_value("available_loggers", entity_reference=scene) if logger_name == "default": input_path = LogService.get_path_to_combined_log_file() else: logger = LogService.get_logger(logger_name) input_path = logger.get_path_to_log_file() output_path = bpy.path.abspath(self.filepath) shutil.copy(input_path, output_path) return {'FINISHED'} ClassManager.add_class(MPFB_OT_Export_Log_Operator)
if "colorMixIn" in values: # This seems to be an enhanced skin material name = material.name if "." in name: (prefix, name) = str(name).split(".", maxsplit=1) if "." in name: (name, number) = str(name).split(".", maxsplit=1) _LOG.debug("final name", name) if name in settings: _LOG.debug("will try to apply settings", settings[name]) NodeService.set_socket_default_values( group_node, settings[name]) self.report({'INFO'}, "Presets were loaded from " + file_name) return {'FINISHED'} @classmethod def poll(cls, context): _LOG.enter() if context.object is None: return False name = ENHANCED_SETTINGS_PROPERTIES.get_value("available_settings", entity_reference=context) if not name: return False return True ClassManager.add_class(MPFB_OT_ApplyEnhancedSettingsOperator)
from mpfb._classmanager import ClassManager from mpfb.services.logservice import LogService from mpfb.services.uiservice import UiService from mpfb.ui.abstractpanel import Abstract_Panel _LOG = LogService.get_logger("ui.newpanel") class MPFB_PT_New_Panel(Abstract_Panel): bl_label = "New human" bl_category = UiService.get_value("MODELCATEGORY") def draw(self, context): _LOG.enter() layout = self.layout scn = context.scene ClassManager.add_class(MPFB_PT_New_Panel)
from mpfb._classmanager import ClassManager from mpfb.services.logservice import LogService from mpfb.services.uiservice import UiService from mpfb.ui.abstractpanel import Abstract_Panel _LOG = LogService.get_logger("ui.assetspanel") class MPFB_PT_Assets_Panel(Abstract_Panel): bl_label = "Apply assets" bl_category = UiService.get_value("MATERIALSCATEGORY") def draw(self, context): _LOG.enter() layout = self.layout scn = context.scene ClassManager.add_class(MPFB_PT_Assets_Panel)
def _load_save_box(self, scene, layout): _LOG.enter() HUMAN_PRESETS_PROPERTIES.draw_properties(scene, layout, ["available_presets"]) layout.operator('mpfb.overwrite_human_presets') HUMAN_PRESETS_PROPERTIES.draw_properties(scene, layout, ["name"]) layout.operator('mpfb.save_new_human_presets') def draw(self, context): _LOG.enter() layout = self.layout scene = context.scene self._load_save_box(scene, self._create_box(layout, "Load/save presets", "MODIFIER")) @classmethod def poll(cls, context): obj = context.active_object if not obj: return False if ObjectService.object_is_basemesh_or_body_proxy(obj): return True if ObjectService.object_is_skeleton(obj): return True return False ClassManager.add_class(MPFB_PT_Human_Presets_Panel)
from mpfb._classmanager import ClassManager from mpfb.services.logservice import LogService from mpfb.services.uiservice import UiService from mpfb.ui.abstractpanel import Abstract_Panel _LOG = LogService.get_logger("ui.materialspanel") class MPFB_PT_Materials_Panel(Abstract_Panel): bl_label = "Materials" bl_category = UiService.get_value("MATERIALSCATEGORY") def draw(self, context): _LOG.enter() layout = self.layout scn = context.scene ClassManager.add_class(MPFB_PT_Materials_Panel)
if leg_mode: self._leg_helpers(armature_object, settings) if arm_mode: self._arm_helpers(armature_object, settings) if eye_mode: self._eye_helpers(armature_object, settings) self.report({'INFO'}, "Helpers were removed") return {'FINISHED'} @classmethod def poll(cls, context): _LOG.enter() if context.object is None or context.object.type != 'ARMATURE': return False armature_object = context.object finger_mode = RigHelpersProperties.get_value("finger_mode", entity_reference=armature_object) leg_mode = RigHelpersProperties.get_value("leg_mode", entity_reference=armature_object) arm_mode = RigHelpersProperties.get_value("arm_mode", entity_reference=armature_object) eye_mode = RigHelpersProperties.get_value("eye_mode", entity_reference=armature_object) return finger_mode or leg_mode or arm_mode or eye_mode ClassManager.add_class(MPFB_OT_RemoveHelpersOperator)
self.report({'ERROR'}, "Must have a selected object") return {'FINISHED'} name = HUMAN_PRESETS_PROPERTIES.get_value("available_presets", entity_reference=context) if not name is None: name = str(name).strip() if name == "" or name is None: self.report({'ERROR'}, "Presets must be chosen from the list") return {'FINISHED'} confdir = LocationService.get_user_config() file_name = os.path.join(confdir, "human." + name + ".json") return {'FINISHED'} @classmethod def poll(cls, context): obj = context.active_object if not obj: return False if ObjectService.object_is_basemesh_or_body_proxy(obj): return True if ObjectService.object_is_skeleton(obj): return True return False ClassManager.add_class(MPFB_OT_Overwrite_Human_Presets_Operator)
import bpy, os _LOG = LogService.get_logger("ui.loadclothespanel") _LOC = os.path.dirname(__file__) LOAD_CLOTHES_PROPERTIES_DIR = os.path.join(_LOC, "properties") LOAD_CLOTHES_PROPERTIES = SceneConfigSet.from_definitions_in_json_directory( LOAD_CLOTHES_PROPERTIES_DIR, prefix="LC_") class MPFB_PT_Load_Clothes_Panel(Abstract_Panel): """UI for loading MHCLO files.""" bl_label = "Load MHCLO" bl_category = UiService.get_value("CLOTHESCATEGORY") bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "MPFB_PT_Assets_Panel" def draw(self, context): _LOG.enter() layout = self.layout scene = context.scene LOAD_CLOTHES_PROPERTIES.draw_properties(scene, layout, [ "object_type", "material_type", "fit_to_body", "delete_group", "specific_delete_group", "set_up_rigging", "interpolate_weights", "makeclothes_metadata" ]) layout.operator("mpfb.load_clothes") ClassManager.add_class(MPFB_PT_Load_Clothes_Panel)
group_node) if "IrisMinorColor" in material_settings: _LOG.debug("will try to apply settings", settings) NodeService.set_socket_default_values(group_node, settings) else: self.report( {'ERROR'}, "Eyes do not seem to have procedural eyes material") return {'FINISHED'} else: self.report({'ERROR'}, "Eyes do not seem to have procedural eyes material") return {'FINISHED'} self.report({'INFO'}, "Settings were loaded from " + file_name) return {'FINISHED'} @classmethod def poll(cls, context): _LOG.enter() if context.object is None: return False name = EYE_SETTINGS_PROPERTIES.get_value("available_settings", entity_reference=context) if not name: return False return True ClassManager.add_class(MPFB_OT_ApplyEyeSettingsOperator)
_LOG = LogService.get_logger("loglevels.operators.listloglevels") class MPFB_OT_List_Log_Levels_Operator(bpy.types.Operator): """List log levels to the console""" bl_idname = "mpfb.list_log_levels" bl_label = "List log levels" bl_options = {'REGISTER'} def execute(self, context): _LOG.enter() print("default".ljust(40, '.') + ": " + LogService.LOGLEVELS[LogService.get_default_log_level()]) loggers = LogService.get_loggers() logger_names = list(loggers.keys()) logger_names.sort() for name in logger_names: logger = loggers[name] level = "(default)" if logger.level_is_overridden: level = logger.level level = LogService.LOGLEVELS[level] print(name.ljust(40, '.') + ": " + level) self.report({"INFO"}, "Levels were printed to the console") return {'FINISHED'} ClassManager.add_class(MPFB_OT_List_Log_Levels_Operator)
bpy.ops.armature.rigify_add_bone_groups() bpy.ops.pose.rigify_layer_init() armature_object.data.rigify_colors_lock = rigify_ui[ "rigify_colors_lock"] armature_object.data.rigify_selection_colors.select = rigify_ui[ "selection_colors"]["select"] armature_object.data.rigify_selection_colors.active = rigify_ui[ "selection_colors"]["active"] i = 0 for color in armature_object.data.rigify_colors: col = rigify_ui["colors"][i] color.name = col["name"] color.normal = col["normal"] i = i + 1 i = 0 for rigify_layer in armature_object.data.rigify_layers: layer = rigify_ui["layers"][i] rigify_layer.name = layer["name"] rigify_layer.row = layer["row"] rigify_layer.selset = layer["selset"] rigify_layer.group = layer["group"] i = i + 1 return {'FINISHED'} ClassManager.add_class(MPFB_OT_Load_Rigify_Layers_Operator)
"""This will overwrite the selected eye material settings, using values from the selected object's material""" bl_idname = "mpfb.overwrite_eye_settings" bl_label = "Overwrite settings" bl_options = {'REGISTER'} def execute(self, context): _LOG.enter() if context.object is None: self.report({'ERROR'}, "Must have a selected object") return {'FINISHED'} name = EYE_SETTINGS_PROPERTIES.get_value("available_settings", entity_reference=context) if not name is None: name = str(name).strip() if name == "" or name is None: self.report({'ERROR'}, "Settings must be chosen from the list") return {'FINISHED'} confdir = LocationService.get_user_config() file_name = os.path.join(confdir, "eye_settings." + name + ".json") return _save_material(self, context, file_name) @classmethod def poll(cls, context): _LOG.enter() return not context.object is None ClassManager.add_class(MPFB_OT_OverwriteEyeSettingsOperator)
_LOG.enter() _LOG.debug("click") blender_object = context.active_object bpy.ops.object.mode_set(mode='EDIT', toggle=False) ball_r = RigService.find_edit_bone_by_name("ball_r", blender_object) bpy.ops.object.mode_set(mode='OBJECT', toggle=False) if not ball_r: self.report( {'ERROR'}, "Only the \"Game engine\" skeleton is supported so far") return {'FINISHED'} bpy.ops.object.transform_apply(location=True, scale=False, rotation=False) from mpfb.ui.rigify.rigifypanel import RIGIFY_PROPERTIES settings = RIGIFY_PROPERTIES.as_dict(entity_reference=context.scene) helpers = RigifyHelpers.get_instance(settings) helpers.convert_to_rigify(blender_object) self.report({'INFO'}, "Converted to rigify") return {'FINISHED'} ClassManager.add_class(MPFB_OT_Convert_To_Rigify_Operator)