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