CGCookie / blender-addon-updater

A module for enabling users to check for add-on updates and install new versions directly from Blender.
GNU General Public License v3.0
247 stars 42 forks source link

Trouble registering the updater #57

Closed Lynchon83 closed 5 years ago

Lynchon83 commented 5 years ago

I'm no a coder so I might be missing or doing something stupid. Right now My addon is basically a collection on different scripts I collect and so I can load them all together every time I need. Sometime ago I found an init example which could register all scipts inside an addon folder by just adding the name of the script into a list, this init file is as follows:

 `bl_info = {
    "name" : "Lynchon Tools 2.80",
    "author" : "Lynchon",
    "description" : "",
    "version" : (1,0), 
    "blender" : (2, 80, 0),
    "location" : "",
    "warning" : "",
    "category" : "Generic"
}

import bpy
from . import addon_updater_ops

modulesNames = ['UI','y_up','xml_parse_particles', 'xml_parse_conformHeight', 'metal_compiler','edit_pivot','uv_tube_unwrap','Hidesato Offset Edges','fillet_280','addon_updater','addon_updater_ops',]

import sys
import importlib

modulesFullNames = {}
for currentModuleName in modulesNames:
    modulesFullNames[currentModuleName] = ('{}.{}'.format(__name__, currentModuleName))

for currentModuleFullName in modulesFullNames.values():
    if currentModuleFullName in sys.modules:
        importlib.reload(sys.modules[currentModuleFullName])
    else:
        globals()[currentModuleFullName] = importlib.import_module(currentModuleFullName)
        setattr(globals()[currentModuleFullName], 'modulesNames', modulesFullNames)

def register():

    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'register'):
                sys.modules[currentModuleName].register()

def unregister():
    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'unregister'):
                sys.modules[currentModuleName].unregister()`

As I try to include the updater (so my coworkers can get any update I do) into the addon problems arrive. My problem comes when I try to register the "addon_updater_ops" bl_info, seems that adding it into my register function in my init doesn't work, it keeps telling me it misses a positional argument even if I already have my bl_info inside the init ( I've also tried importing it from the addon_updater_ops as the tutorial explained).

How should I approach this issue, please ask me if you need any additional info or files.

Thanks in advance

TheDuckCow commented 5 years ago

Hi there, happy to help. The use case you describe is certainly a worthwhile example of using this updater solution.

For your issue, try the modified code below. Notice how I run the registration of the updater code separately from the others (and also removed the updater files from modulesNames); this is because its registration needs to have the bl_info passed in as an argument(to get the true addon version and name), which normally is not needed for registration code and hence the others can still register fine without it.

Let me know if you still have issues after trying this!

 `bl_info = {
    "name" : "Lynchon Tools 2.80",
    "author" : "Lynchon",
    "description" : "",
    "version" : (1,0),
    "blender" : (2, 80, 0),
    "location" : "",
    "warning" : "",
    "category" : "Generic"
}

import bpy
from . import addon_updater_ops

modulesNames = ['UI','y_up','xml_parse_particles', 'xml_parse_conformHeight', 'metal_compiler','edit_pivot','uv_tube_unwrap','Hidesato Offset Edges','fillet_280']

import sys
import importlib

modulesFullNames = {}
for currentModuleName in modulesNames:
    modulesFullNames[currentModuleName] = ('{}.{}'.format(__name__, currentModuleName))

for currentModuleFullName in modulesFullNames.values():
    if currentModuleFullName in sys.modules:
        importlib.reload(sys.modules[currentModuleFullName])
    else:
        globals()[currentModuleFullName] = importlib.import_module(currentModuleFullName)
        setattr(globals()[currentModuleFullName], 'modulesNames', modulesFullNames)

def register():

    # addon updater code and configurations
    addon_updater_ops.register(bl_info)

    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'register'):
                sys.modules[currentModuleName].register()

def unregister():
    # addon updater unregister
    addon_updater_ops.unregister()

    for currentModuleName in modulesFullNames.values():
        if currentModuleName in sys.modules:
            if hasattr(sys.modules[currentModuleName], 'unregister'):
                sys.modules[currentModuleName].unregister()`
Lynchon83 commented 5 years ago

Thank you very much! That solved my registration problem. But right now I'm having trouble drawing the preferences panel, and I'm quite lost because I already have a preferences panel being drawn from one of my scripts so I don't understand why is not working, I've been following the tutorial but with no success, let me show you how I'm trying to draw the preferences panel from my UI script, maybe you see whats missing.

import bpy
from bpy.props import (StringProperty,
                   BoolProperty,
                   IntProperty,
                   FloatProperty,
                   EnumProperty,
                   PointerProperty
                   )
from bpy.types import (Panel,
                   Operator,
                   PropertyGroup
                   )
from . import addon_updater_ops

class MySettings(PropertyGroup):

    path : StringProperty(
        name="path",
        description="Path to Directory",
        default="",
        maxlen=1024,
        subtype='DIR_PATH')

    conform_threshold : FloatProperty(
        name="conform_threshold",
        description="A float property",
        default=0.1,
        min=-5,
        max=30.0)

    invert : BoolProperty(
        name="Enable or Disable",
        description="A simple bool property",
        default = False) 

