日志标签 ‘c++’

基于Qt的多窗口设计-窗体切换的实现

2011年3月26日

窗体类的关系

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

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

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

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

//将界面对应的configUI类定义在Ui名字空间中;
namespace Ui {
    class configUI;
}

class configUI : public QWidget
{
    Q_OBJECT

public:
    explicit configUI(QWidget *parent = 0);
    ~configUI();
//将 Ui::configUI的对象作为configUI的成员变量;
private:
    Ui::configUI *ui;

private slots:
//在此定义槽函数;
};

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

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

namespace Ui {
    class frontPage;
}

class frontPage : public QWidget
{
    Q_OBJECT

public:
    explicit frontPage(QWidget *parent = 0);
    ~frontPage();

private:
    Ui::frontPage *ui;
    configUI *configWidget;
    logUI *logWidget;

signals:
    //在此定义信号;

private slots:
   //在此定义槽函数;
};

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

信号和槽函数的设计

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

signals:
    void goToWidget(int);
private slots:
    void on_logSysBtn_clicked();
    void on_frontPageBtn_clicked();
    void on_configSysBtn_clicked();
    void on_quitBtn_clicked();
    void runningWidget(int);
    void on_quitBtn_clicked();

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

void frontPage::on_frontPageBtn_clicked()
{
    emit goToWidget(0);
}

void frontPage::on_configSysBtn_clicked()
{
    emit goToWidget(1);
}

void frontPage::on_logSysBtn_clicked()
{
    emit goToWidget(2);
}

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

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

void frontPage::runningWidget(int widgetNum)
{
    switch (widgetNum) {
    case 0:
        ui->introTextBrowser->show();
        configWidget->hide();
        logWidget->hide();
        break;
    case 1:
        ui->introTextBrowser->hide();
        configWidget->show();
        logWidget->hide();
        break;
    case 2:
        ui->introTextBrowser->hide();
        configWidget->hide();
        logWidget->show();
        break;
    }
}

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

至此,一个基本的多窗体程序就设计完成了。

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

2011年3月25日

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

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

 

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

布局管理

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

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

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

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

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

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

    QVBoxLayout *widgetLayout = new QVBoxLayout;
    configWidget = new configUI(this);
    logWidget = new logUI(this);
    mainMenu = new frontPage(this);
    widgetLayout->addWidget(ui->introTextBrowser);
    widgetLayout->addWidget(configWidget);
    widgetLayout->addWidget(logWidget);
    configWidget->hide();
    logWidget->hide();

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

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

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

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

   QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addLayout(topLayout);
    mainLayout->addLayout(mainWindowLayout);
    mainLayout->addLayout(bottomLayout);
    this->adjustSize();

    setLayout(mainLayout);

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

基于Qt的FTP编程

2011年3月2日

在Qt中,与文件传输协议(FTP)相对应的类为QFtp。在这个类中,提供了许多与文件上传和下载有关的方法,这使得文件的上传与下载变得十分方便。本文所描述的FTP客户端的基本使用方法是:首先输入正确的FTP服务器地址,用户名以及密码;登录成功后将显示服务器中的文件列表;点击下载按钮对某个文件进行下载。用户界面可参考下图:

用户界面设计可见本文参考1和参考2,接下来将对具体的实现过程进行分析。

构造函数

在构造函数中,对用户界面进行了初始化,并隐藏了进度显示条。除此之外,还将itemActivated信号和processItem槽函数进行了连接。当用户双击(或单击,依据具体OS)TreeWidget中的某一条信息时,QTreeWidget类的对象fileList就会发送itemActivated信号。

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->progressBar->setValue(0);

    connect(ui->fileList, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
            this, SLOT(processItem(QTreeWidgetItem*,int)));
}

on_connectButton_clicked()的实现

在连接按钮单击事件的槽函数中主要完成了两部分工作:登录FTP服务器以及几个信号和槽函数之间的连接,这几个信号均由FTP对象发出。当FTP命令开始执行时,便发出commandStarted信号;当FTP的命令执行完毕时,便发出commandFinished信号;当使用list命令列表显示目录的内容时候,listInfo信号将发出;最后在数据传输的过程中,会不断的发出dataTransferProgress信号以便更新进度显示条。

