2016-10-03 1 views
11

Ich habe die offizielle Qt documentation und viele Artikel und Fragen zu StackOverflow über hohe DPI-Unterstützung in Qt gelesen. Sie alle konzentrieren sich darauf, alte Anwendungen zu portieren und sie so wenig wie möglich zu verändern.Wie wird die Entwicklung einer neuen Qt 5.7+ High-DPI Pro Monitor DPI Aware-Anwendung angegangen?

Aber wenn ich eine brandneue Anwendung starten würde, mit der Absicht, DPI-bewusste App pro Monitor zu unterstützen, was ist der beste Ansatz? Wenn ich richtig verstehe, Qt::AA_EnableHighDpiScaling ist das genaue Gegenteil von dem, was ich will. Ich sollte eigentlich HighDpiScaling deaktivieren und alle Dimensionen manuell zur Laufzeit berechnen?

Viele der Vorschläge sagen, keine Größen zu verwenden, um schwebende Layouts zu verwenden. In vielen Fällen ist es jedoch wünschenswert, dass zumindest eine minimale Breite und/oder eine minimale Höhe vorhanden ist. Da Qt Designer nur erlaubt, Werte in absoluten Pixeln zu setzen, was ist der richtige Ansatz? Wo sollte ich den Code platzieren, um die Abmessungen neu zu berechnen, wenn sich die Bildschirmauflösung ändert?

Oder sollte ich einfach mit der automatischen Skalierung gehen?

Meine Lösung aus früheren Qt app (nicht gut getestet)

In einem meiner älteren Anwendungen, wo ich versuchte HighDPI Unterstützung hinzufügen, ich diesen Ansatz - Liste alle Kinder von DOM und sie eins nach dem anderen die Größe gegeben etwas Verhältnis. Verhältnis = 1 würde Dimensionen erzeugen, die denen entsprechen, die ich in Qt Designer angegeben habe.

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

die von den wichtigsten genannt:

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

ich dies nicht berücksichtigen eine gute Lösung. Da ich gerade neu starte und meinen Code vollständig an die neuesten Qt-Standards anpassen kann, welchen Ansatz sollte ich für HighDPI-Apps verwenden?

+0

Auf Win32 haben wir erfolgreich gefunden, einfach das Betriebssystem (GetDeviceCaps) für die Monitor-dpi-Einstellung abzufragen, dividieren durch 96,0 und die env-Variable 'QT_SCALE_FACTOR' passend zu setzen. Es gibt Kompromisse. Auf Mac mussten wir nichts tun. YMMV. – selbie

+0

@selbie, wie Einstellung QT_SCALE_FACTOR Adresse pro Monitor Skalierung? – AlexanderVX

+0

Vielleicht gibt diese QT_SCREEN_SCALE_FACTORS [Liste] Skalierungsfaktoren für jeden Bildschirm an. Dies ändert nicht die Größe von Schriftarten in Punktgröße. Diese Umgebungsvariable ist hauptsächlich zum Debuggen oder zum Arbeiten mit Monitoren mit falschen EDID-Informationen (Extended Display Identification Data) nützlich. Aber unsicher, jemand kümmert sich darum, es aus der App heraus zu setzen? – AlexanderVX

Antwort

7

Wenn ich mit der Absicht zu Unterstützung pro-Monitor DPI Bewusstsein eine völlig neue Anwendung zu starten, war, was ist der beste Ansatz?

Wir verlassen uns nicht auf Qt für die automatische Skalierung im DPI-bewussten Modus. Zumindest Qt 5.7-basierte App mit Qt::AA_EnableHighDpiScaling Set tut dies nicht und "High DPI Scaling" ist mehr von der genauen Zeichnung unabhängig von der Pixeldichte.

Und pro-Monitor bewusst DPI-Modus aufzurufen, müssen Sie Qt.conf Datei im selben Verzeichnis ändern, in dem Sie ausführbare Datei Projekt ist:

