How to get sharp UI on high dpi with Qt 5.6? - c++

I am developing on a 4k monitor and its a pain...
Finally I managed to set up QtDesigner and then ecountered this issue:
When you use QT_AUTO_SCREEN_SCALE_FACTOR=1 and compile an app with radiobutton and other basic widgets, it looks scaled on a 4k screen.
Otherwise the dimensions of controls are correct and as expected, its just not sharp, but rather pixelated.
I am running on Windows 10 Home 64bit on 4k screen with 200% DPI zoom, using Qt 5.6 RC msvc2015 64bit and tried with achieving the same results using
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
If I use
QGuiApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
the controls are sharp, text size is ok BUT all dimensions are much smaller.
How do I make controls sharp on high DPI screen?

As Qt documentation says:
Use QT_AUTO_SCREEN_SCALE_FACTOR to enable platform plugin controlled per-screen factors.
QT_SCREEN_SCALE_FACTORS to set per-screen factors.
QT_SCALE_FACTOR to set the application global scale factor.
You can try doing what Qt Creator is doing:
static const char ENV_VAR_QT_DEVICE_PIXEL_RATIO[] = "QT_DEVICE_PIXEL_RATIO";
if (!qEnvironmentVariableIsSet(ENV_VAR_QT_DEVICE_PIXEL_RATIO)
&& !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR")
&& !qEnvironmentVariableIsSet("QT_SCALE_FACTOR")
&& !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
}
Basically important thing is the last line QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);.

Here is what was working for me. You can set DPIawareness manually by giving a command line option at the instanciation of the QApplication.
The official documentation is here https://doc.qt.io/qt-5/highdpi.html (section DPI awareness).
According to the documentation, you can set the application to DPI Unaware (thus it will automatically scale but display will be blurred), or to System DPI Aware or to Per-Monitor Aware.
Here is a minimal example code for the instanciation of the QApplication to force High DPI, choose other value than 1 (0 or 2) to enable DPIUnaware or Per Monitor DPI Aware:
int main()
{
int argc = 3;
char*argv[] = {(char*)"Appname", (char*)"--platform", (char*)"windows:dpiawareness=1";
(void) new QApplication(argc, argv);
}

With QT_AUTO_SCREEN_SCALE_FACTOR, the point size of the fonts are not changed, they're just scaled up from their original pixels, so they will never be smooth, only more bumpy.
Ref: http://doc.qt.io/qt-5.6/highdpi.html#high-dpi-support-in-qt
"This will not change the size of point sized fonts"
You need to use QT_SCALE_FACTOR instead to rescale your app, not just rescale its pixels.

Related

Custom cursor scaling with windows accessibility settings

I have implemented 32x32 custom cursors with bitmaps using the following approach (in Qt 6.1.1):
this->setCursor(QCursor(QBitmap(":/Cursors/arrow_up.bmp"), QBitmap(":/Cursors/arrow_up_mask.bmp"), 3, 0));
The cursors work as expected if the Windows display setting for size of apps and text is set to 100%. However, if the size of apps and text setting is increased, Qt scales up the custom cursors accordingly and they become pixelated (e.g. if 150% is selected, Qt increases the size of the custom cursors by 50%).
Is there a way to prevent Qt from resizing the custom cursors? Alternatively, is there a way to know what the user has selected in Windows for the size of apps and text, so that I can provide cursors at the correct resolution?
Try this :
QCursor cursor = QCursor(QIcon(":/Cursors/arrow_up.bmp").pixmap(32, 32), 0, 0);
this->setCursor(cursor);
means that use QIcon instead of QBitmap and set its size by pixmap(32, 32).
Another things that may help you :
put this line in your main.cpp file :
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
Read this Document . As I understand setting AA_EnableHighDpiScaling flag Enables high-DPI scaling in Qt on supported platforms (see also High DPI Displays). Supported platforms are X11, Windows and Android. Enabling makes Qt scale the main (device independent) coordinate system according to display scale factors provided by the operating system. This corresponds to setting the QT_AUTO_SCREEN​_SCALE_FACTOR environment variable to 1.

Design user interfaces with automatically scaling fontsizes in Qt

Qt 5.7 is claiming to have improved high DPI support. With modern Qt it is possible to create an app starter like:
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
return app.exec();
}
I expect UI to automatically scale when running on high DPI, but the scaling doesn't necessarily work as expected. At least it doesn't scale the UI for me under Linux. What I am seeing is that the layout scales up, but the fonts stay right where they were, at the sizes Qt Creator assigned them in the form layout tool.
If you want a larger size font for some element, and you set it in form Design screen, there seems no way to say "twice as big". Instead it injects a font property with an absolute point size.
It seems to be the same for the QMessageBox static methods too. Display a static QMessageBox, like QMessageBox::info and its text and icon do not get scaled up to compensate for the high dpi.
So exactly what is it you are supposed to do to allow your Qt application, designed in Creator at a standard DPI, to automatically adjust to a high DPI environment, fonts, QMessageBoxes and all.
I've gotten some traction setting the application's style sheet to use larger font for QMessageBox. but it feels ugly, and I'm not sure how to trigger it automatically.
EDIT:
Manually setting the environment variable
declare -x QT_SCALE_FACTOR=2
Does seem to invoke the sort of behavior I am looking for. But how to do it automatically only on High DPI environment, and preferably inside the program itself . ( setenv (3) could work under Linux, I guess )
As of Qt5.11 the following seems to be good enough for my Ubuntu 18.04 laptop with a 4k screen:
Download and install Qt5.11 from official website (not from apt).
Open the ~/.local/share/applications/DigiaQt-qtcreator-community.desktop file.
Change the line Exec=/path/to/Qt/Tools/QtCreator/bin/qtcreator to Exec=env QT_AUTO_SCREEN_SCALE_FACTOR=1 /path/to/Qt/Tools/QtCreator/bin/qtcreator
Restart Qt Creator.

