Using Python to show correct 3D model

I’m experimenting with Python and KiCAD. For documentation, is there a better source than this: KiCad Pcbnew Python Scripting: Main Page ?

On to the question.
My SMD footprints for passive compontents are the same for resistor, capacitor and inductor. The 3D models are included for the 3 components in 1 footprint. Now instead of selecting manually which model to show, I’m writing a little script to select the correct one automatically. This is what I have now:

def select_correct_3d_model(footprint) -> None:

    reference = footprint.GetReference()

    show = ""
    if reference[0] == "R":
        show = "Resistor"
    elif reference[0] == "C":
        show = "Capacitor"
    elif reference[0] == "L":
        show = "Inductor"
    else:
        print(f"{reference}: untouched, has {len(footprint.Models())} 3d models")
        return

    for model in footprint.Models():
        if show in model.m_Filename:
            model.m_Show = True
            print(f"{reference}: selected {model.m_Filename}")
        else:
            model.m_Show = False

My problem:
I can’t seem to set model.m_Show to False. How to do this? Where can I find the setters for this attribute?

model.m_Show = works for me. What’s your KiCad version?

Thanks for your time to test this.

Application: KiCad (64-bit)

Version: (6.0.0), release build

Libraries:
wxWidgets 3.1.5
libcurl/7.78.0-DEV Schannel zlib/1.2.11

Platform: Windows 10 (build 19044), 64-bit edition, 64 bit, Little endian, wxMSW

Build Info:
Date: Dec 24 2021 19:17:02
wxWidgets: 3.1.5 (wchar_t,wx containers)
Boost: 1.76.0
OCC: 7.5.0
Curl: 7.78.0-DEV
ngspice: 35
Compiler: Visual C++ 1929 without C++ ABI

Build settings:
KICAD_USE_OCC=ON
KICAD_SPICE=ON


If I print [model.m_Show for model in footprint.Models()], they stay all on ‘True’

1 Like

That’s a rare sign of appreciation! Kudos!

I always try to appreciate people who help me online, as they forfait some of their free time.

Anyways, I did some more testing, as I had a feeling it might be that footprint.Models() gives copies of the models and no references to the models.

I acknowledged it by this snippet (added some print statements):

def select_correct_3d_model(footprint) -> None:

    reference = footprint.GetReference()

    show = ""
    if reference[0] == "R":
        show = "Resistor"
    elif reference[0] == "C":
        show = "Capacitor"
    elif reference[0] == "L":
        show = "Inductor"
    else:
        print(f"{reference} - {footprint.GetValue()}: untouched, has {len(footprint.Models())} 3d models")
        return

    for model in footprint.Models():
        if show in model.m_Filename:
            model.m_Show = True
            print(f"{reference} - {footprint.GetValue()}: selected {model.m_Filename}")
        else:
            model.m_Show = False

        print(model.m_Show)

    print(f"{[model.m_Show for model in footprint.Models()]}")

    print("")

Which gives me this:

C1 - 50V_0603_10%_2.2nF: selected ${KICAD6_3DMODEL_DIR}/Capacitor_SMD.3dshapes/C_0603_1608Metric.step
True
False
False
[True, True, True]


So, how to get references to the models of the footprint used?

I did (in the Scripting Console)

models = fp.Models()
m = models[0]
m.m_Show = False
pcbnew.Refresh()

And the change was reflected in the KiCad 3D viewer.

1 Like

This was asked on discord recently, I explained why changes to models may not reflect (tldr, it is copied list, not original that you are changing)

Kicad version here matters, it may work fine on nightly.

1 Like

I can’t even reach the models on nightly. Models() returns a non-iterable swig wrapper of std::vector (if I remember correctly; I don’t have v6.99 open ATM). I don’t know enough of the details to say what it does.

You are right. Here’s the fix Fix swig wrapper for footprint 3d models (!1222) · Merge requests · KiCad / KiCad Source Code / kicad · GitLab

@eelik 's code snippet works in v6.00. Also I don’t need to call pcbnew.Refresh() to get the changes in pcbnew.

models = footprint.Models()
for i in range(len(models)):
    if show in models[i].m_Filename:
        models[i].m_Show = True
        print(f"{reference} - {footprint.GetValue()}: selected {Path(models[i].m_Filename).name}")
    else:
        models[i].m_Show = False

@qu1ck that i was thinking. The link doesn’t seem to work (discord loads, but not the message, maybe due to work laptop, I’ll recheck when I get huome).

Thank you both

I was confused for a while because a loop didn’t work when I tried. But it really looks like iterating with for over the Models() result dirctly is the culprit. Your latest loop version works.

1 Like

Yes! I guess my Python knowledge was letting me down. In the end it was not a KiCAD problem but a Python problem.

Test case:

arr = list(range(5))
print(f"{arr=}")

for value in arr:
    value = -1
print(f"{arr=}")

for index in range(len(arr)):
    arr[index] = -1
print(f"{arr=}")

Gives:

arr=[0, 1, 2, 3, 4]
arr=[0, 1, 2, 3, 4]
arr=[-1, -1, -1, -1, -1]

Pretty obvious now looking back at it lol.

Don’t confuse changing loop variable itself and changing variable’s field. If you change the loop variable value you are just changing local variable that at that moment has nothing to do with array contents.

When you change a field of a class that you got from the array both the array element and your loop variable point to the same instance. Changing a field will reflect in the array.

The issue with models is most likely deeper, I haven’t confirmed this definitively but I think this is what’s happening on technical level (copy of my discord messages):

Probably that’s exactly what it is, a copy. Since there is a const and non-const accessor pcbnew/footprint.h · 6.0 · KiCad / KiCad Source Code / kicad · GitLab
swig probably defaults to const and then it’s just a copy swig/std_list.i at master · swig/swig · GitHub

I’m still skeptical about this explanation because it wouldn’t make sense that swig makes a deep copy which would be needed to explain model visibility not updating sometimes but there may be 3d viewer caching involved too, obfuscating the issue (in original problem dev was trying to show models, not hide them like in this thread).