1. 程式人生 > >芭蕉樹上第十六根芭蕉-- Qt中Ui名字空間以及setupUi函式的原理和實現

芭蕉樹上第十六根芭蕉-- Qt中Ui名字空間以及setupUi函式的原理和實現

用最新的QtCreator選擇GUI的應用會產生含有如下檔案的工程

下面就簡單分析下各部分的功能。

.pro檔案是供qmake使用的檔案,不是本文的重點【不過其實也很簡單的】,在此不多贅述。

所以呢,還是從main開始,

  1. #include <QtGui/QApplication>

  2. #include "mainwindow.h"

  3. int main(int argc, char *argv[])

  4. {

  5. QApplication a(argc, argv);

  6. MainWindow w;

  7. w.show();

  8. return a.exec();

  9. }

很簡單的樣子

QApplication a(argc, argv)和a.exec()可以理解為載入了Qt的架構,跑Qt的程式都要有此部,就不多說了。

其中呼叫了個MainWindow並把它show了出來,具體分析下

下面是mainwindow.h中的內容

  1. #ifndef MAINWINDOW_H

  2. #define MAINWINDOW_H

  3. #include <QtGui/QMainWindow>

  4. namespace Ui

  5. {

  6. class MainWindow;

  7. }

  8. class MainWindow : public QMainWindow

  9. {

  10. Q_OBJECT

  11. public:

  12. MainWindow(QWidget *parent = 0);

  13. ~MainWindow();

  14. private:

  15. Ui::MainWindow *ui;

  16. };

  17. #endif // MAINWINDOW_H

開始的namespace Ui可能讓人有點摸不著頭腦,這是因為qt把ui相關的類單獨獨立了出來,但類名相同,禁用namespace區別【但是就目前的使用來說,感覺這樣做不怎麼好,後面我會解釋原因】

宣告namespace Ui是因為要呼叫Ui中的MainWindow,此MainWindow非彼MainWindow,後面涉及的*ui指標會呼叫它!

關於Q_OBJECT就不說了,Qt中與signal和slot相關的類都要這麼宣告下。

仔細看出了構造,析構就沒啥了,只有那麼個*ui!不過現在如果執行下,也只會生成個窗體而已。

下面來看建構函式和解構函式,其實也就是mainwindow.c

  1. #include "mainwindow.h"

  2. #include "ui_mainwindow.h"

  3. MainWindow::MainWindow(QWidget *parent)

  4. : QMainWindow(parent), ui(new Ui::MainWindow)

  5. {

  6. ui->setupUi(this);

  7. }

  8. MainWindow::~MainWindow()

  9. {

  10. delete ui;

  11. }

構造時在堆上new了個Ui域中的MainWindow,並呼叫setupUi,析構僅僅是將其delete了,還是很簡單!

正如前面所述Qt很好的把ui分離了出去,前面圖中的那個.ui檔案就是讓QtDesigner使的佈局用檔案!

