基于Qt的多窗口编程--界面的设计

来源:互联网 发布:数据库原理pdf百度云 编辑:程序博客网 时间:2024/06/06 16:38

对于应用程序中的多窗体切换,我们已经习以为常。通常一个应用程序中,不同的窗口代表不同功能的工作区。本文将详细描述基于Qt的多窗体程序的设计方法。在阅读本文之前,你最好了解面向对象的基本思想以及Qt的基本使用方法。

接下来我们以编写一个客户端为例来具体说明多窗体程序的编程方法。该客户端包括多个子系统,每个子系统对应一个窗口;在客户端的主界面,通过点击相应的按钮实现多个窗体之间的切换。主界面图如下:

 

如图所示,该客户端主界面包含顶部的标签、右方的按钮区、中间的主窗口区以及底部的状态栏。通过点击不同的按钮则进入不同的子系统。配置信息子系统和日志信息管理子系统界面设计如下:

布局管理

上述三个界面都可以通过Qt Designer轻松设计完成,这里只对窗体部件的布局管理作说明。通常我们可以显示指定窗体中各个部件的大小以及位置,但是这样麻烦而且缺少灵活性。因此在实际的应用中,使用布局管理器对窗体中的各个子部件进行布局管理。布局管理器会为每个窗体部件提供合理的默认值,并随着个别部件大小的变化而调整整体的窗体布局。

常用的布局管理器有QHBoxLayout、QVBoxLayout和QGridLayout,即依次为水平布局管理、垂直布局管理器和网格布局管理器。结合上面的主界面图示,其对应的布局管理图如下:

使用布局管理器的一般方法为:先创建相应布局管理器的对象;再将要进行布局管理窗体部件加入到布局管理器对象中;为当前窗体设置该布局管理器。除了可以将窗体部件加入到当前的布局管理器中,还可以将另外一个布局管理器对象加入到当前的布局管理器中;此外,设置布局管理器只需在所有布局管理器都添加好之后进行一次的设置即可。具体使用方法可参考随后给出的示例代码。

在topLayout这个顶部的布局管理器中,我们仅加入一个标签。正如上面所述,我们首先创建一个水平布局管理器对象,再将这个mainlabel标签加入其内。

1QHBoxLayout *topLayout = new QHBoxLayout;
2topLayout->addWidget(ui->mainLabel);

右边的按钮区的布局管理rightBtnLayout和上述类似,只不过我们此时使用的是垂直管理器。接下来重点说一下主窗口区布局管理器widgetLayout的设置。两个子系统和主界面的功能不同,因此将他们分别封装成类configUI、logUI和frontPage,我们依次创建三个类的对象。接下来将这三个部件加入到widgetLayout中。由于客户端初始显示主界面,则我们将其他两个窗体暂时隐藏。

1QVBoxLayout *widgetLayout = new QVBoxLayout;
2configWidget = new configUI(this);
3logWidget = new logUI(this);
4mainMenu = new frontPage(this);
5widgetLayout->addWidget(ui->introTextBrowser);
6widgetLayout->addWidget(configWidget);
7widgetLayout->addWidget(logWidget);
8configWidget->hide();
9logWidget->hide();

我们现在已经设置好了rightBtnLayout和widgetLayout,接下来将这两个布局管理器加入到mainWindowLayout中。

1QHBoxLayout *mainWindowLayout = new QHBoxLayout;
2mainWindowLayout->addLayout(widgetLayout);
3mainWindowLayout->addLayout(rightBtnLayout);

这样mainWindowLayout布局管理器就设置好了。最后我们还需要设置bottomLayout布局管理器,只是简单的添加一个标签和一个水平分割线。

至此,我们拥有三个二级布局管理器:topLayout、mainWindowLayout和bottomLayout,我们将这三个布局管理器全部添加到顶级布局管理器mainLayout中:

view source
print?
1QVBoxLayout *mainLayout = new QVBoxLayout;
2 mainLayout->addLayout(topLayout);
3 mainLayout->addLayout(mainWindowLayout);
4 mainLayout->addLayout(bottomLayout);
5 this->adjustSize();
6 
7 setLayout(mainLayout);

此时,我们就设置好了主界面的布局。


窗体类的关系

在布局管理部分,我们已经说过将主界面和两个子系统分别封装成三个不同的类。首先我们讨论每个类中应该封装什么,其次再讨论这三个类之间的具体关系。

通过Qt Dsigner设计好界面后会在工程文件中对应一个.ui的文件;编译器会将该文件转换成能被C++所识别的.h文件。比如configUI.ui文件就对应一个ui_configUI.h文件,该头文件中包含了类Ui::configUI的定义。这个类完全与我们所设计的用户界面等价,也就是说它包含对主界面中各个部件的定义、该界面的布局以及初始化窗体的setupUi()等。

但是应该注意的是,该类仅仅是对我们设计的界面的一种等价转化。我们对该界面的实际操作(比如对各种槽以及信号的定义)并不包含在该类中。为了完成配置系统的具体功能,我们会创建一个新类configUI,并让这个新类继承Ui::configUI。通常在configUI类中会有一个Ui::configUI类的成员变量ui,因此可以通过ui直接获取界面中的各个部件。

