Qt5.6:高 DPI 支持和 OpenGL (OpenSceneGraph)

Qt5.6: high DPI support and OpenGL (OpenSceneGraph)

我有一个最小的应用程序,它使用 QOpenGLWidget 集成了 OpenGL 包装器库 (OpenSceneGraph)。我正在尝试弄清楚在像我使用的那样处理 OpenGL 内容时如何正确使用 Qt5.6 对高 DPI 屏幕的支持。

我的 main() 函数具有以下代码:

int main(int argc, char** argv)
{
    // DPI support is on
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QApplication app(argc, argv);
    QMainWindow window;

    // QOpenGLWidget with OpenSceneGraph content
    QtOSGWidget* widget = new QtOSGWidget();

    window.setCentralWidget(widget);
    window.show();
    return app.exec();
}

QtOSGWidget 派生自 QOpenGLWidget 和 OpenSceneGraph 内容:我使用 osgViewer::GraphicsWindowEmbedded 来渲染我的简单场景。

为了将 OSG 与 Qt 合并,我重新定义了 *GL() 方法:paintGL()resizeGL()initializeGL()。我按照 Qt 文档了解每个 *GL() 方法应包含的内容,即:

当我 运行 我的示例在正常分辨率屏幕上,或使用 QApplication::setAttribute(Qt::AA_DisableHighDpiScaling); 时,场景看起来应该是:

此外,当我操纵相机视图时,鼠标坐标被正确捕获。

然而,当我设置高 DPI 选项时,这是我得到的:

事件的鼠标坐标也被缩放并且没有正确传递到 OpenSceneGraph 的事件处理程序。

如您所见,图形 window 大小未由 Qt 缩放。这可能是因为我设置尺寸的方式:

virtual void resizeGL( int width, int height ) 
{
    // resize event is passed to OSG
    this->getEventQueue()->windowResize(this->x(), this->y(), width, height);

    // graphics window resize
    m_graphicsWindow->resized(this->x(), this->y(), width, height);

    // camera viewport
    osg::Camera* camera = m_viewer->getCamera();
    camera->setViewport(0, 0, this->width(), this->height());
}

Qt 不缩放大小。鼠标事件坐标也会发生同样的事情。

我的问题:有没有办法知道将执行缩放的大小,以便正确地 resizeGL()?或者正确的处理方法是什么?

Update/Solution 使用手动缩放:感谢@AlexanderVX 的回答,我找到了缩放解决方案。首先,我需要知道一些DPI在X和Y维度的参考值。然后我根据它计算缩放坐标并将它们传递给我的小部件 QtOSGWidget。因此,main() 的代码必须包含:

QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);

int x = QApplication::desktop()->physicalDpiX();
int y = QApplication::desktop()->physicalDpiY();
// values 284 and 285 are the reference values
double scaleX = 284.0/double(x);
double scaleY = 285.0/double(y);

QMainWindow window;
QtOSGWidget* widget = new QtOSGWidget(scaleX, scaleY, &window);
// etc.

然后,每当我引用需要传递给 OpenSceneGraph (OpenGL) 内容的尺寸调整函数时,我都必须进行缩放,例如:

// resizeGL example
this->getEventQueue()->windowResize(this->x()*m_scaleX, this->y() * m_scaleY, width*m_scaleX, height*m_scaleY);

// mouse event example
this->getEventQueue()->mouseButtonPress(event->x()*m_scaleX, event->y()*m_scaleY, button);

最终更新:因为我的应用程序的目标平台是Windows 7-10,坚持@AlexanderV 的建议答案更有意义(第二部分),即使用 SetProcessDPIAware() 函数。

Is there a way to know to what size the scaling will be performed so that to do resizeGL() correctly?

首先检测显示器:

        // relative to widget
        int screenNum = QApplication::desktop()->screenNumber(pWidget);

或者也许

        // relative to global screen position
        int screenNum = QApplication::desktop()->screenNumber(pWidget->topLeft());

这给了我们指向 QScreen:

的指针
        QScreen* pScreen = QApplication::desktop()->screen(screenNum);

从中可以看出很多屏幕特性,包括"physical dot per inch"可以判断每英寸有多少像素:

        qreal pxPerInch = pScreen->physicalDotsPerInch();

有了每英寸的像素,您将能够以编程方式缩放您的绘图代码。检测 'normal' 密度是多少,然后根据物理设备上检测到的密度按比例缩放。当然,这种方法更适合精确的图形。不过,请注意这两个 physicalDotPerInch() and devicePixelRatio()

        qreal scaleFactor = pScreen->physicalDotsPerInch() / normalPxPerInch;

Or what is the correct way to deal with the problem?

然而,使用小部件和普通 GUI 绘图通常更容易让 Qt/系统缩放整个 UI。 Qt 文档:High DPI Displays.

如果 OS Windows 至少是 Vista 或更高版本,并且为高 DPI 调整 Qt 听起来很复杂,那么我有一个捷径可以帮助我,尽管 Qt 在日志中抱怨: "SetProcessDpiAwareness failed: "COM error 0xffffffff80070005 (Unknown error 0x0ffffffff80070005)" “。我在事件循环之前从 main() 调用此函数:SetProcessDPIAware() and then all the UI looks alike no matter what monitor density is. I use it with Qt 5.5, though. There is also SetProcessDpiAwareness() 函数,探索。我使用 SetProcessDPIAware 因为它自 Windows Vista 以来可用,但 SetProcessDpiAwareness仅在 Windows 8.1 之后可用。因此,决定可能取决于潜在的客户系统。

一个'shortcut'方法:

int main(int argc, char** argv)
{
    // DPI support is on
    // QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

     // on Windows?
    ::SetProcessDPIAware();
    // MSDN suggests not to use SetProcessDPIAware() as it is obsolete and may not be available.
    // But it works with widgets.

    QApplication app(argc, argv);
    QMainWindow window;

    // QOpenGLWidget with OpenSceneGraph content
    QtOSGWidget* widget = new QtOSGWidget();

    window.setCentralWidget(widget);
    window.show();
    return app.exec();
}