6-1 排列窗體上的控件(Laying Out Widgets on a Form)

 
中英文對照:
form(窗體),layout(佈局或者排列,意思是進行窗體上控件的排列的過程,如大小位置等)
absolute positioning(絕對位置定位),manual layout(手工佈局), layout managers(佈局管理器)
Qt中有三種方式對窗體上的控件進行佈局管理:絕對位置定位(absolute positioning),手工佈局(manual layout),佈局管理器(layout managers)。我們使用圖6.1中的對話框爲例對這三種方式分別進行說明。
Figure 6.1. The Find File dialog
  
絕對位置定位的方法是最原始的排列控件的方法。這個方法是在程序中調用控件的函數設定它的位置和相對窗體它的大小。下面是用着個方法實現的FindFileDialog的構造函數。
FindFileDialog::FindFileDialog(QWidget *parent)
    : QDialog(parent)
{
    ...
    namedLabel->setGeometry(9, 9, 50, 25);
    namedLineEdit->setGeometry(65, 9, 200, 25);
    lookInLabel->setGeometry(9, 40, 50, 25);
    lookInLineEdit->setGeometry(65, 40, 200, 25);
    subfoldersCheckBox->setGeometry(9, 71, 256, 23);
    tableWidget->setGeometry(9, 100, 256, 100);
    messageLabel->setGeometry(9, 206, 256, 25);
    findButton->setGeometry(271, 9, 85, 32);
    stopButton->setGeometry(271, 47, 85, 32);
    closeButton->setGeometry(271, 84, 85, 32);
    helpButton->setGeometry(271, 199, 85, 32);
    setWindowTitle(tr("Find Files or Folders"));
    setFixedSize(365, 240);
}
這種方法缺點很多:
1.         用戶不能改變窗體的大小
2.         如果改變字體或者翻譯到另一種語言,控件上的文本可能不能完全顯示
3.         在一些樣式下,控件的尺寸會不合適
另一種方法爲手工佈局。給出控件的絕對位置,但是他們的尺寸根據窗口的大小確定,可以通過重寫窗體的resizeEvent()實現對子控件的大小設置:
FindFileDialog::FindFileDialog(QWidget *parent)
    : QDialog(parent)
{
    ...
    setMinimumSize(265, 190);
    resize(365, 240);
}
void FindFileDialog::resizeEvent(QResizeEvent * /* event */)
{
    int extraWidth = width() - minimumWidth();
    int extraHeight = height() - minimumHeight();
   namedLabel->setGeometry(9, 9, 50, 25);
    namedLineEdit->setGeometry(65, 9, 100 + extraWidth, 25);
    lookInLabel->setGeometry(9, 40, 50, 25);
    lookInLineEdit->setGeometry(65, 40, 100 + extraWidth, 25);
    subfoldersCheckBox->setGeometry(9, 71, 156 + extraWidth, 23);
    tableWidget->setGeometry(9, 100, 156 + extraWidth,
                             50 + extraHeight);
    messageLabel->setGeometry(9, 156 + extraHeight, 156 + extraWidth,
                              25);
    findButton->setGeometry(171 + extraWidth, 9, 85, 32);
    stopButton->setGeometry(171 + extraWidth, 47, 85, 32);
    closeButton->setGeometry(171 + extraWidth, 84, 85, 32);
    helpButton->setGeometry(171 + extraWidth, 149 + extraHeight, 85,
                            32);
}
在FindFileDialog構造函數中,設置窗體的最小尺寸爲265×190,初始大小爲365×240。在resizeEvent()中,變量extraWidth和extraHeight爲控件相對最小尺寸的差值,根據差值計算子控件的大小,這個在改變窗體大小時控件能夠跟着改變其大小。
Figure 6.2. Resizing a resizable dialog
 
 
絕對位置定位和手工佈局管理都是需要更多的代碼,也需要更多的常量參與計算。這樣編寫代碼非常令人討厭,如果設計改變了,所有的值都要重新計算一遍。雖然手工佈局能改變空間大小,但是有時仍然會無法顯示全部文字,爲了避免這個錯誤,可以考慮控件的sizeHint,但是這樣的代碼會更加複雜了。
管理窗體上控件最簡單的方法就是使用Qt的佈局管理類。這些類能夠給出所有類型控件的默認值,能夠根據控件的字體,樣式,內容得到不同的控件的sizeHint。佈局管理類能夠得到控件的最大,最小尺寸,在字體,內容或者窗口改變時自動調整佈局。
QHBoxLayout,QVBoxLayout,QGridLayout是三個最重要的佈局管理器,這些類從QLayout繼承,QLayout提供佈局最基本的框架。這三個類可以在代碼中使用,也可以在Qt Designer中使用,下面是FindFileDialog使用佈局管理器的代碼
FindFileDialog::FindFileDialog(QWidget *parent)
    : QDialog(parent)
{
    ...
    QGridLayout *leftLayout = new QGridLayout;
    leftLayout->addWidget(namedLabel, 0, 0);
    leftLayout->addWidget(namedLineEdit, 0, 1);
    leftLayout->addWidget(lookInLabel, 1, 0);
    leftLayout->addWidget(lookInLineEdit, 1, 1);
    leftLayout->addWidget(subfoldersCheckBox, 2, 0, 1, 2);
    leftLayout->addWidget(tableWidget, 3, 0, 1, 2);
    leftLayout->addWidget(messageLabel, 4, 0, 1, 2);
    QVBoxLayout *rightLayout = new QVBoxLayout;
    rightLayout->addWidget(findButton);
    rightLayout->addWidget(stopButton);
    rightLayout->addWidget(closeButton);
    rightLayout->addStretch();
    rightLayout->addWidget(helpButton);
    QHBoxLayout *mainLayout = new QHBoxLayout;
    mainLayout->addLayout(leftLayout);
    mainLayout->addLayout(rightLayout);
    setLayout(mainLayout);
    setWindowTitle(tr("Find Files or Folders"));
}
代碼中用到了QHBoxLayout,QGridLayout和QVBoxLayout。窗體的左邊的子控件由QGridLayout負責,右邊的子控件由QVBoxLayout負責。這兩個佈局由QHBoxLayout進行控制。對話框四周的邊緣大小和控件之間的間隔設置爲當前空間樣式的缺省值,函數QLayout::setMargin()和QLayout::setSpacing()能夠對這兩個值進行修改。
這個對話框也可以使用Qt Designer實現,首先把所有的子控件放置在近似適當的位置,選擇需要佈局管理器一同管理的控件,點擊Form|Layout Horizontally,Form|Layout Vertically或者Form|Layout in a Grid。在第二章我們這樣創建了Spreadsheet程序的Go-to-Cell對話框和Sort對話框。
Figure 6.3. The Find File dialog's layout
 
 
QHBoxLayout 和QVBoxLayout的使用很簡單,QGridLayout有點複雜。QGridLayout工作的基礎是一個二維的單元格。左上角的QLabel在佈局中的位置爲(0,0),旁邊的QLineEdit位置爲(0,1)。QCheckBox佔用了(2,0)和(2,1)兩個列的空間,下面的QTreeWidget和QLabel也是如此。QGridLayout::addWidget()語法如下:
layout->addWidget(widget, row, column, rowSpan, columnSpan);
參數widget爲插入到這個佈局的子控件,(row,column)爲控件佔據的左上角單元格位置,rowSpan是控件佔據的行數,colunmSpan是控件佔據的列的個數。rowSpan和colunmSpan默認值爲1。
函數addStretch()使佈局管理器在指定的位置留出一塊空間。上面的代碼中,佈局管理器在Close按鈕和Help按鈕之間留出一個額外的空隙。在Qt Designer中,我們可以加入一個spacer實現這一功能,在Qt Designer中,spacer表現爲藍色的彈簧式折線。
使用佈局管理類還能獲得其他多的功能。如果把一個控件加到一個佈局中,或者從佈局中刪除一個控件,佈局管理器會自動適應變化,調整控件大小。調用子控件的hide()或者show()函數時,佈局管理器同樣也會自動進行調整。如果子控件的sizeHint改變了,佈局管理器就會根據控件新的sizeHint進行調整。根據所有子控件的最小尺寸和sizeHint,佈局管理器還會計算出整個窗體最小尺寸。
在上例中,我們只是把控件放到佈局中,使用spacer(stretches)填滿餘下的空間。有時,光是這些還是不夠的,我們還可以改變控件的sizePolicy,或者sizeHint,使窗體的佈局更加符合我們的需要。
一個控件的sizePolicy說明控件在佈局管理中的縮放方式。Qt提供的控件都有一個合理的缺省sizePolicy,但是這個缺省值有時不能適合所有的佈局,開發人員經常需要改變窗體上的某些控件的sizePolicy。一個QSizePolicy的所有變量對水平方向和垂直方向都適用。下面列舉了一些最長用的值:
1.         Fixed:控件不能放大或者縮小,控件的大小就是它的sizeHint。
2.         Minimum:控件的sizeHint爲控件的最小尺寸。控件不能小於這個sizeHint,但是可以放大。
3.         Maximum:控件的sizeHint爲控件的最大尺寸,控件不能放大,但是可以縮小到它的最小的允許尺寸。
4.         Preferred:控件的sizeHint是它的sizeHint,但是可以放大或者縮小
5.         Expandint:控件可以自行增大或者縮小
圖6.4以文本爲“Some Text”的QLabel顯示了這些不同的sizePolicy的含義,
Figure 6.4. The meaning of the different size policies
 
在圖中,Preferred和Expanding的表現是一樣的,二者的區別何在那?如果一個窗體中既有Preferred控件也有Expanding控件,在改變大小時,由Expanding控件填滿其餘的控件,而Preferred控件不變,認爲它的sizeHint。
還有兩個sizePolicy值爲MinimumExpanding和Ignored。MinimumExpanding在老的Qt版本中有時會用到,但是現在已經不用了。替代的方法時使用Expanding值和重寫合適的minimumSizeHint()函數。Ignored和Expanding很像,只是它忽略控件的sizeHint和最小的sizeHint。
除了水平和垂直方向的值,QSizePolicy還包含了一個水平和垂直方向的放縮倍數(stretch factor)。當窗體放大時,這兩個值決定不同控件放大的程度。例如,如果QTreeWidget和QTextEdit上下排列,如果我們希望QTextEdit高度爲QTreeWidget的兩倍,就可以設置QTextEdit的垂直放縮倍數爲2,QTreeWidget的垂直放縮倍數爲1。
控件的最小尺寸,最大尺寸和固定尺寸也是影響佈局的因素。佈局管理器排列控件時會考慮這些限制。如果這些還不夠,可以創建新類重寫sizeHint()。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章