Qt MainWindow with QOpenGLWIdget in Retina display displays wrong size

I have a Qt application with a MainWindow.
I embed a QOpenGLWidget in it. Everything works fine until I start using an Apple Retina Display and run my app in High DPI mode: my QOpenGLWidget is just 1/4 of the size it was supposed to have (i.e., it only fills the bottom-left part of the area it is supposed to fill). This widget is displaying raw OpenGL data (actually, an OpenSceneGraph context)
What can I do to solve this?
Found out that the best option for now, for OpenGL related widgets and events, is to use the QPaintDevice::devicePixelRatio() (http://doc.qt.io/qt-5/qpaintdevice.html#devicePixelRatio)
This implies multiplying everything that uses pixel coordinates, i.e., mouse events, resizing events, and so on. Example:
void MyGLWidget::resizeGL(int width, int height) {
width *= Application::desktop()->devicePixelRatio();
height *= Application::desktop()->devicePixelRatio();
...
// Continue with previous code
}
When running in low resolution mode in a Retina/HighDPI display, or when running in a regular display, this ratio is 1, so this seems portable to me.
From Qt docs (section OsX),
Note: The scaling is not applied to Open GL windows
I didn't try this approach on Mac, but it helped with the same issue on my Windows machine. I'm not sure if it's the best solution, though, there could be easier. Try and see if it works.
The main idea is to scale up your OpenGL content sizes manually.
First, Define the amount of scale to be performed. You can use the characteristic of physical dot per inch:
QApplication app(argc, argv);
int x = QApplication::desktop()->physicalDpiX();
int y = QApplication::desktop()->physicalDpiY();
// values 284 and 285 are the examples of reference values that we determined when DPI scaling was disabled
double scaleX = 284.0/double(x);
double scaleY = 285.0/double(y);
physicalDpi* makes possible to judge how many pixels we have for an inch. In order to define the scale, detect how much is reference value of density and then scale proportionally against the density of the physical device (next step).
Second, you have to use the scaleX and scaleY inside your QOpenGLWidget and make sure we manually scale:
sizes such as QOpenGLWidget::width() and QOpenGLWidget::height() will turn into this->width()*m_scaleX and this->height()*m_scaleY
mouse events coordinates, e.g., event->x()*m_scaleX and event->y()*m_scaleY

Qt 5.3 full screen issue

I have a Qt application which runs on Ubuntu 12.04 Server with KDE plasma desktop. If I compile with Qt 4.8 full screen works as expected. With Qt 5.3, the window is getting bigger than the available resolution. If I set the resolution using the following code, it works.
QSize sz(QApplication::desktop()->size());
main_window->setFixedSize(sz.width() + 1, sz.height() + 1);
main_window->showFullScreen();
Is this the proper way to solve this issue?
Thanks in advance.
Qt is quite flexible in application sizing and provides you with a lots of informations (and options).
For what concerns QApplication you can use QDesktopWidget.
QDesktopWidget * screen = QApplication::desktop();
screen->availableGeometry();
As stated in the docs about availableGeometry:
Returns the available geometry of the screen with index screen. What
is available will be subrect of screenGeometry() based on what the
platform decides is available (for example excludes the dock and menu
bar on Mac OS X, or the task bar on Windows). The default screen is
used if screen is -1.
Read the section "Use of the Primary Screen" in QDesktopWidget docs for details about the "default screen" and the general handling of multiple screens. Using these methods you will have full control over the way your application is laid out, even with multiple screens available.
For what concerns QGuiApplication you can use QScreen:
QScreen * screen = QGuiApplication::primaryScreen();
screen->availableGeometry();
Finally, in QML it is possible (and advisable) to use Screen object which provides Screen.desktopAvailableWidth and Screen.desktopAvailableHeight which ensure proper resizing with different versions of Android/iOS.

Changing DPI scaling size of display make Qt application's font size get rendered bigger

I have created some GUI application using Qt. My GUI application contains controls like push button and radio button. When I run the application, buttons and fonts inside button looks normal. When I change the DPI scaling size of display from 100% to 150% or 200%, font size of controls rendered bigger but not control size (pushbutton, radio button) irrespective of resolution. Due to this the text inside controls were cut off. please see the attached image.
Qt application look when DPI scaling size set to 100%
Qt application look when DPI scaling size set to 200%
I am running my application in some tablets also. In tablets, DPI scale value should be more than 150% else everything will be shown very small.
I searched in the web for creating UI application in Qt irrespective of resolution and DPI scale value but no luck. So I am posting my questing here. Please let me know if there is some way to get rid of this.
High DPI support is enabled from Qt 5.6 onward.
Setting QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling) in your application source code allows automatic high-DPI scaling.
NOTICE: To use the attribute method, you must set the attribute before you create your QApplication object:
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
return app.exec();
}
Using layouts correctly can help.
http://qt-project.org/doc/qt-4.8/layout.html
Telling the OS that you handle DPI changes, will prevent weird font changes that you weren't expecting.
http://msdn.microsoft.com/en-us/library/ms701681(v=vs.85).aspx
For spacing critical places, you can check the size of your rendered font, and then set the minimum size of your object based on the resulting size of your text.
http://qt-project.org/doc/qt-4.8/qfontmetrics.html#details
https://blog.qt.digia.com/blog/2009/06/26/improving-support-for-higher-dpi-on-vista/
You could try checking with other built in measurements from Qt:
http://qt-project.org/doc/qt-4.8/qpaintdevice.html#widthMM
http://qt-project.org/doc/qt-4.8/qpaintdevice.html#logicalDpiX
If you are using QML, try for pristine layouts of only anchor based placement.
http://qt-project.org/doc/qt-4.8/qml-anchor-layout.html
QApplication has some settings that are somewhat related.
http://qt-project.org/doc/qt-4.8/qapplication.html#setDesktopSettingsAware
You could manually specify the font, too.
http://qt-project.org/doc/qt-4.8/qapplication.html#setFont
Hope that helps.
I had a fixed size window which was not large enough to fit all the text it contained when Windows accessibility settings where applied to scale up all text sizes. Windows does this via dpi increases. I fixed this by retreiving the os scaling factor and then adjusted the size of the my window and some of it's layouts (which I couldn't get to scale automatically for some reason).
Here's how I got the dpi scale (in a file called "WindowsDpiScale.h"):
#ifndef WINDOWSDPISCALE_H
#define WINDOWSDPISCALE_H
#include <QtGlobal>
#ifdef Q_OS_WIN
#include <windows.h>
const float DEFAULT_DPI = 96.0;
float windowsDpiScale()
{
HDC screen = GetDC( 0 );
FLOAT dpiX = static_cast<FLOAT>( GetDeviceCaps( screen, LOGPIXELSX ) );
ReleaseDC( 0, screen );
return dpiX / DEFAULT_DPI;
}
#endif //Q_OS_WIN
#endif // WINDOWSDPISCALE_H
And then, how I applied it in my case:
...
#include "WindowsDpiScale.h"
MainWindow::MainWindow( QWidget *parent )
: QMainWindow( parent )
{
...
// Enlarge the window and various child widgets to accomendate
// OS display scaling (i.e. accessibily options)
setScaleToOsSettings();
...
}
void MainWindow::setScaleToOsSettings()
{
#ifdef Q_OS_WIN
setScale( windowsDpiScale() );
#endif
}
void MainWindow::setScale( float scale )
{
// Resize the window
this->setFixedSize( (int)(scale * this->maximumWidth()),
(int)(scale * this->maximumHeight()) );
// Resize the layouts within the stacked widget
foreach( QVBoxLayout * layout,
windowUi_->pagerStackedWidget->findChildren<QVBoxLayout *>() )
layout->parentWidget()->setFixedSize(
(int)(scale * layout->parentWidget()->contentsRect().width()),
(int)(scale * layout->parentWidget()->contentsRect().height()) );
}
Here is a workaround:
Create a file qt.conf and add these lines to it.
[Platforms]
WindowsArguments = dpiawareness=0
Put this file in the application binary folder. That's all. It will work well but only issue is that the look will not be that crisp.
For delivering it to the customer, add this file where you have your dependency files & while creating setup, like normally you run your dependency files, run this same as well.
There are several options when dealing with high-resolution displays:
Do nothing and develop in high resolution. Eventually, a higher resolution becomes a new standard, everybody will get 13-15-17 inches. It did work in the 2000th, didn't it?
Leave it to the OS. For example, Windows has a special compatibility setting which scales everything to correct size, while keeping the application think that it renders of a low-resolution display. This is called DPI Unaware on Windows.
Try to use Qt capabilities, QT_AUTO_SCREEN_SCALE_FACTOR=0, QT_SCALE_FACTOR=1.
Use a little bit of help from Qt by setting QT_AUTO_SCREEN_SCALE_FACTOR to 1 or a correspondent AA_EnableHighDpiScaling attribute (introduced in Qt 5.6). This will scale widget sizes relative to the font size, so you only need to deal with raster images.
Turn off AA_EnableHighDpiScaling and rethink all your pixel sizes by making them relative to the font size or multiplying them over the device pixel ratio.
An improvement over a previous step: take into an account the device pixel ratio on every display, so that UI is scaled appropriately when you move it to another display.
Sources:
https://doc.qt.io/qt-5/scalability.html
https://doc.qt.io/qt-5/highdpi.html
https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
There are many ways.
one of them is to have reference (height, width and DPI) and when you run your application on a different setup all you need is to get height, width and the logical dots per inch
you can use qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch();
and then you calculate the new font size by multiplying it with the calculated ratio.
qreal refDpi = 216.;
qreal refHeight = 1776.;
qreal refWidth = 1080.;
QRect rect = QGuiApplication::primaryScreen()->geometry();
qreal height = qMax(rect.width(), rect.height());
qreal width = qMin(rect.width(), rect.height());
qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch();
m_ratio = qMin(height/refHeight, width/refWidth);
m_ratioFont = qMin(height*refDpi/(dpi*refHeight), width*refDpi/(dpi*refWidth));
the second way, which is extremely simple, more productive and personally I use it, is simply by using stylesheets!!, and the font size won't change by your DPI scaling.
you can use font property like that for example
font: bold italic large "Times New Roman";