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

6
我有一个使用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()方法应包含什么的说明,即:

  • paintGL()确保查看器已更新
  • resizeGL()确保图形窗口被正确调整大小(连同相机和视口);
  • initializeGL()确保初始化OpenGL状态。
  • 我还重新定义了Qt鼠标事件,以便将事件传递给OSG

当我在普通分辨率屏幕上运行我的示例,或者使用 QApplication :: setAttribute(Qt :: AA_DisableHighDpiScaling); 时,场景看起来像它应该的样子:

cylinder example - high DPI support is off

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

然而,当我打开高 DPI 选项时,会得到以下结果:

high DPI is on

鼠标事件的坐标也被缩放了,但没有正确传递给OpenSceneGraph的事件处理程序。

正如您所看到的,图形窗口的大小没有被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()?或者应该如何正确地解决这个问题? 更新/手动缩放的解决方案:感谢@AlexanderVX的回答,我找到了缩放的解决方案。首先,我需要知道X和Y维度中DPI的一些参考值。然后,我基于此计算缩放坐标,并将它们传递给我的widget 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()函数,更加合理。

当你调整窗口大小时,你的resize方法会被调用吗? - KimKulling
是的,我检查过了。它会自动调用每一次。 - vicrucann
1个回答

4

有没有办法知道缩放将执行到什么大小,以便正确执行 resizeGL()

首先,检测显示器:

        // 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);

您可以从中读取许多屏幕特征,包括“物理每英寸点数”,这使我们能够判断每英寸有多少像素:

        qreal pxPerInch = pScreen->physicalDotsPerInch();

拥有每英寸像素,您将能够以编程方式缩放绘图代码。检测“正常”密度的数量,然后根据在物理设备上检测到的密度进行比例缩放。当然,这种方法更适合准确的图形。但是,请注意physicalDotPerInch()devicePixelRatio()

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

或者说正确的处理问题的方式是什么?

然而,对于小部件和普通GUI绘图,让Qt/系统缩放整个UI往往更容易。参见Qt文档:高DPI显示

如果操作系统是Windows Vista或更高版本,而且调整Qt以适应高DPI听起来很复杂,那么我有一个快捷方式可以帮助我,尽管Qt在日志中抱怨:“SetProcessDpiAwareness失败:“COM错误0xffffffff80070005(未知错误0x0ffffffff80070005)””。 我在事件循环之前从main()调用此函数:SetProcessDPIAware(), 然后所有的UI看起来都一样,无论显示器密度如何。虽然我使用的是Qt 5.5,但也有SetProcessDpiAwareness()函数可供探索。我使用SetProcessDPIAware,因为它自Windows Vista以来就可用,但SetProcessDpiAwareness仅自Windows 8.1以来可用。因此,决策可能取决于潜在客户的系统。

一种“快捷方式”方法:

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();
}

在您认为“正常”的系统上运行代码。尝试查看physicalDotsPerInch返回的值。 - Alexander V
1
@vicrucann,注意现在你有多屏幕不兼容的问题。试试使用两个不同像素密度的显示器。 - Alexander V
@AlexandeVX:我有多个显示器,并已经对它们进行了一些测试。是的,你是正确的:当我从高DPI拖到低DPI时,缩放不起作用。但我认为这与问题有点偏题。实际上,Qt尝试缩放其小部件,它有点起作用(虽然不是最好的外观结果)。对于缩放,我将尝试找出如何检测屏幕DPI的变化并更新比例因子。如果不直接,我可能会探索第二个建议的选项;即使它只适用于Windows。 - vicrucann
那就是 SetProcessDPIAware 的作用。但不是其他的缩放。 - Alexander V
我现在明白你的意思了。我在我的扩展项目中尝试了一下,包含了复杂场景,看起来更好。对于我来说,在大型项目中我会选择 SetProcessDPIAware - vicrucann
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接