class UI_PT_LynchonPanel(bpy.types.Panel):
    """Las super herramientas de Juan"""
    bl_label = "Lynchon Tools"
    bl_space_type = 'VIEW_3D'
    bl_region_type = "UI"
    bl_category = "Lynchon Tools"

    def draw(self, context):
        layout = self.layout

        row = layout.row()
        row.operator("object.y_up") 
        row = layout.row()
        row.operator("mesh.simple_edit_pivot") 

        row = layout.row()
        row.operator("uv.tube_uv_unwrap") 

        split = layout.split()

        # First column
        col = split.column()
        col.operator("mesh.hidesato_offset_edges", text='Offset').geometry_mode='offset' 
        col = split.column(align=True)
        # Second column
        col.operator("mesh.hidesato_offset_edges", text='Offset Extrude').geometry_mode='extrude' 
        col = split.column(align=True)
        col.operator("mesh.hidesato_offset_edges_profile", text='Offset with Profile') 

        row = layout.row()
        row.operator("curve.fillet") 

        # split = layout.split()
        # # First column
        # col = split.column()
        # col.operator("texture.metal_compiler")

        # # Second column
        # scn = context.scene
        # mytool = scn.my_tool
        # col = split.column(align=True)
        # col.prop(mytool, 'invert' ,text = "Invert")   

class UI_PT_LynchonMMCPanel(bpy.types.Panel):
    """Las super herramientas de Juan"""
    bl_label = "MMC Tools"
    bl_space_type = 'VIEW_3D'
    bl_region_type = "UI"
    bl_category = "Lynchon Tools"

    def draw(self, context):
        layout = self.layout

        scn = context.scene
        mytool = scn.my_tool

        split = layout.split()

        # First column
        col = split.column()
        col.label(text="Import Venue")
        col.operator( "xml.lowpolygeneratorparticles")

        # Second column, aligned
        col = split.column(align=True)
        col.label(text="Collapse Venue")
        col.operator( "xml.conform_lp_venue")

        # Create two columns, by using a split layout.  
        split = layout.split()

        # First column
        col = split.column()
        col.operator("xml.conformheight")

        # Second column, aligned
        col = split.column(align=True)
        col.prop(mytool, "conform_threshold")
        # root for export
        col = layout.column(align=True)
        col.prop(mytool, "path", text="")

@addon_updater_ops.make_annotations
class UI_PT_LynchoToolsPreferences(bpy.types.AddonPreferences):
    bl_name = __package__
    # addon updater preferences

    auto_check_update = bpy.props.BoolProperty(
        name="Auto-check for Update",
        description="If enabled, auto-check for updates using an interval",
        default=False,
    )

    updater_intrval_months = bpy.props.IntProperty(
        name='Months',
        description="Number of months between checking for updates",
        default=0,
        min=0
    )

    updater_intrval_days = bpy.props.IntProperty(
        name='Days',
        description="Number of days between checking for updates",
        default=7,
        min=0,
        max=31
    )

    updater_intrval_hours = bpy.props.IntProperty(
        name='Hours',
        description="Number of hours between checking for updates",
        default=0,
        min=0,
        max=23
    )

    updater_intrval_minutes = bpy.props.IntProperty(
        name='Minutes',
        description="Number of minutes between checking for updates",
        default=0,
        min=0,
        max=59
    )

    def draw(self, context):
        layout = self.layout
        # col = layout.column() # works best if a column, or even just self.layout
        mainrow = layout.row()
        col = mainrow.column()

        # updater draw function
        # could also pass in col as third arg
        addon_updater_ops.update_settings_ui(self, context)

        # Alternate draw function, which is more condensed and can be
        # placed within an existing draw function. Only contains:
        #   1) check for update/update now buttons
        #   2) toggle for auto-check (interval will be equal to what is set above)
        # addon_updater_ops.update_settings_ui_condensed(self, context, col)

        # Adding another column to help show the above condensed ui as one column
        # col = mainrow.column()
        # col.scale_y = 2
        # col.operator("wm.url_open","Open webpage ").url=addon_updater_ops.updater.website

def register():
    bpy.utils.register_class(UI_PT_LynchonMMCPanel)
    bpy.utils.register_class(UI_PT_LynchoToolsPreferences)
    bpy.utils.register_class(UI_PT_LynchonPanel)
    bpy.utils.register_class(MySettings)
    bpy.types.Scene.my_tool = PointerProperty(type=MySettings)

def unregister():
    bpy.utils.unregister_class(UI_PT_LynchonMMCPanel)
    bpy.utils.unregister_class(UI_PT_LynchonPanel)
    bpy.utils.unregister_class(UI_PT_LynchoToolsPreferences)
    bpy.utils.unregister_class(MySettings)
    del bpy.types.Scene.my_tool
TheDuckCow commented 5 years ago

Glad that helped. At a glance, not sure why your preferences aren't drawing. Based on the code though looks like you're working in 2.8, and I do notice your panel UI_PT_LynchonPanel does not have a bl_idname, which is required; you can just add bl_idname = 'UI_PT_LynchonPanel' as a class-level field (might be : actually instead of =).

Beyond that, the console is your friend see if any errors are printed out that occur higher up that prevent the rest of the script from running. Fallback, place print statements in different functions to see that things are being triggered; e.g. print('Pref draw') atop the preferences draw function, and print('Pref registered') just after the register statement in register(). Of course, remove these print statements once you resolve the issue.

Lynchon83 commented 5 years ago

Thanks again, I solved the drawing issue by placing all the preferences together into the same class and drawing function.