由于QFtp类中封装了许多与FTP有关成员函数,因此使用connectToHost成员函数可以登录到FTP服务器,使用login函数可以登录到服务器。

void Widget::on_connectButton_clicked()
{
    ui->fileList->clear();
    currentPath.clear();
    isDirectory.clear();
    ftp = new QFtp(this);

    connect(ftp, SIGNAL(commandFinished(int,bool)),
            this, SLOT(ftpCommandFinished(int,bool)));
    connect(ftp, SIGNAL(listInfo(QUrlInfo)), this, SLOT(addToList(QUrlInfo)));
    connect(ftp, SIGNAL(dataTransferProgress(qint64,qint64)),
            this, SLOT(updateDataTransferProgress(qint64,qint64)));

    QString ftpServer = ui->ftpServerLineEdit->text();
    QString userName = ui->userNameLineEdit->text();
    QString passWord = ui->passWordLineEdit->text();

    ftp->connectToHost(ftpServer, 21);//连接到服务器,默认端口是21;
    ftp->login(userName, passWord);//登录;
}

ftpCommandFinished()的实现

当FTP命令执行完毕后,将执行ftpCommandFinished函数。与本文所描述的命令有ConnectToHost()、Login()、Get()、Close()和List()。通过currentCommand成员函数可以具体得到当前执行完毕命令的id,因此根据不同命令的id在用户界面上显示不同的命令状态。

void Widget::ftpCommandFinished(int, bool error)
{
    if (ftp->currentCommand() == QFtp::ConnectToHost) {
        if (error)
            ui->label->setText(tr("connecting error %1").arg(ftp->errorString()));
        else
            ui->label->setText(tr("connecting success!"));
    }

    if (ftp->currentCommand() == QFtp::Login) {
        if (error)
            ui->label->setText(tr("login error").arg(ftp->errorString()));
        else {
            ui->label->setText(tr("login success!"));
            ftp->list();
        }
    }
    //部分代码省略
}

on_downloadButton_clicked()的实现

当单击下载按钮后,将进入该函数进行相应文件的下载。首先通过fileList对象中的成员函数得到要下载文件的名称,在打开该文件,最后通过get函数获取数据。

void Widget::on_downloadButton_clicked()
{
    QString fileName = ui->fileList->currentItem()->text(0);
    file = new QFile(fileName);
    if (!file->open(QIODevice::WriteOnly)) {
        delete file;
        return;
    }

    ui->downloadButton->setEnabled(false);
    ftp->get(ui->fileList->currentItem()->text(0), file);
}

addToList()的实现

当对FTP服务器上某个目录执行list()命令时,对于list()找到的每一个文件都会发出listInfo()信号。该信号会使得addToList函数执行,以便在treeWidget上增加一条新的文件信息。

因此,该函数首先根据urlInfo参数建立一条新的文件信息,然后再利用addTopLevelItem成员函数将该条新的文件信息加入到treeWidget最顶端。

void Widget::addToList(const QUrlInfo &urlInfo)
{
    QTreeWidgetItem *item = new QTreeWidgetItem;
    item->setText(0, urlInfo.name());
    item->setText(1, QString::number(urlInfo.size()));
    item->setText(2, urlInfo.owner());
    item->setText(4, urlInfo.lastModified().toString("mmm dd yyyy"));
    QPixmap pixmap(urlInfo.isDir() ? "../dir.png" : "../file.png");
    item->setIcon(0, pixmap);
    isDirectory[urlInfo.name()] = urlInfo.isDir();

    //存储该路径是否为目录的信息;
    ui->fileList->addTopLevelItem(item);
    if (!ui->fileList->currentItem()) {
        ui->fileList->setCurrentItem(ui->fileList->topLevelItem(0));
        ui->fileList->setEnabled(true);
    }
}

processItem()的实现

当双击文件列表中某一项时候,如果该项所显示的文件为目录,则进入该目录中显示其内部的文件列表。上述的内容即为processItem函数所要完成的工作。

void Widget::processItem(QTreeWidgetItem *item, int)
{
    //打开一个目录;
    QString name = item->text(0);
    if (isDirectory.value(name)) {
        ui->fileList->clear();
        isDirectory.clear();
        currentPath += '/';
        currentPath += name;
        ftp->cd(name);
        ftp->list();
        ui->cdToParentButton->setEnabled(true);
    }
}

