**System Information**
Operating system: Windows 7
Graphics card: GTX 970
**Blender Version**
Broken: 2.80, 6bab905c9d40, blender2.8, 2019-02-24
Worked: 2.7x
**Short description of error**
Redrawing an UIList on a Property Dialog crashes Blender 2.8x
**Exact steps for others to reproduce the error**
Following simple example creates a dialog that allows to display previously selected images from disk via //UIList // using a //PropertyGroup// to store them.
# Run the script to test call //Image List// operator
# Press //Add Files to List//
# Select an arbitrary Image
The code runs without any issues in 2.7x. However in 2.8, after selecting an image from disk (coming back from the //File Browser//) Blender crashes because of supplying `row.template_list(...)` within the draw method. When removing that line the dialog comes back as expected, but without the list of course :). I'm not sure what the issue is. I even don't get any valuable error when running blender in debug mode, just crashes instantly.
//For debugging I also added the same UIList to the Toolbar of the Text Editor, which works as expected.//
**2.8x (Broken)**
```
import bpy
from bpy.props import IntProperty, CollectionProperty, StringProperty
from bpy.types import Operator, Panel, UIList
from bpy_extras.io_utils import ImportHelper
import os
# -------------------------------------------------------------
# Operators
# -------------------------------------------------------------
class ImageSelectOperator(Operator, ImportHelper):
"""Select Images within the File Browser"""
bl_idname = "custom.image_select"
bl_label = "Select Image(s)"
bl_options = {'PRESET'}
filter_glob: StringProperty(
default="*.exr;*.tiff;*.jpeg;*.jpg;*.pic;*.png",
options={'HIDDEN'},
)
files: CollectionProperty(type=bpy.types.PropertyGroup)
def execute(self, context):
scn = context.scene
folder = (os.path.dirname(self.filepath))
# Iterate through the selected files
for i in self.files:
path_to_file = (os.path.join(folder, i.name))
item = scn.custom.add()
item.id = len(scn.custom)
item.name = i.name
item.path = path_to_file
self.report({'INFO'}, "Files added to the List")
# Call the file browser
bpy.ops.custom.popup_dialog('INVOKE_DEFAULT')
return {'FINISHED'}
class UIListActions(Operator):
bl_idname = "custom.list_action"
bl_label = "List Action"
action: bpy.props.EnumProperty(
items=(
('REMOVE', "Remove", ""),
('ADD', "Add", "")
)
)
def invoke(self, context, event):
scn = context.scene
idx = scn.custom_index
try:
item = scn.custom[idx]
except IndexError:
pass
else:
if self.action == 'REMOVE':
info = 'Item %s removed from list' % (scn.custom[scn.custom_index].name)
scn.custom_index -= 1
if scn.custom_index < 0: scn.custom_index = 0
self.report({'INFO'}, info)
scn.custom.remove(idx)
if self.action == 'ADD':
bpy.ops.custom.image_select('INVOKE_DEFAULT')
scn.custom_index = (len(scn.custom))
return {"FINISHED"}
# -------------------------------------------------------------
# Drawing
# -------------------------------------------------------------
class ULItems(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
split = layout.split(factor=0.7)
split.prop(item, "name", text="", emboss=False, translate=False, icon='IMAGE')
split.label(text="Index: %d" % (index))
def invoke(self, context, event):
pass
class PopupDialog(Operator):
"""Displays the Popup Dialog"""
bl_idname = "custom.popup_dialog"
bl_label = "Image List"
bl_options = {'REGISTER'}
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width=400)
def check(self, context):
return True
def draw(self, context):
layout = self.layout
scn = bpy.context.scene
try:
current_item = (scn.custom[scn.custom_index].name)
except IndexError:
current_item = ""
row = layout.row()
"""
Crashes Blender 2.8x
"""
row.template_list("ULItems", "", scn, "custom", scn, "custom_index", rows=2)
row = layout.row()
col = layout.column(align=True)
rowsub = col.row(align=True)
rowsub.operator("custom.list_action", icon='ZOOM_IN', text="Add Files to List").action = 'ADD'
rowsub.operator("custom.list_action", icon='ZOOM_OUT', text="Remove File").action = 'REMOVE'
row = layout.row(align=True)
row.label(text="Active Item is: {}".format(current_item))
# DEBUGGING
class UIListPanelExample(Panel):
"""Creates a Panel in the Text Editor"""
bl_idname = 'OBJECT_PT_my_panel'
bl_space_type = "TEXT_EDITOR"
bl_region_type = "UI"
bl_label = "Custom Image Files"
def draw(self, context):
layout = self.layout
scn = bpy.context.scene
try:
current_item = (scn.custom[scn.custom_index].name)
except IndexError:
current_item = ""
row = layout.row()
row.template_list("ULItems", "", scn, "custom", scn, "custom_index", rows=2)
row = layout.row()
col = layout.column(align=True)
rowsub = col.row(align=True)
rowsub.operator("custom.list_action", icon='ZOOM_IN', text="Add Files to List").action = 'ADD'
rowsub.operator("custom.list_action", icon='ZOOM_OUT', text="Remove File").action = 'REMOVE'
row = layout.row(align=True)
row.label(text="Active Item is: {}".format(current_item))
class CustomProps(bpy.types.PropertyGroup):
#name: StringProperty() -> Initiated by default
id: IntProperty()
path: StringProperty()
# -------------------------------------------------------------------
# Register & Unregister
# -------------------------------------------------------------------
classes = (
CustomProps,
UIListActions,
ImageSelectOperator,
ULItems,
UIListPanelExample, # DEBUGGING
PopupDialog
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.Scene.custom = CollectionProperty(type=CustomProps)
bpy.types.Scene.custom_index = IntProperty()
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
del bpy.types.Scene.custom
del bpy.types.Scene.custom_index
if __name__ == "__main__":
register()
bpy.ops.custom.popup_dialog('INVOKE_DEFAULT')
```
**2.7x (Working)**
```
import bpy
from bpy.props import IntProperty, CollectionProperty, StringProperty
from bpy.types import Operator, Panel, UIList
from bpy_extras.io_utils import ImportHelper
import os
# -------------------------------------------------------------
# Operators
# -------------------------------------------------------------
class ImageSelectOperator(Operator, ImportHelper):
"""Select Images within the File Browser"""
bl_idname = "custom.image_select"
bl_label = "Select Image(s)"
bl_options = {'PRESET'}
filter_glob = StringProperty(
default="*.exr;*.tiff;*.jpeg;*.jpg;*.pic;*.png",
options={'HIDDEN'},
)
files = CollectionProperty(type=bpy.types.PropertyGroup)
def execute(self, context):
scn = context.scene
folder = (os.path.dirname(self.filepath))
# Iterate through the selected files
for i in self.files:
path_to_file = (os.path.join(folder, i.name))
item = scn.custom.add()
item.id = len(scn.custom)
item.name = i.name
item.path = path_to_file
self.report({'INFO'}, "Files added to the List")
# Call the file browser
bpy.ops.custom.popup_dialog('INVOKE_DEFAULT')
return {'FINISHED'}
class UIListActions(Operator):
bl_idname = "custom.list_action"
bl_label = "List Action"
action = bpy.props.EnumProperty(
items=(
('REMOVE', "Remove", ""),
('ADD', "Add", "")
)
)
def invoke(self, context, event):
scn = context.scene
idx = scn.custom_index
try:
item = scn.custom[idx]
except IndexError:
pass
else:
if self.action == 'REMOVE':
info = 'Item %s removed from list' % (scn.custom[scn.custom_index].name)
scn.custom_index -= 1
if scn.custom_index < 0: scn.custom_index = 0
self.report({'INFO'}, info)
scn.custom.remove(idx)
if self.action == 'ADD':
bpy.ops.custom.image_select('INVOKE_DEFAULT')
scn.custom_index = (len(scn.custom))
return {"FINISHED"}
# -------------------------------------------------------------
# Drawing
# -------------------------------------------------------------
class ULItems(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
split = layout.split(percentage=0.7)
split.prop(item, "name", text="", emboss=False, translate=False, icon='IMAGE_COL')
split.label(text="Index: %d" % (index))
def invoke(self, context, event):
pass
class PopupDialog(Operator):
"""Displays the Popup Dialog"""
bl_idname = "custom.popup_dialog"
bl_label = "Image List"
bl_options = {'REGISTER'}
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width=400)
def check(self, context):
return True
def draw(self, context):
layout = self.layout
scn = bpy.context.scene
try:
current_item = (scn.custom[scn.custom_index].name)
except IndexError:
current_item = ""
row = layout.row()
row.template_list("ULItems", "", scn, "custom", scn, "custom_index", rows=2)
row = layout.row()
col = layout.column(align=True)
rowsub = col.row(align=True)
rowsub.operator("custom.list_action", icon='ZOOM_IN', text="Add Files to List").action = 'ADD'
rowsub.operator("custom.list_action", icon='ZOOM_OUT', text="Remove File").action = 'REMOVE'
row = layout.row(align=True)
row.label(text="Active Item is: {}".format(current_item))
# DEBUGGING
class UIListPanelExample(Panel):
"""Creates a Panel in the Text Editor"""
bl_idname = 'OBJECT_PT_my_panel'
bl_space_type = "TEXT_EDITOR"
bl_region_type = "UI"
bl_label = "Custom Image Files"
def draw(self, context):
layout = self.layout
scn = bpy.context.scene
try:
current_item = (scn.custom[scn.custom_index].name)
except IndexError:
current_item = ""
row = layout.row()
row.template_list("ULItems", "", scn, "custom", scn, "custom_index", rows=2)
row = layout.row()
col = layout.column(align=True)
rowsub = col.row(align=True)
rowsub.operator("custom.list_action", icon='ZOOM_IN', text="Add Files to List").action = 'ADD'
rowsub.operator("custom.list_action", icon='ZOOM_OUT', text="Remove File").action = 'REMOVE'
row = layout.row(align=True)
row.label(text="Active Item is: {}".format(current_item))
class CustomProps(bpy.types.PropertyGroup):
#name: StringProperty() -> Initiated by default
id = IntProperty()
path = StringProperty()
# -------------------------------------------------------------------
# Register & Unregister
# -------------------------------------------------------------------
classes = (
CustomProps,
UIListActions,
ImageSelectOperator,
ULItems,
UIListPanelExample, # DEBUGGING
PopupDialog
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.Scene.custom = CollectionProperty(type=CustomProps)
bpy.types.Scene.custom_index = IntProperty()
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
del bpy.types.Scene.custom
del bpy.types.Scene.custom_index
if __name__ == "__main__":
register()
bpy.ops.custom.popup_dialog('INVOKE_DEFAULT')
```
Cheers,
Christian