获取屏幕缩放比
网上很多代码是用 logicalDotsPerInch 除以 96 来获取屏幕缩放比:
// Windows 除以 96,macOS 除以 72
qreal factor = window->screen()->logicalDotsPerInch() / 96.0;
当使能了缩放适配后,logicalDotsPerInch 值就不随系统缩放变了:
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// Qt6 默认开启缩放
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
如果是 PassThrough 支持小数的设置,此时可以用 QScreen 的 devicePixelRatio 获取缩放比。
很明显,我们需要更通用的方式。通过查看源码,发现其实 Qt 内部是有对应接口的,比如 QHighDpiScaling 类的接口:
qreal QHighDpiScaling::rawScaleFactor(const QPlatformScreen *screen)
{
// Determine if physical DPI should be used
static const bool usePhysicalDpi = qEnvironmentVariableAsBool(usePhysicalDpiEnvVar, false);
// Calculate scale factor beased on platform screen DPI values
qreal factor;
QDpi platformBaseDpi = screen->logicalBaseDpi();
if (usePhysicalDpi) {
QSize sz = screen->geometry().size();
QSizeF psz = screen->physicalSize();
qreal platformPhysicalDpi = ((sz.height() / psz.height()) + (sz.width() / psz.width())) * qreal(25.4 * 0.5);
factor = qreal(platformPhysicalDpi) / qreal(platformBaseDpi.first);
} else {
const QDpi platformLogicalDpi = QPlatformScreen::overrideDpi(screen->logicalDpi());
factor = qreal(platformLogicalDpi.first) / qreal(platformBaseDpi.first);
}
return factor;
}
其中 usePhysicalDpi 一般我们也用不到,所以可以直接用 else 部分的逻辑。不过这里用到了 QPlatformScreen 类,需要引入 gui-private 模块:
QT += core gui widgets
QT += gui-private
简单的测试:
void MainWindow::calcDpi()
{
// 可以从 QWidget 或者 QWindow 拿到 QScreen 对象
QScreen *cur_screen = this->screen();
qDebug()<<__FUNCTION__<<cur_screen;
if (cur_screen) {
// 逻辑 dpi (logicalBaseDpi().first) 默认值 win 96/ mac 72
const QDpi base_dpi = cur_screen->handle()->logicalBaseDpi();
const QDpi logic_dpi = QPlatformScreen::overrideDpi(cur_screen->handle()->logicalDpi());
// 得到屏幕缩放百分比
const qreal factor = qreal(logic_dpi.first) / qreal(base_dpi.first);
qDebug()<<"calc dpi"<<factor;
}
}
关联屏幕设置变化
当在多个屏幕拖动,或者修改当前屏幕缩放比后,我们需要重新获取缩放比。
QWindow 提供了 screenChanged 信号可以感知屏幕切换。
QScreen 提供了 physicalDotsPerInchChanged 和 logicalDotsPerInchChanged 信号可以感知分辨率变化。
在 Widgets 中的大致代码就是:
void MainWindow::initWindow()
{
qDebug()<<this->winId();
// 构造的时候 windowHandle 还未初始化,可以等 show 的时候关联,或者先调用一次 winId
connect(this->windowHandle(), &QWindow::screenChanged, this, &MainWindow::onScreenChange);
calcDpi();
}
void MainWindow::onScreenChange(QScreen *screen)
{
// QMetaObject::Connection 保存连接便于释放;
if (logicalDpiConnection) {
disconnect(physicalDpiConnection);
disconnect(logicalDpiConnection);
}
if (screen) {
// 切换缩放比时,EnableHighDpiScaling 会触发 physicalDotsPerInchChanged
// 此时 Qt6 不触发 logicalDotsPerInchChanged
physicalDpiConnection = connect(screen, &QScreen::physicalDotsPerInchChanged, this, &MainWindow::calcDpi);
logicalDpiConnection = connect(screen, &QScreen::logicalDotsPerInchChanged, this, &MainWindow::calcDpi);
calcDpi();
}
}
void MainWindow::calcDpi()
{
// 可以从 QWidget 或者 QWindow 拿到 QScreen 对象
QScreen *cur_screen = this->screen();
qDebug()<<__FUNCTION__;
if (cur_screen) {
// 逻辑 dpi (logicalBaseDpi().first) 默认值 win 96/ mac 72
const QDpi base_dpi = cur_screen->handle()->logicalBaseDpi();
const QDpi logic_dpi = QPlatformScreen::overrideDpi(cur_screen->handle()->logicalDpi());
// 得到屏幕缩放百分比
const qreal factor = qreal(logic_dpi.first) / qreal(base_dpi.first);
qDebug()<<"calc dpi"<<factor;
}
}
完成示例代码:
QWidget 获取 DPI 缩放:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/TestQt_20231221_Dpi
QML 获取 DPI 缩放:
https://github.com/gongjianbo/MyTestCode/tree/master/Qml/TestQml_20231221_Dpi