updateDataTransferProgress()的实现

每当FTP服务器端的数据返回时,就引发该函数的执行。该函数的主要工作即更新进度条的显示。

void Widget::updateDataTransferProgress(qint64 readBytes, qint64 totalBytes)
{
    ui->progressBar->setMaximum(totalBytes);
    ui->progressBar->setValue(readBytes);
}

 

未完待续~
参考:

1. Qt Assistant

2. http://www.yafeilinux.com/?p=757

3. C++ GUI Qt 4编程(第二版); 电子工业出版社;Blanchette,J,Summerfield,M 著;闫锋欣 等译;

基于Qt的Http编程-进阶

2011年2月27日

进阶

上文的Http客户端只能下载指定网址的数据,这样的客户端在交互性和功能性上都很差。本文所描述的程序则在这个基本的客户端上进行改造,实现任意目标地址的数据下载,并且改善了用户界面的。具体UI可参考下图:

对于UI而言,该客户端增加了任意地址的输入框、下载进度条和下载按钮;对于下载的数据而言,该客户端不再局限于下载文本数据。接下来本文将按照程序执行的大致顺序对相关函数进行分析。

构造函数

用户界面的设计可见本文参考1和参考2,这里从构造函数开始。在构造函数中除了对用户界面进行初始化外,还创建了一个QNetworkAccessManager对象,并将进度显示条隐藏了起来。

widget::widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::widget)
{
    ui->setupUi(this);
    manager = new QNetworkAccessManager(this);
    ui->progressBar->hide();
}

on_pushButton_clicked槽函数

该槽函数对应着下载按钮的单击事件。该函数完成的工作有从用户界面获得下载地址;从这个地址中解析出所下载文件的名称;打开(创建)所下载的文件;发送下载请求;更新进度显示条的信息。

从上文中可以看到,在Qt中使用QUrl类来存储url地址,通过该类的成员函数还可以根据具体情况对url地址进行相应的处理。使用QFileInfo类的对象存储不依赖具体系统的文件属性,比如文件的名称,路径,访问权限等;通过该类的成员函数可以方便的获取文件的某些属性,比如通过fileName成员函数就可以从路径名中快速解析出文件名。

得到了文件名就可以创建一个QFile类的对象,通过open成员函数就可以打开这个文件,QIODevice::WriteOnly为打开模式。如果打开错误,则弹出警告提示框,并进行相应的错误处理。

文件打开成功后,紧接着就应该发送下载链接的请求了,这个工作将在startRequest()中完成。最后设置进度更新条的初始值,并将其在界面上显示出来。具体的实现代码可参考下图:

void widget::on_pushButton_clicked()
{
    url.setUrl(ui->lineEdit->text());
    QFileInfo info(url.path());//获得地址;
    QString fileName(info.fileName());//从地址中获得文件名;

    //如果地址类似www.edsionte.com,则文件名为index.html;
    if (fileName.isEmpty())
        fileName = "index.html";

    file = new QFile(fileName);
    if (!file->open(QIODevice::WriteOnly))
    {
        QMessageBox::warning(this, tr("Warning"),
                             tr("file open error"),
                             QMessageBox::Yes);
        qDebug() << "file open error";
        delete file;
        file = 0;
        return;
   }
   startRequest(url);//进行请求;
   ui->progressBar->setValue(0);
   ui->progressBar->show();
}

startRequest()的实现

通过参考下面的示例代码就可以发现,该函数注意进行了两部分的内容:发送下载请求并获得数据回复和几组信号和槽函数之间的连接。

get()函数在上文中已有说明,它将返回一个QNetworkReply类的对象reply。当所有的数据下载完毕后,manager对象将发送finished信号,进而调用httpFinished槽函数。

上文所描述的Http客户端是等待所有的数据都下载到内存再读出并显示,而本文所描述的Http客户端则将请求的数据进行分段下载并保存。因此每当有一部分新数据到达本地,reply就会发送readyRead信号,进而调用httpReadyRead槽函数将这部分新的数据保存到本地。使用这种分段下载并保存数据的方法可以有效的节省内存。而一旦网络请求有数据返回,reply对象就会发送downloadProgress信号,进而引发updataReadProcess槽函数对进度显示条进行更新。

