1
$\begingroup$

I have an UIList in which I display a PropertyGroup defined like this:

class CUSTOM_PG_library_list(bpy.types.PropertyGroup):
  lib_name: bpy.props.StringProperty(name ="lib_name")
  lib_last_mod: bpy.props.IntProperty(name ="lib_last_mod")
  lib_active: bpy.props.BoolProperty(name="lib_active", default=False)
    
class CUSTOM_UL_items(UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
        checkbox = "CHECKBOX_HLT" if item.lib_active else "CHECKBOX_DEHLT"
        split = layout.split(factor=0.1)
        split.prop(item, "lib_active", text="", emboss=False, icon=checkbox)
        split.label(text=item.lib_name)

I never worked with UILists so I have a bit of a hard time to understand how they work.

In the UiList, I would like to be able to only set one checkmark at any time. (Switching the others off). Unfortunately, toggling the checkmark on or off is a different action as selecting one line in the UIList.

Is it possible to bind the click on an UIList line (name or checkmark doesn't matter) together? So if I click on a line it toggles the checkmark belonging to that line to on but also turns all other off. (One needs to be selected, so there can not be a state where all are off.)

How can I achieve this? Do I need an update function to lib_active or some sort of an update function to the UIList itself?

EDIT: Here is the call from the UI:

layout.template_list("CUSTOM_UL_items", "", self, "lib_list", self, "lib_list_index", rows=2)

The look should be like this:

enter image description here

What I would like to avoid is this:

enter image description here

(UIList selection and Set Checkmark are mismatched.)

I don't know how to "synchronize" the click on a list with the state of a checkmark. For example, I can toggle the checkmark without selecting the list, which is also not what I want. Checkmark and active line in UIList should be always the same.

$\endgroup$
4
  • $\begingroup$ if i understood you right, you don't want a checkmark, but more an enum value, is that right? $\endgroup$
    – Chris
    Commented Apr 22 at 12:54
  • $\begingroup$ Note the data.active_propname value is changed when you click on a UI list element. Where do you store it ? How do you call the ui list template in your layout code ? $\endgroup$
    – Gorgious
    Commented Apr 22 at 13:25
  • $\begingroup$ I updated the question with the UI call and with some clarification on what I want. @Chris : No, I want the checkmark but always in sync with the selected UIList line. I have some more data in the PropertyGroup which will be displayed, so it is not just the name and the checkmark. $\endgroup$
    – Steve
    Commented Apr 22 at 14:33
  • $\begingroup$ @Gorgious This UIList is shown in my Add-On's preferences. The question is a follow up to my other question: blender.stackexchange.com/questions/316781/… Dealing with Preferences in combination with UIList is very hard for a beginner as there is very little documentation especially on UILists. Outside of the preferences where the SCENE is used for data it is much easier. $\endgroup$
    – Steve
    Commented Apr 23 at 7:38

1 Answer 1

0
$\begingroup$

preferences.lib_list_index is the index of the selected item in your UI list.

You can directly use that to display a checkbox when drawing your item.

class CUSTOM_UL_items(UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        # The for loop is used to retrieve the current item index in the list
        for i, list_item in enumerate(data.lib_list):
            if list_item == item:
                break
        split = layout.split(factor=0.1)
        checkbox = "CHECKBOX_HLT" if i == getattr(active_data, active_propname) else "CHECKBOX_DEHLT"
        split.label(text="", icon=checkbox)
        split.label(text=item.lib_name)
$\endgroup$
10
  • $\begingroup$ Thank you! When I test it, I get the index number of the selected item instead of a checkbox. What do I do wrong? Also, could you explain what the for loop does? Do active_data and active_propname contain the selected line in the UIList? $\endgroup$
    – Steve
    Commented Apr 23 at 12:54
  • $\begingroup$ So, the for is to loop trough the amount of entries in lib_list and it breaks when it is the same position as the index from the UIList active item. This I think I understand, but i is an integer. Does getattr gives back a "positional" integer where it finds the active_propname in the active_data? $\endgroup$
    – Steve
    Commented Apr 23 at 13:26
  • $\begingroup$ @Steve you understood well. I made a mistake, layout.prop can't show an icon IIRC for an integer field. You can simply set a label with the icon. See my modified answer $\endgroup$
    – Gorgious
    Commented Apr 24 at 5:57
  • $\begingroup$ Fantastic, thanks! I put a "print(active_data, active_propname)" statement inbetween and it showed lib_list_index as active_propname. I just didn't know how to handle it and change it to lib_active or any other way to display the checkmark. Your modified solution is very simple and elegant! I keep forgetting that the draw_item function is a constant loop. $\endgroup$
    – Steve
    Commented Apr 24 at 12:28
  • $\begingroup$ I'm still confused by the UIList... :-( Where can I put additional code that should be executed when the user clicks on one of the rows? I need to set lib_active in my Property_Group to True on the one item the user clicked but to false on all other items. The code in the UIList is a constant loop, so there is no point constantly setting some variables there. Is there a function that is called when a user clicked on a list item? $\endgroup$
    – Steve
    Commented Apr 24 at 12:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .