How do I update (resize) a template list when an item is clicked to be expanded?

I initially asked this question on blender-stack_exchange here:


But everyone is focused on my folder tree rather than answering the question…
I want a folder selection in my pop-up window, nothing hard to understand there…

Anyways, could I get some help here please, all I want to do is update a template_list…
I know it’s possible, but I don’t know how.

Thank you. :slight_smile:

Just to understand correctly, you’re saying that the code is working, but you don’t see the changes until you reopen the dialog?

Yeah, but also, if i click the arrows to collapse (without closing the ui) after I know they’re expanded, blender crashes.

Do you think it could possibly just not be redrawing after the changes? You could add a check if that’s the case:


def check(self, context):    
    return True

… and that might even be the cause of the crash as well. Just theorizing here…

Possibly, but how would I make it redraw??
wm.redraw_timer() seems to do nothing, unless I’m just executing it in the wrong context…

You put the code I posted in your operator, like after the execute.

Lol, excuse my noobishness there, sorry :stuck_out_tongue:

So I tried it and blender crashes instantly when I click the arrow… >_>

Hmm. Hard to suggest anything else without seeing more code. You can pm me if you don’t want to share public.

Lol, phones aren’t very friendly when it comes to stuff like that… :stuck_out_tongue:

Hold on, I’ll see what I can do.

I hate phones btw, I’m only forced to use one…
Internet is too expensive for us…

init.py


# -*- coding: utf-8 -*-
'''
CustomSteve 1.3.0 Beta Character Exporter Version 1.0.0 Alpha
'''
bl_info={
        'category': 'Import-Export',
        'name': 'customsteve_char_export',
        'author': 'Tcll5850',
        'version': (1,0,0),
        'blender': (2,7,5),
        'location': 'File > Import-Export',
        'description': 'Export a CustomSteve character',
        'support': 'COMMUNITY',
        }

import sys,subprocess
if 'win' in sys.platform:
    from string import ascii_uppercase
    from ctypes import windll
    drives = ['%s:/'%ascii_uppercase[i] for i,v in enumerate(bin(windll.kernel32.GetLogicalDrives())[:1:-1]) if v=='1']
    del ascii_uppercase
    del windll
