web-dev-qa-db-ja.com

新しいQt5.7 +高DPI /モニターDPI対応アプリケーションの開発にアプローチする方法は?

Qtでの高DPIサポートに関する公式 Qtドキュメント およびStackOverflowに関する多くの記事と質問を読みました。それらはすべて、古いアプリケーションを移植し、可能な限り変更を加えずに機能させることに重点を置いています。

しかし、モニターごとのDPI対応アプリをサポートすることを目的として、まったく新しいアプリケーションを開始する場合、最善のアプローチは何ですか?

私が正しく理解していれば、Qt::AA_EnableHighDpiScalingは私が望むものとは正反対です。実際にHighDpiScalingを無効にして、実行時にすべてのディメンションを手動で計算する必要がありますか?

提案の多くは、フローティングレイアウトを使用するために、サイズをまったく使用しないことを示しています。しかし、多くの場合、少なくとも最小の幅および/または最小の高さが存在することが望まれます。 Qt Designerでは絶対ピクセルでしか値を入力できないので、正しいアプローチは何ですか?モニターの解像度が変更された場合、ディメンションを再計算するためのコードをどこに配置する必要がありますか?

それとも、自動スケーリングを使用する必要がありますか?

以前のQtアプリからの私のソリューション(十分にテストされていません)

HighDPIサポートを追加しようとした古いアプリの1つで、このアプローチを使用しました。DOMのすべての子を一覧表示し、一定の比率で1つずつサイズを変更します。比率= 1は、QtDesignerで指定したものと同じ寸法を生成します。

    void resizeWidgets(MyApp & qw, qreal mratio)
    {

        // ratio to calculate correct sizing
        qreal mratio_bak = mratio;

        if(MyApp::m_ratio != 0)
            mratio /= MyApp::m_ratio;

        // this all was done so that if its called 2 times with ratio = 2, total is not 4 but still just 2 (ratio is absolute)
        MyApp::m_ratio = mratio_bak;

        QLayout * ql = qw.layout();

        if (ql == NULL)
            return;

        QWidget * pw = ql->parentWidget();

        if (pw == NULL)
            return;

        QList<QLayout *> layouts;

        foreach(QWidget *w, pw->findChildren<QWidget*>())
        {
            QRect g = w->geometry();

            w->setMinimumSize(w->minimumWidth() * mratio, w->minimumHeight() * mratio);
            w->setMaximumSize(w->maximumWidth() * mratio, w->maximumHeight() * mratio);

            w->resize(w->width() * mratio, w->height() * mratio);
            w->move(QPoint(g.x() * mratio, g.y() * mratio));

        }

        foreach(QLayout *l, pw->findChildren<QLayout*>())
        {
            if(l != NULL && !(l->objectName().isEmpty()))
                layouts.append(l);
        }

        foreach(QLayout *l, layouts) {
            QMargins m = l->contentsMargins();

            m.setBottom(m.bottom() * mratio);
            m.setTop(m.top() * mratio);
            m.setLeft(m.left() * mratio);
            m.setRight(m.right() * mratio);

            l->setContentsMargins(m);

            l->setSpacing(l->spacing() * mratio);

            if (l->inherits("QGridLayout")) {
                QGridLayout* gl = ((QGridLayout*)l);

                gl->setHorizontalSpacing(gl->horizontalSpacing() * mratio);
                gl->setVerticalSpacing(gl->verticalSpacing() * mratio);
            }

        }

        QMargins m = qw.contentsMargins();

        m.setBottom(m.bottom() * mratio);
        m.setTop(m.top() * mratio);
        m.setLeft(m.left() * mratio);
        m.setRight(m.right() * mratio);

        // resize accordingly main window
        qw.resize(qw.width() * mratio, qw.height() * mratio);
        qw.setContentsMargins(m);
        qw.adjustSize();
    }

これはメインから呼び出されます:

int main(int argc, char *argv[])
{

    QApplication a(argc, argv);
    MyApp w;

    // gets DPI
    qreal dpi = a.primaryScreen()->logicalDotsPerInch();

    MyApp::resizeWidgets(w, dpi / MyApp::refDpi);

    w.show();

    return a.exec();
}

これは良い解決策ではないと思います。新しく始めて、コードを最新のQt標準に完全にカスタマイズできることを考えると、HighDPIアプリを入手するにはどのようなアプローチを使用する必要がありますか?