現在執行下,會生成ui_mainwindow.h,這個裡面會涉及到真正佈局用的函式,也就是那個Ui域中的MainWindow.下面具體看一下,

  1. #ifndef UI_MAINWINDOW_H

  2. #define UI_MAINWINDOW_H

  3. #include <QtCore/QVariant>

  4. #include <QtGui/QAction>

  5. #include <QtGui/QApplication>

  6. #include <QtGui/QButtonGroup>

  7. #include <QtGui/QHeaderView>

  8. #include <QtGui/QMainWindow>

  9. #include <QtGui/QMenuBar>

  10. #include <QtGui/QStatusBar>

  11. #include <QtGui/QToolBar>

  12. #include <QtGui/QWidget>

  13. QT_BEGIN_NAMESPACE

  14. class Ui_MainWindow

  15. {

  16. public:

  17. QMenuBar *menuBar;

  18. QToolBar *mainToolBar;

  19. QWidget *centralWidget;

  20. QStatusBar *statusBar;

  21. void setupUi(QMainWindow *MainWindow)

  22. {

  23. if (MainWindow->objectName().isEmpty())

  24. MainWindow->setObjectName(QString::fromUtf8("MainWindow"));

  25. MainWindow->resize(600, 400);

  26. menuBar = new QMenuBar(MainWindow);

  27. menuBar->setObjectName(QString::fromUtf8("menuBar"));

  28. MainWindow->setMenuBar(menuBar);

  29. mainToolBar = new QToolBar(MainWindow);

  30. mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));

  31. MainWindow->addToolBar(mainToolBar);

  32. centralWidget = new QWidget(MainWindow);

  33. centralWidget->setObjectName(QString::fromUtf8("centralWidget"));

  34. MainWindow->setCentralWidget(centralWidget);

  35. statusBar = new QStatusBar(MainWindow);

  36. statusBar->setObjectName(QString::fromUtf8("statusBar"));

  37. MainWindow->setStatusBar(statusBar);

  38. retranslateUi(MainWindow);

  39. QMetaObject::connectSlotsByName(MainWindow);

  40. } // setupUi

  41. void retranslateUi(QMainWindow *MainWindow)

  42. {

  43. MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0, QApplication::UnicodeUTF8));

  44. Q_UNUSED(MainWindow);

  45. } // retranslateUi

  46. };

  47. namespace Ui {

  48. class MainWindow: public Ui_MainWindow {};

  49. } // namespace Ui

  50. QT_END_NAMESPACE

  51. #endif // UI_MAINWINDOW_H

吼吼,一下子多了不少,但其實還是很容易的。Ui_MainWindow聲明瞭幾個構件,具體我就不說了,因為也沒啥可說的,它實現了setupUi函式,也就是前面那個MainWindow中呼叫的setupUi。

但是要說明的是QMetaObject::connectSlotsByName函式會自動連線相應名稱的訊號與槽,但要注意它連線的是傳入的MainWindow及其子構件【不是子類】,注意前邊ui->setupUi(this)中傳入的this,也就是非ui域中的MainWindow,所以如果要宣告signal和slot時還是要在非ui域的MainWindow中來宣告,然後通過ui->xxx的形式來與GUI產生互動!如果我們在QtDesiner中拖放一個按鈕然後點選go to slot就很容易印證這一點。

retranslateUi則會為ui中的構件命名,具體也不在此多說。

最後還是看看這段程式碼

  1. namespace Ui {

  2. class MainWindow: public Ui_MainWindow {};

  3. } // namespace Ui

前面非Ui域中的MainWindow的*ui指向的是Ui域中的MainWindow,而Ui域中的MainWindow出了繼承了Ui_MainWindow之外,內部一貧如洗!【有點繞口了】

來張圖片,再複習下

最後要說明的有兩點,個人感覺是QtCreator的BUG,

其一是如果自己定製控制元件,並且想在內建的designer中載入,win下用mingw是不可行的,因為sdk套件中的designer是用微軟的編譯器編譯的,當然也有個比較方便的解決的辦法,就是把qtcreator的原始碼下來,用現有的creator再編譯一遍,然後覆蓋過去就行了。

其二也是前面提到的,兩個同名的MainWindow僅用Ui域來區分,雖然感覺這樣做從設計上來說是很美的,但除錯時卻會有些許的問題,總之在creator中除錯不能識別正確的域,具體見下圖例

 

像上面這張圖this實際上應該指向的是Ui域中的MainWindow【this其實指向的是MainWindow,它並不知是哪個域的MainWindow,再往下展開就錯誤的指向了Ui域】,但除錯的資料區指向了Ui域中的MainWindow,當然也不是沒有解決的辦法,你可以手工將Ui域中的MainWindow改下名就可以獲得正確的除錯資訊了,只是這樣做稍顯麻煩,而且再度執行qmake後可能還要重新修改。