很多時候運行時的樣子,並不是我們想要的,所以說我們可以用Qt給我們提供的Qt Designer
(界面設計師),拖拖拽拽就可以直觀的創建出程序大體的界面。
1.1 創建帶UI文件的項目
-
雙擊.ui文件打開設計師界面
-
設計師面板介紹
-
組件面板:窗口左側是界面設計組件面板,分為多個組,如Layouts、Buttons、Display Widgets等,界面設計的常見組件都可以在組件面板里找到。
-
中間主要區域是待設計的窗體。如果要將某個組件放置到窗體上時,從組件面板上拖放一個組件到窗體上即可。例如,先放一個 Label 和一個 Push Button 到窗體上。
-
Signals 和 Slots 編輯器與 Action 編輯器是位於待設計窗體下方的兩個編輯器。Signals 和Slots 編輯器用於可視化地進行信號與槽的關聯,Action 編輯器用於可視化設計 Action。
-
布局和界面設計工具欄:窗口上方的一個工具欄,工具欄上的按鈕主要實現布局和界面設計。
-
對象瀏覽器(Object Inspector):窗口右上方是 Object Inspector,用樹狀視圖顯示窗體上各組件之間的布局包含關系,視圖有兩列,顯示每個組件的對象名稱(ObjectName)和類名稱。
-
屬性編輯器(Property Editor):窗口右下方是屬性編輯器,是界面設計時最常用到的編輯器。屬性編輯器顯示某個選中的組件或窗體的各種屬性及其取值,可以在屬性編輯器里修改這些屬性的值。
-
1.2 UI設計器使用
1,轉到槽
-
遞歸搜索給定對象的所有子對象,並將來自它們的匹配信號連接到遵循以下形式的對象槽:
void on_<object name>_<signal name>(<signal parameters>);
讓我們假設我們的對象有一個 QPushButton 類型的子對象,對象名為 button1。 捕捉按鈕的 clicked() 信號的插槽是:
void on_button1_clicked();
如果對象本身具有正確設置的對象名稱,則其自身的信號也連接到其各自的插槽。
2,自定義控件(提升為)
1.3 UI設計師原理
就算我們不編寫一行程序語句,都能實現界面功能,Qt 是怎么實現的呢?
為了搞清楚Widget類的定義,以及界面功能的實現原理,先將項目進行編譯。編譯后在項目目錄下會自動生成一個文件 ui_widget.h,這樣對於一個窗口,就有 4 個文件了,各文件的功能說明見下表。
文件名 | 描述 |
---|---|
widget.h | 定義Widget類的頭文件 |
widget.cpp | Widget 類的功能實現源文件 |
widget.ui | 界面文件,由UI設計器自動生成,存儲了窗體上各個組件的屬性設置和布局 |
ui_widget.h | 編譯后,根據窗體上的組件及其屬性、信號與槽的關聯等自動生成的一個類的定義文件,類的名稱是Ui_Widget |
下面分別分析各個文件的內容及其功能,以及它們是如何聯系在一起工作,實現界面的創建與顯示的。
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
-
namespace申明
代碼中有如下的一個 namespace 聲明:
namespace Ui { class Widget; }
這是聲明了一個名為 Ui 的命名空間(namespace),包含一個類 Widget。但是這個類 Widget 並不是本文件里定義的類 Widget,而是 ui_widget.h 文件里定義的類,用於描述界面組件的。這個聲明相當於一個外部類型聲明(具體要看完 ui_widget.h 文件內的解釋之后才能搞明白)。
-
private指針定義
Ui::Widget *ui;
這個指針是用前面聲明的 namespace Ui 里的 Widget 類定義的,所以指針 ui 是指向可視化設計的界面,后面會看到要訪問界面上的組件,都需要通過這個指針 ui。
Widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
-
注意到,在這個文件的頭文件包含部分自動加入了如下一行內容
#include "ui_widget.h"
這個就是 Qt 編譯生成的與 UI 文件 widget.ui 對應的類定義文件。
-
在構造函數初始化參數列表里有這么一段代碼
ui(new Ui::Widget)
其意義是:執行父類 QWidget 的構造函數,創建一個 Ui::Widget 類的對象 ui。這個 ui 就是 Widget 的 private 部分定義的指針變量 ui。
-
構造函數里只有一行代碼
ui->setupUi(this);
它是執行了 Ui::Widget 類的 setupUi() 函數,這個函數實現窗口的生成與各種屬性的設置、信號與槽的關聯(后面會具體介紹)。
-
析構函數只是簡單地delete用 new 創建的指針 ui。
-
所以,在 ui_widget.h 文件里有一個 namespace 名稱為 Ui,里面有一個類 Widget 是用於描述可視化設計的窗體,且與 widget.h 里定義的類同名。在 Widget 類里訪問 Ui::Widget 類的成員變量或函數需要通過 Widget 類里的 ui 指針,如同構造函數里執行 ui->setupUi( this) 函數那樣。
Widget.ui
widget.ui 是界面定義文件,是一個 XML 文件,定義了窗口上的所有組件的屬性設置、布局,及其信號與槽函數的關聯等。用UI設計器可視化設計的界面都由 Qt 自動解析,並以 XML 文件的形式保存下來。在設計界面時,只需在 UI 設計器里進行可視化設計即可,而不用管 widget.ui 文件是怎么生成的。
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Widget</string>
</property>
</widget>
<resources/>
<connections/>
</ui>
ui_Widget.h
ui_widget.h 是在對 widget.ui 文件編譯后生成的一個文件,ui_widget.h 會出現在編譯后的目錄下,或與 widget.ui 同目錄(與項目的 shadow build 編譯設置有關)。
文件 ui_widget.h 並不會出現在 Qt Creator 的項目文件目錄樹里,當然,可以手工將 ui_widget.h 添加到項目中。方法是在項目文件目錄樹上,右擊項目名稱節點,在調出的快捷菜單中選擇“Add Existing Files…”,找到並添加 ui_widget.h 文件即可。
注意,ui_widget.h 是對 widget.ui 文件編譯后自動生成的,widget.ui 又是通過 UI 設計器可視化設計生成的。所以,對 ui_widget.h 手工進行修改沒有什么意義,所有涉及界面的修改都應該直接在UI 設計器里進行。所以,ui_widget.h 也沒有必要添加到項目里。
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_Widget
{
public:
void setupUi(QWidget *Widget)
{
if (Widget->objectName().isEmpty())
Widget->setObjectName(QString::fromUtf8("Widget"));
Widget->resize(800, 600);
retranslateUi(Widget);
QMetaObject::connectSlotsByName(Widget);
} // setupUi
void retranslateUi(QWidget *Widget)
{
Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));
} // retranslateUi
};
namespace Ui {
class Widget: public Ui_Widget {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_WIDGET_H
-
查看 ui_widget.h 文件的內容,發現它主要做了以下的一些工作:
-
1,定義了一個類 Ui_Widget,用於封裝可視化設計的界面。
-
2,自動生成了界面各個組件的類成員變量定義。在 public 部分為界面上每個組件定義了一個指針變量,變量的名稱就是設置的 objectName。比如,在窗體上放置了一個 QLabel 和一個 QPushButton 並命名后,自動生成的定義是:
QLabel *LabDemo; QPushButton *btnClose;
-
定義了 setupUi() 函數,這個函數用於創建各個界面組件,並設置其位置、大小、文字內容、字體等屬性,設置信號與槽的關聯。setupUi() 函數體的第一部分是根據可視化設計的界面內容,用 C++ 代碼創建界面上各組件,並設置其屬性。
接下來,setupUi() 調用了函數 retranslateUi(Widget),用來設置界面各組件的文字內容屬性,如標簽的文字、按鍵的文字、窗體的標題等。將界面上的文字設置的內容獨立出來作為一個函數 retranslateUi(),在設計多語言界面時會用到這個函數。
setupUi() 函數的第三部分是設置信號與槽的關聯:
QMetaObject::connectSlotsByName(Widget);
該代碼是設置槽函數的關聯方式,用於將 UI 設計器自動生成的組件信號的槽函數與組件信號相關聯。
所以,在Widget 的構造函數里調用 ui->setupUI(this),就實現了窗體上組件的創建、屬性設置、信號與槽的關聯。
-
定義 namespace Ui,並定義一個從Ui_Widget 繼承的類Widget。
namespace Ui { class Widget: public Ui_Widget {}; }
-
提示:ui_widget.h 文件里實現界面功能的類是 Ui_Widget。再定義一個類 Widget 從 Ui_Widget 繼承而來,並定義在 namespace Ui 里,這樣 Ui:: Widget 與 widget.h 里的類 Widget 同名,但是用 namespace 區分開來。所以,界面的 Ui:: Widget 類與文件 widget.h 里定義的 Widget 類實際上是兩個類,但是 Qt 的處理讓用戶感覺不到 Ui:: Widget 類的存在,只需要知道在 Widget 類里用 ui 指針可以訪問可視化設計的界面組件就可以了
1.4 用Ui文件生成頭文件
-
1,打開終端,並切換工作目錄到.ui文件所在的目錄
-
2,輸入如下命令,可以看到ui_widget.h文件就生成出來了
uic widget.ui -o ui_widget.h
1.5 使用ui_widget.h
一句話,要讓ui designer設計出來的界面顯示出來,只要能設法調用Ui_widget類的setupUi函數就行了。
三步即可完成
-
1,前置聲明
//widget.h namespace Ui { class widget; }
-
2,類中定義
//widget.h private: Ui::widget* ui;
-
3,申請內存,設置ui
//widget.cpp Widget::Widget(QWidget *parent) : QWidget(parent) ,ui(new Ui::widget) //給ui指針分配內存 { ui->setupUi(this); //給當前窗口this,設置ui界面 }