else:
    drives = [str(drive.split()[2])[2:-1] for drive in subprocess.Popen( 'mount', shell=True, stdout=subprocess.PIPE ).communicate()[0].split(b'
') if drive.startswith(b'/dev')]


if 'bpy' in locals():
    from importlib import reload
    # noinspection PyUnresolvedReferences,PyUnboundLocalVariable
    reload(O)
    # noinspection PyUnresolvedReferences,PyUnboundLocalVariable
    reload(B)
else:
    import bpy
    from bpy.props import StringProperty, IntProperty, BoolProperty, CollectionProperty, PointerProperty
    from . import O,B

class Exporter_Dialog( bpy.types.Operator ):
    bl_idname = 'export_action.cs_char_dlg'
    bl_label = 'CS Character Exporter'
    
    mcDir = StringProperty( name="Minecraft Directory",
        description="Directory path used for exporting associate files", maxlen=1024, subtype='NONE' )
    
    check = lambda this, context: True
    
    def execute( this, context ):
        
        print('executed')
        return {'FINISHED'}

    def invoke( this, context, event ):
        D = context.scene.CSCEData
        
        B.character_rigs[:] = [] # clear
        
        characters = D.characters
        characters.clear()
        for obj in bpy.data.objects:
            if obj.type!='ARMATURE' or not obj.animation_data: continue
            
            char = characters.add()
            char.name = obj.name

            B.character_rigs.append(obj)
        
        tracks = D.tracks
        tracks.clear()
        for obj in B.character_rigs:
            armature = obj.name
            for track in obj.animation_data.nla_tracks:
                name = track.name
                track = tracks.add()
                track.armature = armature
                track.name = name
        
        B.expanded_directories[:] = [] # clear
        
        B.folders = folders = D.folders
        folders.clear()
        for drive in drives:
            folder = folders.add()
            folder.path = drive
            folder.name = drive.split('/')[-1] or '/'
            folder.level = 0 # drives are the roots
            
        context.window_manager.invoke_props_dialog(this,width=800)
        return {'RUNNING_MODAL'}

    @classmethod
    def menu_func( this, menu, context): menu.layout.operator( this.bl_idname, text='CustomSteve Character', icon='PLUGIN')

    def draw( this, context ):
        layout = this.layout
        D = context.scene.CSCEData
        
        columns = layout.split(.55)
        
        info_column = columns.column()
        
        selection_lists = info_column.split(.50)
        
        character_list = selection_lists.column()
        character_list.label( text='Select Characters:' )
        character_list.template_list( "EXPORT_ACTION_UL_CS_Characters", "", D, "characters", D, "character_index", rows=10, maxrows=10 )
        
        track_list = selection_lists.column()
        track_list.label( text='Select Tracks:' )
        track_list.template_list( "EXPORT_ACTION_UL_CS_Tracks", "", D, "tracks", D, "track_index", rows=10, maxrows=10 )
        
        toggle_subspace = track_list.split( .19 )
        toggle_subspace.label( text='Select' )
        
        buttons = toggle_subspace.row( align=True )
        props = buttons.operator( "scene.track_toggle", text='All' );     props.type = 'all'
        props = buttons.operator( "scene.track_toggle", text='None' );    props.type = 'none'
        props = buttons.operator( "scene.track_toggle", text='Inverse' ); props.type = 'inverse'
        
        # directory tree:
        directory_column = columns.column()
        directory_column.alignment = 'LEFT'
        
        directory_column.prop(this,'mcDir')
        directory_column.template_list( "EXPORT_ACTION_UL_CS_Folders", "", D, "folders", D, "folder_index", rows=20, maxrows=20 )
        
        print(D.folders.__doc__)


def register():
    global registered; registered = True
    bpy.utils.register_module(__name__)
    bpy.types.INFO_MT_file_export.append(Exporter_Dialog.menu_func)
    bpy.types.Scene.CSCEData = PointerProperty( type=B.CSCEData )

def unregister():
    global registered; registered = False
    bpy.utils.unregister_module(__name__)
    bpy.types.INFO_MT_file_export.remove(Exporter_Dialog.menu_func)
    del bpy.types.Scene.CSCEData
    

B.py


# -*- coding: utf-8 -*-

# noinspection PyUnresolvedReferences
from bpy.props import StringProperty,IntProperty,BoolProperty,CollectionProperty,PointerProperty
import bpy, os
from . import drives

character_rigs = []
expanded_directories = []
toggle = True
folders = None

class Selection_Toggle( bpy.types.Operator ):
    bl_idname = "scene.track_toggle"
    bl_label = ""
    bl_option = {'REGISTER'}

    type = StringProperty( name='Selection Type' )

    def invoke( this, context, event ):
        Type = this.properties.type
        CS_Tracks = context.scene.CS_Tracks
        if Type=='all':
            for track in CS_Tracks.tracks: track.export = True
        if Type=='none':
            for track in CS_Tracks.tracks: track.export = False
        if Type=='inverse':
            for track in CS_Tracks.tracks: track.export = not track.export
        return {'RUNNING_MODAL'}

def recurse(directory,level=0):
    global folders
    for entry in os.listdir(directory):
        fullpath=( '%s%s' if directory[-1]=='/' else '%s/%s' )%(directory,entry)
        if os.path.isdir(fullpath):
            folder=folders.add()
            folder.path=fullpath
            folder.name=entry
            folder.level=level
            if fullpath in expanded_directories:
                recurse(fullpath,level+1)
                folder.expand=True

def expand_toggle(folder,context):
    global toggle,folders,expanded_directories
    # NOTE: folder.expand is set before getting here
    if toggle: # this should ONLY be True when folder.expand is clicked (not set)
        
        if folder.expand: expanded_directories.append( folder.path )
        elif folder.path in expanded_directories: expanded_directories.remove( folder.path )
        
        # NOTE: the initial invoke of the dialog class does the same thing as here (no recursion).
        
        folders.clear()  # why doesn't this clear the UI?

        toggle=False # don't update when setting `folder.expand`
        for drive in drives:
            folder=folders.add()
            folder.path=drive
            folder.name=drive.split('/')[-1] or '/'
            folder.level=0 # drives are the roots
            if drive in expanded_directories:
                recurse(drive,1)
                folder.expand=True # this would call this function (recursion depth error)
        toggle=True

def character_toggle(character,context):
    pass

class EXPORT_ACTION_UL_CS_Characters( bpy.types.UIList ):
    def draw_item( self, context, layout, data, item, icon, active_data, active_propname, index ):
        layout.alignment='LEFT'
        layout.prop( item, "export", text=item.name, emboss=False, translate=False, icon='OUTLINER_OB_ARMATURE' )
class Character( bpy.types.PropertyGroup ):
    name = StringProperty( name="Character Name" )
    export = BoolProperty( name="Export", description="Export this character", default=True, update=character_toggle )

class EXPORT_ACTION_UL_CS_Tracks( bpy.types.UIList ):
    def draw_item( self, context, layout, data, item, icon, active_data, active_propname, index ):
        layout.alignment='LEFT'
        layout.prop( item, "export", text='%s : %s'%(item.armature,item.name), emboss=False, translate=False, icon='NLA' )
class Track( bpy.types.PropertyGroup ):
    armature, name = StringProperty( name="Armature Name" ), StringProperty( name="Track Name" )
    export = BoolProperty( name="Export", description="Export this track", default=True )


class EXPORT_ACTION_UL_CS_Folders( bpy.types.UIList ):
    def draw_item( self, context, layout, data, item, icon, active_data, active_propname, index ):
        layout.alignment='LEFT'
        for i in range(item.level): layout.label(text='')
        layout.prop( item, "expand", text='', emboss=False, translate=False, icon='TRIA_DOWN' if item.expand else 'TRIA_RIGHT', icon_only=True, )
        layout.label(text=item.name, icon='FILE_FOLDER' if item.level else 'DISK_DRIVE')
class Folder( bpy.types.PropertyGroup ):
    name=StringProperty( name="Folder Name" )
    path=StringProperty( name="Path" )
    expand=BoolProperty( name="Expand", description="", default=False, update=expand_toggle)
    level=IntProperty()

class CSCEData( bpy.types.PropertyGroup ):
    # template_list wrapper (since collections don't come with a required index property)
    
    characters = CollectionProperty( name="Characters", type=Character, description="Select characters to export" )
    character_index = IntProperty()
    
    tracks = CollectionProperty( name="Tracks", type=Track, description="Select tracks to export" )
    track_index = IntProperty()
    
    folders=CollectionProperty( name="Folders", type=Folder, description="Select a folder" )
    folder_index=IntProperty()


Sorry for multi-posting… if I could keep things to 1 post, I would…

You can ignore O.py in the imports…
All that is, is file Output code.

EDIT: and no, I’m not worried about my code :slight_smile:
It’s to be distributed with CustomSteve anyways when finished, SO yea… :stuck_out_tongue:

Thanks though :slight_smile:

I take it I found a blender bug :stuck_out_tongue:

Lol, phones aren’t very friendly when it comes to stuff like that… :stuck_out_tongue:

Huh?

I just had a chance to test out the code. The behavior I’m getting is I click an arrow and it expands/collapses. The problem comes when I click a folder with permission issues,

PermissionError: [WinError 5] Access is denied: 'C:/Documents and Settings'

or a drive that isn’t ready.

PermissionError: [WinError 21] The device is not ready: 'E:/'

Then the code fails and the arrows stop working until the script is reloaded.

Huh… blender just up and crashes when i click anything in the dialog :confused:

Maybe it’s cause I’m on Linux…

Sucks because I can’t update, unless there’s a .deb installer I’m unaware of :confused:

EDIT: found one :stuck_out_tongue:

EDIT2: nvm, foolishly didn’t read the fine print >.<
It’s an older version than I have

Ok, so I just tried manually updating to the version I had on my laptop being 2.77, but to no avail… The crash still happens…
Even tried on my laptop but it still crashed…

I guess you must be using a Windows version I don’t dare touch… (anything above XP)

If XP is too old, then Linux shouldn’t be used because it’s far older.
Such a shame blender is now using python 3.5, for which the C-API modules don’t work on XP…

Anyways… is there anything I can do to possibly stop blender from crashing??

Here’s my crash log as well:

# Blender 2.77 (sub 0), Commit date: 2016-04-05 18:12, Hash abf6f08
bpy.data.window_managers["WinMan"].addon_search = "char"  # Property

# backtrace
/usr/lib/blender/blender(BLI_system_backtrace+0x1d) [0x15fff0d]
/usr/lib/blender/blender() [0xcb5908]
/lib/x86_64-linux-gnu/libc.so.6(+0x36d40) [0x7fa43cba0d40]
/usr/lib/blender/blender(BLI_findstring+0xa) [0x15ce68a]
/usr/lib/blender/blender(uiTemplateList+0x29d) [0xeac54d]
/usr/lib/blender/blender(UILayout_template_list_call+0x58) [0x1542858]
/usr/lib/blender/blender(RNA_function_call+0x12) [0x14559e2]
/usr/lib/blender/blender() [0x1089ca8]
/usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0(PyObject_Call+0x6a) [0x7fa44634c4ba]
/usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0(PyEval_EvalFrameEx+0x378b) [0x7fa44632cf1b]
/usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0(+0x1f2632) [0x7fa446384632]
/usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0(PyEval_EvalCodeEx+0x48) [0x7fa446384748]
/usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0(+0x1f2cf6) [0x7fa446384cf6]
/usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0(PyObject_Call+0x6a) [0x7fa44634c4ba]
/usr/lib/blender/blender() [0x108a32e]
/usr/lib/blender/blender() [0x155d466]
/usr/lib/blender/blender(uiLayoutOperatorButs+0xad) [0xe9679d]
/usr/lib/blender/blender() [0xccab9f]
/usr/lib/blender/blender(ui_popup_block_refresh+0x76) [0xe9fac6]
/usr/lib/blender/blender() [0xea0ad9]
/usr/lib/blender/blender(ED_region_do_draw+0x99f) [0xf714af]
/usr/lib/blender/blender(wm_draw_update+0x50f) [0xcbc91f]
/usr/lib/blender/blender(WM_main+0x28) [0xcb8198]
/usr/lib/blender/blender(main+0xefa) [0xc7d43a]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fa43cb8bec5]
/usr/lib/blender/blender() [0xcb531f]

Found my problem, and why it worked for you…

I was using PPA-updated distributions which apparently aren’t very reliable…
I’ve just figured out how to install from the site and that worked as it should’ve… so everything works as it’s supposed to now.

Thanks everyone for your help :slight_smile:

Wasn’t much help, and I wouldn’t have figured that out, but I’m glad you got it worked out.