Home > Mobile >  QT Designer not playing nicely with Windows display scaling
QT Designer not playing nicely with Windows display scaling

Time:04-16

I've been trying to use QT Designer to make some python GUI. All was going well until I converted the .ui file to .py and ran into the following issue.

In QT Designer the window looks great when I preview it:

Preview from QT Designer

However when I try to run the python script I generated, I see the following:

How it looks in python

Dragging the window larger doesn't fix the problem:

After making the window bigger

Right now I'm trying to redo this GUI using layouts, as I'm sure is best practice anyways. However, I'm not sure if this will fix my issue and am sure that there's a much easier way to fix this than I am missing.

I'm nearly certain that this is an issue caused by Windows display scaling, and thus was sure that there would be a well documented option or quick fix for this problem. But I unfortunately was not able to find any such help online.

Or could the solution be as simple as making my font sizes smaller or getting the labels to scale properly? Looking back at the window python opened it looks like that might be a temporary fix. Regardless, I would much rather get at the root of the problem so I don't run into this issue in the future.

Thanks for any help.

CodePudding user response:

You are correct, this is caused by Window's DPI scaling. Text is scaled by Windows automatically, however none of the widgets are aware of this scaling so you see that mess. The correct way to address this is to get the DPI scaling factor and (annoyingly) multiply it by the original sizes everywhere in your app.

One method is to create a global variable that stores this scaling factor and then anywhere you create a widget, set all size and position parameters to the scaled up size. I did this on a project and it worked great, but was tedious.

Another method (one I now use in production for large apps) is to create a singleton class that has some helper functions that will recursively update the important sizing parts of widgets and layouts. It won't affect everything however, such as fixed sizing or layout spacing, so it's still not perfect. That will need to be done manually as shown in my example below.

Here's the singleton DPI class:

class DPI
{
    Q_DISABLE_COPY(DPI)
public:

    static DPI& Get(){
        static DPI instance;
        return instance;
    }

    static float val(){
        return Get().Ival();
    }

    static void setup(){
        DPI& instance = Get();
        instance._val = qApp->desktop()->logicalDpiX()/96.0; //this is what gets the scaling factor
        if(instance._val < 1)
            instance._val = 1;
    }

    static void apply(QWidget *widget){
        if(widget->property("DPI_applied").toBool()) //property is applied to ensure nothing gets applied twice
            return;

        QRect rect = widget->geometry();
        widget->setGeometry(rect.x()*DPI::val(), rect.y()*DPI::val(), rect.width()*DPI::val(), rect.height()*DPI::val());
        widget->setContentsMargins(widget->contentsMargins()*DPI::val());
        widget->setProperty("DPI_applied", true);
    }

    static void apply(QLayout *layout){
        if(layout->property("DPI_applied").toBool())
            return;
        layout->setSpacing(layout->spacing()*DPI::val());
        layout->setContentsMargins(layout->contentsMargins()*DPI::val());
        layout->setProperty("DPI_applied", true);
    }

    static void applyToChildren(QWidget *widget){
        QList<QWidget*> childWidgets = widget->findChildren<QWidget*>();

        QListIterator<QWidget*> iw(childWidgets);
        while(iw.hasNext()){
            QWidget *child = iw.next();
            DPI::apply(child);
        }

        QList<QLayout*> childLayouts = widget->findChildren<QLayout*>();

        QListIterator<QLayout*> il(childLayouts);
        while(il.hasNext()){
            QLayout *child = il.next();
            DPI::apply(child);
        }
    }

    static void applyToChildren(QLayout *layout){
        QList<QWidget*> childWidgets = layout->findChildren<QWidget*>();

        QListIterator<QWidget*> iw(childWidgets);
        while(iw.hasNext()){
            QWidget *child = iw.next();
            DPI::apply(child);
        }

        QList<QLayout*> childLayouts = layout->findChildren<QLayout*>();

        QListIterator<QLayout*> il(childLayouts);
        while(il.hasNext()){
            QLayout *child = il.next();
            DPI::apply(child);
        }
    }

private:
    DPI() {}

    float Ival(){return _val;}

    float _val;
};

And here's how I use it throughout my project:

//First call this at the top of the mainwindow constructor
DPI::setup();

//Then call this at the end of the mainwindow constructor.
//I also call it whenever I create new GUI elements or other windows that didn't get scaled yet.
//I can pass in any widget/layout and it'll adjust most things for me
DPI::applyToChildren(this);

//If I need to get the scaling factor directly I use this
DPI::val();

//An example use case would be
myButton->setFixedSize(64*DPI::val(), 64*DPI::val());

//If I need to scale inside a stylesheet, I have to build the string using this:
setStyleSheet("#myWidget{border-radius: "   QString::number(6*DPI::val())   "px;}");

It ain't quick to apply this to an existing project, but the earlier you start, the easier implementation gets.

This also makes Qt Designer irrelevant in many cases because widget sizes need to get altered via code anyway, might as well make it with code in the first place.

This will make everything way bigger than it is now, but it'll look how it's supposed to look on higher scaled DPI screens.

I tried the solutions listed here years ago but they never worked for me... but you're free to give them a try: Changing DPI scaling size of display make Qt application's font size get rendered bigger

CodePudding user response:

Figured out a way to accomplish this quick and easy:

import sys
from PyQt5 import QtWidgets, QtCore, QtGui #pyqt stuff

QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) #enable highdpi scaling
QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) #use highdpi icons

#rest of your code...

(From https://leomoon.com/journal/python/high-dpi-scaling-in-pyqt5/)

If that doesn't work you can also take a look at this thread: PyQt 5 and 4k screen

  • Related