15
michnovka

モニターごとのDPI認識をサポートすることを目的として、まったく新しいアプリケーションを開始する場合、最善のアプローチは何ですか?

モニターごとのDPI対応モードでの自動スケーリングをQtに依存していません。少なくともQt::AA_EnableHighDpiScalingが設定されたQt5.7ベースのアプリはそれを行わず、「高DPIスケーリング」はピクセル密度に関係なくより正確な描画になります。

また、モニターごとのDPI対応モードを呼び出すには、実行可能ファイルを投影するのと同じディレクトリにあるQt.confファイルを変更する必要があります。

[Platforms]
# 1 - for System DPI Aware
# 2 - for Per Monitor DPI Aware
WindowsArguments = dpiawareness=2

# May need to define this section as well
#[Paths]
#Prefix=.

私が正しく理解していれば、Qt :: AA_EnableHighDpiScalingは私が望むものとは正反対です。実際にHighDpiScalingを無効にして、実行時にすべてのディメンションを手動で計算する必要がありますか?

いいえ、それは反対ではなく、別のことです。バグなしとしてクローズされたQtバグがいくつかあります: QTBUG-55449 および QTBUG-5551 これらは機能の背後にある意図を示しています。ところで、 QTBUG-5551 修正せずにQt DPI認識を設定するためのプログラムによる回避策が提供されています(qt.confを修正せずにインターフェイスを変更する「プライベート」Qt実装クラスを使用するため、独自の裁量で使用してください新しいQtバージョンでの通知)。

また、モニターごとのDPI対応モードでスケーリングを行うための正しいアプローチを表現しました。残念ながら、当時は選択肢があまりないことを除けば。ただし、あるモニターから別のモニターに移動するときにウィンドウスケーリングのイベント処理を支援するプログラム的な方法があります。この質問の先頭にあるresizeWidget(1つ、多くはない)のようなメソッドは、(Windows)のようなものを使用して呼び出す必要があります。

// we assume MainWindow is the widget dragged from one monitor to another
bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
   MSG* pMsg = reinterpret_cast<MSG*>(message);

   switch (pMsg->message)
   {
      case WM_DPICHANGED:
         // parameters TBD but mind that 'last' DPI is in
         // LOWORD(pMsg->wParam) and you need to detect current
         resizeWidget(monitorRatio());
         break;

これは非常に困難で面倒な方法であり、ユーザーがモードを選択してアプリプロセスを再開できるようにすることで、アプリがシステムとモニターごとのDPI対応モードを切り替えることができるようにしました(qt.confを修正するか、回避策を実行します)。 from QTBUG-5551 アプリの起動時)。 Qt社が、ウィジェットの自動スケーリングを備えたモニターごとのDPI対応モードの必要性を認識していることを願っています。なぜそれが必要なのか(?)は別の質問です。私の場合、スケーリングされるはずの独自のアプリウィジェットキャンバス内にモニターごとのレンダリングがあります。

最初に、@ selbieからのこの質問へのコメントを読んで、アプリの起動中にQT_SCREEN_SCALE_FACTORSを設定しようとする方法があるかもしれないことに気付きました。

QT_SCREEN_SCALE_FACTORS [list]は、各画面の倍率を指定します。これにより、ポイントサイズのフォントのサイズは変更されません。この環境変数は、主にデバッグ、または誤ったEDID情報(拡張表示識別データ)を持つモニターを回避するために役立ちます。

次に、複数の画面要素を適用する方法について Qt blog を読み、4Kが最初にリストされている4Kおよび1080pモニター(メイン)に対して以下を実行しようとしました。

qputenv("QT_SCREEN_SCALE_FACTORS", "2;1");

それは少し役立ちます:ほぼ正しいレンダリングしかしは、ウィンドウをあるモニターから別のモニターに移動するときに、ウィンドウサイズに欠陥があります QTBUG -55449 あります。お客様が現在のアプリの動作をバグと見なす場合は、WM_DPICHANGED + QT_SCREEN_SCALE_FACTORSアプローチを採用すると思います(システムDPI対応を介してすべてのモニターDPIに対して同じベースを作成します)。それでも、Qtのすぐに使用できるソリューションはまだありません。

9
Alexander V