void widget::startRequest(QUrl url)
{
    reply = manager->get(QNetworkRequest(url));

    connect(reply, SIGNAL(finished()),
            this, SLOT(httpFinished()));

    connect(reply, SIGNAL(readyRead()),
            this, SLOT(httpReadyRead()));

    connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
            this, SLOT(updataReadProcess(qint64,qint64)));
}

相关槽函数

当有一部分新数据返回到本地后,就回执行httpReadyRead函数。该函数将这部分新到达内存的数据写入file对象所对应的文件中。

void widget::httpReadyRead()
{
    //如果文件存在,则将数据写入文件;
    if (file)
        file->write(reply->readAll());
}

每当请求的数据有返回时,就执行updataReadProcess函数以便及时更新进度显示条。该函数包含两个参数byteRead和totalBytes。前者表示当前接受到的数据总量,后者表示应该下载的数据总量。显然,随着byteRead的增加,进度显示条也不断更新。当byteRead和totalBytes相等时,表示下载完毕。

void widget::updataReadProcess(qint64 byteRead, qint64 totalBytes)
{
    ui->progressBar->setMaximum(totalBytes);//最大值;
    ui->progressBar->setValue(byteRead);//当前值
}

当所有数据都下载完毕后,就执行httpFinished函数。在该函数中将隐藏进度显示条,并将缓冲区的数据清空。并且关闭文件对象,再释放之前申请的一些数据空间。

void widget::httpFinished()
{
    ui->progressBar->hide();
    file->flush();
    file->close();
    reply->deleteLater();
    reply = 0;
    delete file;
    file = 0;
}

至此,基本上完成了一个Http下载客户端。理论上可以下载任何数据,不过我在测试中发现有些地址不能如期下载(比如QQ的下载链接)。

参考:

1. Qt Assistant

2. http://www.yafeilinux.com/?p=734

3. C++ GUI Qt 4编程(第二版); 电子工业出版社;Blanchette,J,Summerfield,M 著;闫锋欣 等译;

基于Qt4的GUI开发流程

2011年2月19日

最近在学习使用Qt4进行GUI的开发。经过几天的学习,基本上清楚了大致的开发流程。本文以一个简单的文本编辑器为例,说明Qt4的基本开发流程。

之所以选用文本编辑器为例是因为它属于一个标准的主窗口,整个用户界面包含菜单、工具栏、状态栏以及应用程序所需要的对话框。效果图如下:

类似这样的主窗口实现需要三个步骤:用户界面的设计、建立信号与槽之间的连接和自定义槽的实现。Qt4为开发人员提供了两种实现用户界面的方法,一种是通过传统的编码方式实现用户界面;另一种是通过Qt designer(现被集成到Qt Creator中)快速实现。这两种实现方式只是体现在用户界面的设计上,虽然在后续的两个步骤中稍有不同,但本质上是相同的。为了方便说明,本文将这两种设计方式分开描述。

1. 使用编码方式实现

此部分所描述的文本编辑器对应的工程文件为code_myTextEdit,该文本编辑器对应的类为MainWindow,它继承了QMainWindow类。该工程内部所有的文件构成如下图:

1.1 用户界面的实现

如果使用编码方式,则需要在定义MainWindow类的时候声明用户界面所需要的所有部件。比如该文本编辑器的有三个菜单,则需要分别声明三个QMenu类型的变量:menu_File、menu_Edit和menu_Help。此外,还需要声明一些QAction类型的变量,该类型的变量代表鼠标点击菜单或工具栏上相应选项时所产生的动作。比如File菜单中的New选项就对应QAction类型的变量newAct。除上述的成员变量外,在类的定义中还必须声明所需的槽函数。以上这些声明通常被单独放在一个头文件中,即mainwindow.h。

声明完毕后,对用户界面的建立常常会被安排在对应类的构造函数中。其实现通常在.cpp文件中完成,比如mainwindow.cpp。比如在MainWindow类的构造函数中就有createMenus()、createActions()、createToolBars()和createStatusBar()几个函数,他们分别创建主窗口的菜单、菜单选项、工具栏和状态栏。

