adafruit/Adafruit_CircuitPython_Display_Text

save_text=False throws error on init

sargunv opened this issue · 11 comments

label = Label(
    font=FONT,
    color=color,
    anchor_point=(0.0, 0.0),
    anchored_position=(column, 0),
    text=str,
    save_text=False,
)
  File "adafruit_display_text/bitmap_label.py", line 100, in __init__
  File "adafruit_display_text/bitmap_label.py", line 161, in _reset_text
  File "adafruit_display_text/bitmap_label.py", line 277, in _text_bounding_box
TypeError: 'NoneType' object is not iterable

I believe this is because that _reset_text function is using the property self._text instead of the local variable text in some (but not all) places.

I am unable to reproduce this on a PyPortal running Adafruit CircuitPython 6.3.0 on 2021-06-01 or Adafruit CircuitPython 7.0.0-alpha.6 on 2021-08-12

What version of CircuitPython are you running?
This will be in the contents of boot_out.txt on the device.

What versions of the libraries are installed?
Run circup freeze from the connected computer. If you need to install circup, run pip3 install circup
For example, I get

> circup freeze
Found device at /media/user/CIRCUITPY, running CircuitPython 7.0.0-alpha.6.
adafruit_fakerequests==1.0.3
adafruit_miniqr==1.3.6
adafruit_pixelbuf==1.1.0
adafruit_requests==1.10.0
adafruit_touchscreen==1.1.6
neopixel==6.2.1
simpleio==3.0.1
adafruit_bitmap_font==1.5.1
adafruit_bus_device==5.0.6
adafruit_display_text==2.20.0
adafruit_esp32spi==3.5.11
adafruit_io==5.5.0
adafruit_portalbase==1.9.1
adafruit_pyportal==6.0.0

You can use circup update to update the libraries on the device to the latest version if needed.

Hi @sargunv, I noticed your code has the parameter text=str. Is it possible you're setting the text to the type str then an actual string value? That is to say does it work if you use something like text="LabelName"?

I ask because I'd love to look into this if you have any more information :)

Hi, I encounter the same problem. Using a Pimoroni Pico Breakout Garden (BG) base with a Raspberry Pi Pico; an OLED 1.12in monochrome 128x128px SPI; an Adafruit TMP117 via QT-stemma and a BG adapter. Circuitpython version: 7.1.0-beta.3 on 2021-12-13. To preserve memory, I tried the save_text=False parameter when calling label.Label() as follows:
tempv_text_area = label.Label(terminalio.FONT, scale=2, text="{:0.2f} C".format(tmp117.temperature), color=0x000000, x=15, y=104, save_text=False) About this call definition: I also tried to define tempv_text = "{:0.2f} C".format(tmp117.temperature) and then in the call use text=tempv_text. Result the same.

@PaulskPt are you able to post a copy of your entire script that is raising this error? Or if it's very large possibly see if you can make a self-contained reproducer script that contains the minimum code needed to raise the issue?

Hi @FoamyGuy, thank you for responding.
Here is the listing of code.py:

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
#
# 2021-12-19 22h00, by @Paulskpt
# Downloaded from:
# https://learn.adafruit.com/adafruit-monochrome-1-12-in-128x128-oled/circuitpython.
# After modifying pin numbers for the actual hardware, the OLED was displaying text.
# I only had to change the ROTATION TO 270 degrees to have the orientation in the right way.
# This is version 2.0
# In this version are implemented various measures to prevent exaust of memory.
#
"""
Based on example by Mark Roberts (mdroberts1243).

This example writes text to the display, draws three squares and a rectangle.
It writes the temperature value from a TMP117 sensor in the rectangle.
"""
import sys
import time
import board
import busio
import displayio
import terminalio
import adafruit_tmp117
from adafruit_display_text import bitmap_label as label
from adafruit_displayio_sh1107 import SH1107, DISPLAY_OFFSET_ADAFRUIT_128x128_OLED_5297

displayio.release_displays()

# Global variable definitions
display = None
tmp117 = None
# Width, height and rotation for Monochrome 1.12" 128x128 OLED
WIDTH = 128
HEIGHT = 128
ROTATION = 270
# Border width
BORDER = 2