[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=. 

Wenn ich richtig verstehe, Qt :: AA_EnableHighDpiScaling das ist sehr gegenüber von dem, was ich will. Ich sollte eigentlich deaktivieren HighDpiScaling und alle Dimensionen manuell zur Laufzeit berechnen?

Nein, es ist kein Gegenteil, sondern eine andere Sache. Es gibt ein paar Qt-Bugs, die als No-Bugs geschlossen sind: QTBUG-55449 und QTBUG-55510, die die Absicht hinter dem Feature zeigen.BTW, gibt es QTBUG-55510 eine programmatische Problemumgehung für die Einstellung Qt DPI Bekanntheit ohne Fixierung qt.conf wird zur Verfügung gestellt (Verwendung nach eigenem Ermessen, weil es 'private' Qt Implementierungsklassen verwendet, die die Schnittstelle ohne Hinweis mit neueren Qt-Version ändern).

Und Sie drückten den richtigen Ansatz aus, um die Skalierung im pro-Monitor DPI-bewussten Modus durchzuführen. Leider außer dass es zu dieser Zeit keine Alternative gibt. Es gibt jedoch programmatische Möglichkeiten, die Ereignisbehandlung für die Fensterskalierung zu unterstützen, wenn es von einem Monitor zu einem anderen verschoben wird. Das Verfahren wie resizeWidget (eines, nicht viele) an der Spitze dieser Frage sollte wie (Windows) mit so etwas genannt werden:

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

Das ist ziemlich schwierig und mühsam Weg zu gehen, und ich griff die App aktivieren Wechsle zwischen dem DPI Aware-Modus und dem DPI Aware-Modus, indem der Benutzer den Modus auswählt und den App-Prozess erneut startet (entweder qt.conf oder eine Umgehungslösung von QTBUG-55510 beim Start der App). Wir hoffen, dass die Firma Qt erkennt, dass ein DPI-fähiger Modus für jeden Monitor mit automatischer Skalierung für Widgets erforderlich ist. Warum sollten wir es brauchen (?) Ist eine andere Frage. In meinem Fall habe ich per-Monitor-Rendering in eigenen App-Widget-Leinwand, die skaliert werden sollte.

Zunächst auf diese Frage den Kommentar zu lesen von @selbie ich vielleicht realisiert gibt es einen Weg, um zu versuchen QT_SCREEN_SCALE_FACTORS zu setzen, während die App startet:

QT_SCREEN_SCALE_FACTORS [Liste] Skalierungsfaktoren für jeden Bildschirm spezifiziert . Dies ändert nicht die Größe von Schriftarten in Punktgröße. Diese Umgebungsvariable ist hauptsächlich nützlich für das Debuggen oder um Monitore mit falschen EDID-Informationen zu arbeiten (Extended Display Identification Daten).

Ich las dann Qt blog, wie mehrere Bildschirme Faktoren anzuwenden und versuchte, die unten für 4K und 1080p-Monitore zu tun, wo 4K zuerst genannt (Haupt-).

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

Das tut hilft ein bisschen: fast richtig zu machen aber Defekte mit Fenstergröße führt, während das Fenster von einem Monitor zum anderen ziemlich viel wie QTBUG-55449 tut. Ich denke, ich werde mit WM_DPICHANGED + QT_SCREEN_SCALE_FACTORS Ansatz gehen, wenn der Kunde das aktuelle App-Verhalten als Fehler betrachtet (wir machen die gleiche Basis für alle Monitore DPI über System DPI Aware). Noch gibt es keine fertige Lösung von Qt.

+0

danke für die Antwort. Der Mangel an Unterstützung für einfache High-Dpi-Lösung wie in Android-Programmierung macht mich verrückt. Gibt es etwas, das ich meiner resizeWidgets-Funktion hinzufügen sollte?Kennen Sie eine Opensource Demo App mit Qt Highdpi Funktionen? thx –

+0

@ TomášNavara Noch keine Opensource-Lösung dafür. Ich habe versucht und gehe so ist ein Minenfeld zu viel Routine Skalierung zu tun. Sie zeigen bereits, was programmgesteuert zu tun ist, aber in Windows können Sie WM_DPICHANGED nutzen, die Einstellung MAYBE QT_SCREEN_SCALE_FACTORS [list] funktioniert ABER für bereits verbundene Monitore, wenn Sie die App starten. Ich habe dazu ein paar Statements hinzugefügt. – AlexanderVX

+0

also nur um zu verdeutlichen - welchen Vorteil hat es, dpiawareness = 2 zu spezifizieren, wenn ich die ganze Arbeit selbst aus dem Code mache, nachdem ich das dpichange-Ereignis erhalten habe? und was ist mit Linux? –

Verwandte Themen