11

I am trying to write some text on a panel that I created, like this:

box.label(text="Some long text that is out of borders")

but because the text is too long, the panel only shows the text in one line and does not make the text stop at the edge of the panel and start a new line.

So it appears like this:

Some long text...ers

How can I make it contained within the panel's width?

Gorgious
  • 30,723
  • 2
  • 44
  • 101
Hamza Al Omari
  • 331
  • 3
  • 8

3 Answers3

14

Another solution is to use python's textwrap module:

import textwrap

textTowrap = "Your text......................................"
wrapp = textwrap.TextWrapper(width=50) #50 = maximum length
wList = wrapp.wrap(text=textTowrap)

#Now in the panel: for text in wList: row = layout.row(align = True) row.alignment = 'EXPAND' row.label(text=text)

Gorgious
  • 30,723
  • 2
  • 44
  • 101
Noob Cat
  • 1,222
  • 3
  • 20
  • 61
  • There is no documentation in Blender's API, but this is a python module present in Blender. textwrap docs: https://docs.python.org/3.7/library/textwrap.html – Noob Cat May 15 '20 at 21:07
  • 3
    You can also add this row.scale_y = 0.6 somewhere before row.label(text) so to increase or decrease the space between lines. – Simos Sigma Dec 15 '20 at 14:30
8

I know I'am answering my own question, because I knew how to do it only after posting the question, so for anyone who may need this answer here it goes.

I used a wrap function that I made my self, like this:

def wrap(width, text):

  lines = []

  arr = text.split()
  lengthSum = 0

  strSum = ""

  for var in arr:
    lengthSum+=len(var) + 1
    if lengthSum <= width:
      strSum += " " + var
    else:
      lines.append(strSum)
      lengthSum = 0
      strSum = var

  lines.append(" " + arr[len(arr) - 1])

  return lines

The width argument is the width of the panel in terms of number of characters that fit in there. Then in the draw function I got the width of the panel like this:

tool_shelf = None
area = bpy.context.area

for region in area.regions:
  if region.type == 'TOOLS':
    tool_shelf = region

Where tool_shelf is now the panel I created, then I call the wrap function with the width and text like this:

lines = wrap(math.ceil(tool_shelf.width / 9), "Some long text that is out of borders")

I divided by 9 because tool_shelf.width gives back the width in pixels, and I assumed 9 pixels for each character, now lines is an array where every element is a line. Then I write the lines in the panel like this:

for var in lines:
  row = layout.row(align = True)
  row.alignment = 'EXPAND'
  row.label(var)

I know this is not the optimal way, but anybody can get the main idea from this code and optimize it to their needs.

Note: function wrap is not in any class, but all the other codes are inside the draw function of the class Panel.

Hamza Al Omari
  • 331
  • 3
  • 8
1

I drew a script that also takes into account the resolution of the UI.

for blender3.2

import bpy
import textwrap

long_text = """ a long text long text long text b test c long text long text long text long text long text long d text long text long text long text """

class MyPanel(bpy.types.Panel): bl_idname = "OBJECT_PT_my_panel" bl_label = "My Panel" bl_space_type = "VIEW_3D" bl_region_type = "UI"

def draw(self, context):
    layout = self.layout

    # Get the 3D View area
    for area in bpy.context.screen.areas:
        if area.type == 'VIEW_3D':
            break
    # Calculate the width of the panel
    for region in area.regions:
        if region.type == 'UI':
            panel_width = region.width
            break

    # Calculate the maximum width of the label
    uifontscale = 9 * context.preferences.view.ui_scale
    max_label_width = int(panel_width // uifontscale)

    # Split the text into lines and format each line
    for line in long_text.splitlines():
        # Remove leading and trailing whitespace
        line = line.strip()

        # Split the line into chunks that fit within the maximum label width
        for chunk in textwrap.wrap(line, width=max_label_width):
            layout.label(text=chunk)

Register the panel

bpy.utils.register_class(MyPanel)

enter image description here

When the font scale is different in 2 bytes, such as Japanese

method part

import bpy

def split_by_n(string, n): result = [string[i:i+n] for i in range(0, len(string), n)] return result

def longtext_set(context,layout,long_text): # Get 3D viewport area for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': break

    # Automatically set the width of the UI region to the width of the panel
    for region in area.regions:
        if region.type == 'UI':
            panel_width = region.width
            break

    # Calculate the maximum width of the label
    font_scale= 15
    uifontscale = font_scale * context.preferences.view.ui_scale
    max_label_width = int(panel_width // uifontscale)

    save_alignment=layout.alignment

    layout.alignment = 'RIGHT'
    box = layout.box()
    # Split the text into lines and format each line
    for line in long_text.splitlines():
        # Remove leading and trailing whitespace
        line = line.strip()

        # Split the line into chunks that fit within the maximum label width

        for chunk in split_by_n(line, max_label_width):

        # for chunk in textwrap.wrap(line, width=max_label_width, break_long_words=False, break_on_hyphens=False):
            box.label(text=chunk)
    layout.alignment = save_alignment

Depiction of panel section

long_text="""
これは日本語のテスト文書です。
これは日本語のテスト文書です。
これは日本語のテスト文書です。
これは日本語のテスト文書です。
これは日本語のテスト文書です。
"""

longtext_set(context,layout=self.layout,long_text=long_text)

mml
  • 443
  • 2
  • 9