cdelker / schemdraw

MIT License
110 stars 20 forks source link

Pin access to custom elem.Ic not granted. #45

Closed monthiller closed 3 weeks ago

monthiller commented 2 months ago

Is there a reason why I can't reference the pins of the elm.Ic directly: pin = getattr(custom_ic, k) => error pin = custom_ic.A0 => error elm.Line().at(getattr(custom_ic, k) ).length(2).right().label(v) = OK 2 Tests scripts with error and non-error output provided.

Test script1:

import schemdraw
import schemdraw.elements as elm

# This simulate dynamic data that is retrieved from some database.
data = {
    "A1": "input signal 1",
    "A2": "input signal 2",
    "A3": "input signal 3",
}

with schemdraw.Drawing() as d:
        custom_ic = elm.Ic(pins=[
            elm.IcPin(name='A1', side='right'),
            elm.IcPin(name='A2', side='right'),
            elm.IcPin(name='A3', side='right'),
            ])

        for k, v in data.items():
            print(f"{k=}")
            print(f"{v=}")
            pin = getattr(custom_ic, k)
            elm.Line().at(pin).length(2).right().label(v)

output(error):

C:\Users\Joa\Desktop>py test_issue.py
k='A1'
v='input signal 1'
Traceback (most recent call last):
  File "C:\Users\Joa\Desktop\test_issue.py", line 20, in <module>
    pin = getattr(custom_ic, k)
  File "C:\Users\Joa\AppData\Local\Programs\Python\Python310\lib\site-packages\schemdraw\elements\elements.py", line 117, in __getattr__
    raise AttributeError(f'{name} not defined in Element')
AttributeError: A1 not defined in Element

Figure_1

Test script1:

import schemdraw
import schemdraw.elements as elm

# This simulate dynamic data that is retrieved from some database.
data = {
    "A1": "input signal 1",
    "A2": "input signal 2",
    "A3": "input signal 3",
}

with schemdraw.Drawing() as d:
        custom_ic = elm.Ic(pins=[
            elm.IcPin(name='A1', side='right'),
            elm.IcPin(name='A2', side='right'),
            elm.IcPin(name='A3', side='right'),
            ])

        for k, v in data.items():
            print(f"{k=}")
            print(f"{v=}")
        elm.Line().at(getattr(custom_ic,k)).length(2).right().label(v)

output

k='A1'
v='input signal 1'
k='A2'
v='input signal 2'
k='A3'
v='input signal 3'

Figure_1

Your library look pretty amazing. Isn't the point of your library to be able to draw schematics dynamically from external data. I didn't see any example for this use case. The existing example are with hardcoded "labels" and "pin" names...

cdelker commented 2 months ago

Should be fixed by caae92074e520835c0c09c476883f82e14087864.

Long answer: It had to do with the way the context manager adds elements to the drawing. It can't calculate position of an element when it's instantiated, because a later call to at or the like will change it's position. So the context manger queues up the element until it's ready to position - "ready" meaning either 1) another Element was instantiated, or 2) a position attribute was requested but not filled in yet, or 3) the context manager exits. Option 2 means Element overrides getattr to check for this case, but that logic didn't extend to IC pins because they are not assigned to the anchor list in the same way.

Note this approach could cause other unexpected behavior, such as this case:

with schemdraw.Drawing():
    p = elm.Dot()
    elm.Line()
    p.at((3,3))

The Dot position is determined at the time the Line is created, so moving it later with at won't do anything.