Why aren't my QML items being scaled when I move the window to a screen with a different DPI?

Viewed 1100

I am using Windows 10 and Qt 5.15.1. When I move my QML application window from a low DPI screen (100% scale) to a high DPI (125% scale) screen, the window scales up (resizes) to use more pixels, as expected. This makes the window appear to be the same physical size on both screens.

However the items in the window do not scale -- they stay the same number of pixels. So all the items appear to be physically smaller on the high DPI screen.

How do I get the items to scale (to the same physical size) when I move the window between screens with different DPIs? I want this to occur with all items, like text, buttons, rectangles, etc.

My QML is:

import QtQuick 2.12
import QtQuick.Controls 2.12

ApplicationWindow {
    visible: true
    width: 240
    height: 60

    Text {
        text: "Hello World"
        font.pointSize: 14
    }

}

My Python is:

QtCore.QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QtCore.QCoreApplication.setAttribute(Qt.AA_UseOpenGLES)

app = QtWidgets.QApplication([])

engine = QtQml.QQmlEngine()
context = QtQml.QQmlContext(engine.rootContext())
designer = QtQml.QQmlComponent(engine, 'main.qml')
designer.create(context)

app.exec_()

Actual and expected screenshots of text. The text is not scaled up as expected on the high DPI screen.

2 Answers

I learned that Qt rounds DPI scaling to integers by default. So on my screen with 125% scaling, it was rounded down to 100%... thus no change.

This behavior can be disabled as of Qt 5.14 by setting the QT_SCALE_FACTOR_ROUNDING_POLICY environment variable to PassThrough. Or in code:

QtGui.QGuiApplication.setHighDpiScaleFactorRoundingPolicy(
    Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)

Alternatively, if you know the scale factors of your monitors, you can specify them directly by setting the QT_SCREEN_SCALE_FACTORS environment variable like QT_SCREEN_SCALE_FACTORS=1.25\;1.

So now I get the expected automatic scaling on both of my screens.

Scaled properly at 100% and 125%

It's actually weird to me that using a higher DPI changes the dimensions of your app. It's not really "scaling" it though. It's just making the window bigger. And you're not tying the size of the Rectangle to the ApplicationWindow, so that's why it stays the same size. The simple answer here is you could do this:

    Rectangle {
        anchors.fill: parent
        color: "green"
    }

That would keep the Rectangle at the same size as the application.

If you actually want all of the contents of the app (including children of the Rectangle) to scale as the ApplicationWindow changes size, then you could do something like this:

ApplicationWindow {
    visible: true
    width: defaultWidth
    height: defaultHeight

    property real defaultWidth: 320
    property real defaultHeight: 240

    Rectangle {
        width: defaultWidth
        height: defaultHeight
        color: "green"

        transform: Scale { 
            xScale: parent.width / defaultWidth
            yScale: parent.height / defaultHeight
        }
    }
}
Related