注意,由于界面所对应的类configUI被定义在UI这个名字空间中,因此两个同名的类并不会发生名字冲突。这两个类的关系可以通过下面的代码进一步理解:

[c] view plaincopy
  1. //将界面对应的configUI类定义在Ui名字空间中;  
  2. namespace Ui {  
  3.     class configUI;  
  4. }  
  5.   
  6. class configUI : public QWidget  
  7. {  
  8.     Q_OBJECT  
  9.   
  10. public:  
  11.     explicit configUI(QWidget *parent = 0);  
  12.     ~configUI();  
  13. //将 Ui::configUI的对象作为configUI的成员变量;  
  14. private:  
  15.     Ui::configUI *ui;  
  16.   
  17. private slots:  
  18. //在此定义槽函数;  
  19. };  

在configUI类中,还可以根据需要包含一些成员函数、信号和槽函数等。其他两个界面对应的类也有类似的封装关系,不再赘述。

在布局管理中,通过点击主界面上的按钮就可以切换到相应的窗体。由此引发出我们对这几个窗体类之间关系的思考。具体的做法是,我们可以将两个子系统对应类的对象作为主界面类的成员变量,比如:

[c] view plaincopy
  1. namespace Ui {  
  2.     class frontPage;  
  3. }  
  4.   
  5. class frontPage : public QWidget  
  6. {  
  7.     Q_OBJECT  
  8.   
  9. public:  
  10.     explicit frontPage(QWidget *parent = 0);  
  11.     ~frontPage();  
  12.   
  13. private:  
  14.     Ui::frontPage *ui;  
  15.     configUI *configWidget;  
  16.     logUI *logWidget;  
  17.   
  18. signals:  
  19.     //在此定义信号;  
  20.   
  21. private slots:  
  22.    //在此定义槽函数;  
  23. };  

那么在切换各个窗体时,就可以方便的通过show()和hide()近来完成。至此,我们已经完成了界面的设计和类的定义,下面要做的就是实现具体窗口的跳转工作。

信号和槽函数的设计

由上文可得知,我们要实现的功能即通过点击每个按钮就可以跳转到相应的窗口。所以三个窗体对应的按钮就对应三个槽函数,触发这几个槽函数的信号即为clicked()。在类frontPage中对上述三个槽函数的声明如下:

[c] view plaincopy
  1. signals:  
  2.     void goToWidget(int);  
  3. private slots:  
  4.     void on_logSysBtn_clicked();  
  5.     void on_frontPageBtn_clicked();  
  6.     void on_configSysBtn_clicked();  
  7.     void on_quitBtn_clicked();  
  8.     void runningWidget(int);  
  9.     void on_quitBtn_clicked();  

除了三个窗口按钮对应的槽函数外,还包含其他函数的声明,我们在稍候会解释。按照以往的做法,我们声明了按钮对应的槽函数后,就应该依次去实现这些函数以便实现窗体间的跳转。不过,由于本程序中窗体跳转之间有一定的规律,所以将采用下面的方法:

[c] view plaincopy
  1. void frontPage::on_frontPageBtn_clicked()  
  2. {  
  3.     emit goToWidget(0);  
  4. }  
  5.   
  6. void frontPage::on_configSysBtn_clicked()  
  7. {  
  8.     emit goToWidget(1);  
  9. }  
  10.   
  11. void frontPage::on_logSysBtn_clicked()  
  12. {  
  13.     emit goToWidget(2);  
  14. }  

也就是说,在每个按钮对应的槽函数中再次发送goToWidget(int)信号,不同按钮将传递不同的整数。在这里我们使用emit关键字显示的发送信号,这和平时我们在Qt Designer中所使用的关联方发不同,但是本质是相同的。由于这个信号是我们自己定义的,并且信号本身就是一个函数,因此需要在frontPage类中对这个信号进行声明,具体可参见上面的示例代码。

我们将此信号和槽函数runningWidget(int)关联。也就是说点击不同窗体对应的按钮都会执行runningWidget(int)槽函数。只不过在该函数内部则会根据所传递的整形参数执行不同的程序段。runningWidget函数的示例代码如下:

[c] view plaincopy
  1. void frontPage::runningWidget(int widgetNum)  
  2. {  
  3.     switch (widgetNum) {  
  4.     case 0:  
  5.         ui->introTextBrowser->show();  
  6.         configWidget->hide();  
  7.         logWidget->hide();  
  8.         break;  
  9.     case 1:  
  10.         ui->introTextBrowser->hide();  
  11.         configWidget->show();  
  12.         logWidget->hide();  
  13.         break;  
  14.     case 2:  
  15.         ui->introTextBrowser->hide();  
  16.         configWidget->hide();  
  17.         logWidget->show();  
  18.         break;  
  19.     }  
  20. }  

从上面的代码中可以看出,通过传递不同的整形参数就可以显示三个窗体中的某一个,并同时隐藏另外两个窗体。通过上面的方法,这样就可以实现多窗体之间的切换了。另外,退出按钮的槽函数的实现基本方法即为直接调用exit函数。