1.2 建立信号和槽之间的连接

第一部分的代码实现了用户界面,接下来应该建立信号和槽之间的连接。Qt中的信号和槽机制与Java中的事件驱动机制类似,这里的信号相当于用户所引发的事件,而槽本质上就是一个函数,它实现了这个动作所对应的功能。

Qt中使用connect函数来建立信号和槽之间的链接。通常一个信号可以链接多个槽,多个信号也可以链接同一个槽,并且一个信号还可以与另一个信号相连接。连接函数通常放在相关类的构造函数中完成,上述的文本编辑器所对应的MainWindow类的构造函数中,createActions()不仅创建了相应事件的动作对象,而且完成了所有可能引发的事件与相应槽之间的连接。下面是New菜单选项的创建代码:

    newAct = new QAction(tr("&New"), this);
    newAct->setShortcuts(QKeySequence::New);
    newAct->setStatusTip(tr("Create a new file"));
    connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));

connect函数说明当用户对New选项发出“触发”动作时,当前的对象就调用newFile函数进行事件处理。这里的触发即包含鼠标点击又包含快捷键等。

1.3 实现自定义的槽函数

这部分应该是最关键的一步,根据每个事件的具体功能实现相应的槽函数。比如下面的代码就是实现New菜单选项:

void MainWindow::newFile()
{
    if (maybeSave()) {
        textEdit->clear();
        setCurrentFile("");
    }
}

其中maybeSave()用来判断当前已修改的文件是否进行了保存;如果已保存则清空当前的文本编辑器,并设置相应的主窗口的标题名。其中,maybeSave()和setCurrentFile()都需要用户自己实现。

将第一步声明的所有槽函数都实现后,文本编辑器也基本上算开发完成。

2. 使用设计方式实现

2.1 用户界面的实现

相比传统的编码方式,使用Qt designer则可以快速设计出所需要的用户界面。通过从工具栏中拖动相应的部件到主窗口就可以完成用户界面的涉及。关于主窗口的设计可以参考Qt在线手册中的Creating Main Windows in Qt Designer一节,这里不再赘述。此部分所说的文本编辑器对应的工程文件为design_myTextEdit,内部文件如下图:

如果使用这种可视化的设计方式,则会使用.ui文件保存这个用户界面。用户界面编译器(user interface compiler,uic)则会将.ui文件转换成适合C++的.h文件。比如上述的文本编辑器的用户界面存储在mainwindow.ui中,uic会将其转换成ui_mainwindow.h文件。所生成的ui_mainwindow.h文件中包含了类Ui::MainWindow的定义,该类是一个与mainwindow.ui等价的C++文件。这个类中声明了用户界面中的一些部件以及初始化窗体的setupUi()。

具体在使用时,则通常再定义一个新的类MainWindow,让这个类继承QMainWindow和Ui::MainWindow两个类。此时,就不需要MainWindow中声明窗体中的各个部件了,因为这已经在mainwindow.ui文件中完成;同时对窗体的初始化也可以通过调用setupUi(this)自动完成。这一点是与用编码方式实现用户界面所不同的。

2.2 建立信号和槽之间的连接

使用设计方式创建完主窗口后,建立信号和槽之间的连接也十分简单。只需在事件编辑器中选取具体事件和具体的触发动作,并点击右键的go to slot选项即可在mainwindow.h和mainwindow.cpp文件中创建相应槽函数声明。

2.3 实现自定义的槽函数

到此步只需在实现相应的槽函数即可。比如上述save选项对应的槽如下所示,现在只需输入相应的实现代码即可。

void MainWindow::on_action_Save_triggered()
{
    save();
}

因此,从上述内容来看,创建用户界面的这两种方式在本质上是相同的。虽然设计方式简单方便,但是对于初学者最好两种方法都掌握,这样才能理解用户界面的开发方式。

参考:

1. Qt Assistant

2. http://www.yafeilinux.com/?page_id=3

3. C++ GUI Qt 4编程(第二版); 电子工业出版社;Blanchette,J,Summerfield,M 著;闫锋欣 等译;

 

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key