2016-10-03 137 views
11

我已閱讀官方Qt documentation以及StackOverflow上關於Qt中對高DPI支持的許多文章和問題。他們都專注於移植舊應用程序,並儘可能少地進行修改。如何開發新的Qt 5.7+高DPI每個監視器DPI感知應用程序?

但是,如果我要啓動一個全新的應用程序,並且打算支持每臺顯示器支持DPI的應用程序,那麼最佳方法是什麼?

如果我理解正確,Qt::AA_EnableHighDpiScaling是我想要的相反。我應該實際上禁用HighDpiScaling並在運行時手動計算所有尺寸?

許多建議表示不使用大小,而是使用浮動佈局。但是在許多情況下,希望存在至少最小寬度和/或最小高度。由於Qt Designer只允許我以絕對像素來表示值,所以正確的方法是什麼?如果顯示器分辨率發生變化,我應該在哪裏放置代碼來重新計算尺寸?

或者我應該只是去自動縮放?

從以前的Qt應用程序我的解決方案(沒有得到很好的測試)

在我的舊的應用程序,我試圖添加HighDPI扶持的企業之一,我用這個方法 - 列表DOM的所有兒童,並調整它們的大小一一給出一些比例。 Ratio = 1會產生與我在Qt Designer中指定的尺寸相同的尺寸。

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應用程序?

+0

在Win32上,我們發現成功簡單地查詢顯示器dpi設置的操作系統(GetDeviceCaps),除以96.0,並設置env變量'QT_SCALE_FACTOR'以匹配。有權衡。在Mac上,我們不需要做任何事情。因人而異。 – selbie

+0

@selbie,如何設置QT_SCALE_FACTOR地址每監視器縮放? – AlexanderVX

+0

也許這個QT_SCREEN_SCALE_FACTORS [list]指定每個屏幕的比例因子。這不會更改磅值字體的大小。此環境變量主要用於調試,或者用錯誤的EDID信息(擴展顯示標識數據)解決顯示器問題。但不確定,任何人都在嘗試從應用內設置它? – AlexanderVX

回答

7

如果我要啓動一個全新的應用程序,並打算通過支持每臺顯示器的DPI認識 ,那麼最佳方法是什麼?

我們不依賴Qt自動縮放每個顯示器的DPI感知模式。至少基於Qt 5.7的應用程序Qt::AA_EnableHighDpiScaling集不會這樣做,「高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-55449QTBUG-55510顯示了該功能背後的意圖。順便說一句,有QTBUG-55510編程解決方案設置Qt DPI意識而不修復qt.conf提供(由於它使用'私人'的Qt實現類,沒有任何通知與更新的Qt版本改變接口自行決定使用。

而且您表達了正確的方法來執行每個顯示器DPI感知模式下的縮放比例。不幸的是除了當時沒有太多選擇。不過,有一些編程方式可以幫助窗口縮放時的事件處理,當它從一個監視器移動到另一個監視器時。在這個問題的頭似resizeWidget的方法(一個,也不多)應該使用類似(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; 

這是相當困難的,去麻煩的路,我使出使應用通過讓用戶選擇模式並重新啓動應用程序進程(修復qt.conf或在應用程序啓動時從QTBUG-55510開始解決方法)在系統和每臺顯示器DPI感知模式之間切換。我們希望Qt公司意識到需要針對小部件自動縮放的每個監視器DPI感知模式。爲什麼我們需要它(?)是另一個問題。在我的情況下,我有每個監視器在自己的應用程序小部件畫布內渲染,應該縮放。

首先,從@selbie我意識到讀書的評論對這個問題也許有一種方法來嘗試設置QT_SCREEN_SCALE_FACTORS而應用程序啓動:

QT_SCREEN_SCALE_FACTORS [名單]指定每個 屏幕比例因子。這不會更改磅值字體的大小。這個 環境變量主要用於調試,或者爲解決錯誤的EDID信息(擴展顯示標識 數據)而工作的 監視器。

我然後讀取關於如何應用多個屏幕要素Qt blog並試圖對於其中4K列出第一(主)4K和1080p監視器執行以下。

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

這確實有點幫助:幾乎正確渲染引入缺陷與窗口的大小,同時從一個監視器移動窗口到另一個很像QTBUG-55449一樣。如果客戶認爲當前的應用程序行爲是一個錯誤(我們通過System DPI Aware爲所有顯示器DPI創建相同的基礎),我想我會採用WM_DPICHANGED + QT_SCREEN_SCALE_FACTORS方法。儘管如此,Qt還沒有準備好使用的解決方案。

+0

謝謝你的答案。在Android編程中缺乏對簡單的highdpi解決方案的支持使我瘋狂。有什麼我應該添加到我的resizeWidgets功能?你知道任何與Qt highdpi功能的開源演示應用程序嗎? thx –

+0

@TomášNavara尚未察覺到開源解決方案。我嘗試過這種方式是一個做太多例程縮放的礦區。您已經顯示了對需要以編程方式完成的事情的理解,但是在Windows中,您可以利用WM_DPICHANGED,MAYBE設置QT_SCREEN_SCALE_FACTORS [list]將在您啓動應用程序時適用於已連接的顯示器。我已經爲此添加了幾個聲明。 – AlexanderVX

+0

所以只是爲了澄清 - 當我在接收dpichange事件時從代碼中完成所有工作時,它有什麼優勢來指定dpiawareness = 2?那麼linux呢? –