Page MenuHome

Python: BLF does not provide a way to account for monitor DPI scaling and displays inconsistently on different monitors
Closed, InvalidPublic


System Information
Operating system: Windows-10-10.0.17763 64 Bits
Graphics card: GeForce GTX 1080/PCIe/SSE2 NVIDIA Corporation 4.5.0 NVIDIA 390.77

Blender Version
Broken: version: 2.80 (sub 74), branch: master, commit date: 2019-07-03 09:54, hash: rBf990c23bcfb5
Worked: (optional)

Short description of error
See the two attached screenshots- in the first, it is a 4k monitor with windows dpi scaling set to 150%. The second screenshot is a 1080p monitor with the default dpi scaling (100%). The attached script was written and targeted for the 4k monitor, but shows up for 1080p users as in the second shot. There is no way that I have found to reconcile this difference.



Exact steps for others to reproduce the error
Execute the following script on a 4k display, and again on a 1080p display to reproduce the attached results.

import bpy
import bgl
import blf

def draw():
    font_size = 16
    font_dpi = 72
    line_height = font_size * 1.25 # ensures a bit of space between each line
    # Draw 3 lines of text just below the selected object name in the 3D viewport.
    lines = ["The quick brown fox", "jumped over the lazy", "bug report"]

    # calculate the top and left position based on whether or not the toolbar and header is visible so the text aligns with the built-in viewport text
    tool_region = None
    header_region = None
    window_region = None
    for r in bpy.context.area.regions:
        if r.type == "TOOLS":
            tool_region = r
        if r.type == "HEADER":
            header_region = r
        if r.type == "WINDOW":
            window_region = r
    desired_top = int(window_region.height) - 100
    left = 30

    if tool_region != None:
        left = tool_region.width + left
    if header_region != None:
        desired_top -= header_region.height
    blf.size(0, font_size, font_dpi)
    actual_top = desired_top - int(line_height * len(lines))
    for i, line in enumerate(lines):
        y_offset = (i - len(lines)) * -1
        blf.position(0, left, actual_top + (y_offset * line_height), 0)
        blf.draw(0, line)

    bpy.types.SpaceView3D.draw_handler_remove(draw, "WINDOW")
    # wasn't added yet, first run.

bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

Event Timeline

Germano Cavalcante (mano-wii) changed the task status from Unknown Status to Invalid.Jul 11 2019, 6:07 PM

This seems to be more a feature request than a bug.
Have you tried it with C.preferences.system.dpi?
What the blf module does is just to expose Blender's BLF built-in functions to python.

It's not a feature request, so far as I know, unless I'm missing something. The issue is that there is no way to ensure the end result is 100% identical on different displays that have different dpi. it doesn't matter what I set the dpi to in blf.size, it will look incorrect regardless. For example, if I set the dpi in blf.size to bpy.context.preferences.system.dpi, I get the following result in my 4k monitor (with a dpi of 108, according to system.dpi):

and this result in my 1080p monitor (with a dpi of 72):

I can't create a consistent user experience across all display types when using BLF

system.dpi should not be used, it's deprecated.

Instead scale any UI elements including text by this:

the UI scale value has not been changed between those two shots, they are both 1.

This is not equal to the value in the preferences, it takes into account DPI. Did you check that bpy.preferences.system.ui_scale is actually the same on both?

Aha! now that is a detail that I was not aware of. I had incorrectly assumed that ui_scale was simply the value that appears in preferences->interface, I never actually queried the value. It does correctly report a ui_scale that takes dpi into consideration, and now that I have a multiplier I can create consistent results on any display. Thanks for the guidance!