def setup():
    global SH1107, display, splash, WIDTH, HEIGHT, ROTATION, BORDER, tmp117

    # For SPI:
    dc = board.GP16
    cs = board.GP17  # for BG SPI socket A (front). GP22 for socket B
    sck = board.GP18
    mosi = board.GP19
    rst = board.GP20  # for BG SPI socket A (front). GP21 for socket B

    spi_bus = busio.SPI(sck, mosi)
    display_bus = displayio.FourWire(spi_bus, command=dc, chip_select=cs, reset=rst)

    display = SH1107(
        display_bus,
        width=WIDTH,
        height=HEIGHT,
        display_offset=DISPLAY_OFFSET_ADAFRUIT_128x128_OLED_5297,
        rotation=ROTATION,
    )

    # i2c for sensors like the TMP117 or the RTC RV3028
    i2c = busio.I2C(board.GP5, board.GP4)

    # Create tmp117 object
    tmp117 = adafruit_tmp117.TMP117(i2c)

def drw():
    global WIDTH, HEIGHT, ROTATION, BORDER, tmp117
    white = 0xFFFFFF
    black = 0x000000
    # Make the display context
    splash = displayio.Group()

    color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
    color_palette = displayio.Palette(1)
    color_palette[0] = white

    bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
    splash.append(bg_sprite)

    # Draw a smaller inner rectangle in black
    inner_bitmap = displayio.Bitmap(WIDTH - BORDER * 2, HEIGHT - BORDER * 2, 1)
    inner_palette = displayio.Palette(1)
    inner_palette[0] = black
    inner_sprite = displayio.TileGrid(
        inner_bitmap, pixel_shader=inner_palette, x=BORDER, y=BORDER
    )
    splash.append(inner_sprite)

    b_0 = [8, 16, 32, 110]
    b_1 = [8, 16, 32, 50]
    b_x = [58, 71, 91, 10]
    b_y = [17, 15, 28, 69]
    ble = len(b_0)

    # Draw  three white squares and a white rectangle
    for _ in range(ble):
        bm = displayio.Bitmap(b_0[_], b_1[_], 1)
        sq = displayio.TileGrid(bm, pixel_shader=color_palette, x=b_x[_], y=b_y[_])
        splash.append(sq)

    fnt = terminalio.FONT

    # Draw some label text
    t_lst = ["Monochrome 1.12in", "128x128", "OLED", "TEMP:", "       "]
    s_lst = [1, 1, 2, 2, 2]
    x_lst = [8, 8, 9, 15, 15]
    y_lst = [8, 25, 44, 80, 104]
    c_lst = [white, white, white, black, black]

    le = len(x_lst)
    for _ in range(le):
        if _ < le-1:
            t = t_lst[_]
        else:
            t = "{:0.2f} C".format(tmp117.temperature)
        t_area = label.Label(fnt, text=t, scale=s_lst[_], color=c_lst[_], x=x_lst[_], y=y_lst[_], save_text=False)
        splash.append(t_area)

    display.show(splash)

    del color_bitmap  # to prevent memory exception error
    del splash        # idem

def main():
    global display
    setup()
    while True:
        try:
            drw()
            time.sleep(5)
        except KeyboardInterrupt:
            break

    sys.exit(0)

if __name__=="__main__":
    main()

Here's the error traceback:

  File "code.py", line 139, in <module>
  File "code.py", line 131, in main
  File "code.py", line 118, in drw
  File "adafruit_display_text/bitmap_label.py", line 100, in __init__
  File "adafruit_display_text/bitmap_label.py", line 161, in _reset_text
  File "adafruit_display_text/bitmap_label.py", line 277, in _text_bounding_box
TypeError: 'NoneType' object is not iterable```

Here an image of the result:

IMG_3445

Thanks @PaulskPt! Looking at the code it looks like @sargunv is right, it's because of how self._text and local variable text are no longer consistent if save_text = False. I'm out for the holidays starting tomorrow, but I should be able to squeeze out a PR, but I won't be able to test. Thanks for the code and followup!

@tekktrik I wish you nice holidays. I'll await the development with attention.

Hi @PaulskPt I submitted a fix for the issue as PR #160. If you want you can give that a try and see if that change fixes the issue.

resolved by #160