Qt:在C++应用程序中使用Qt Designer UI文件

1059 篇文章 285 订阅

Qt设计器UI文件以xml格式表示表单的小部件树。可以处理窗体:

  • 在编译时间,这意味着窗体转换为可编译的C++代码
  • 在运行时间,这意味着窗体由QUiLoader类处理,该类在解析XML文件时动态构建小部件树

编译时间窗体处理

使用Qt Designer创建用户界面组件,并使用Qt的集成构建工具qmakeuic在应用程序构建时为它们生成代码。生成的代码包含窗体的用户界面对象。它是一个C++结构,包含:

  • 指向窗体小部件、布局、布局项、按钮组和操作的指针。
  • 一个名为setupUi()的成员函数,用于在父小部件上构建小部件树。
  • 一个名为retranslateUi()的成员函数,用于处理窗体的字符串属性的转换。

生成的代码可以包含在您的应用程序中并直接使用。或者,您可以使用它来扩展标准小部件的子类。

编译时间处理窗体可以在您的应用程序中使用,方法如下:

  • 直接方法:构建一个小部件,用作组件的占位符,并在其内部设置用户界面
  • 单一继承方法:将窗体的基类(例如QWidget或QDialog)子类划分为子类,并包含表单用户界面对象的私有实例。
  • 多重继承方法:您将窗体的基类和表单的用户界面对象分为子类。这允许窗体中定义的小部件直接从子类的范围内使用。

为了演示,我们创建了一个简单的计算器表单应用程序。它基于原始计算器表单示例。

该应用程序由一个源文件main.cpp和一个 UI 文件组成

使用Qt 设计器设计的文件calculatorform.ui如下所示:
在这里插入图片描述
我们将用于构建可执行文件,因此我们需要编写文件:qmake.pro

HEADERS     = calculatorform.h

此文件的特殊功能是FORMS声明,它告诉qmake使用uic处理哪些文件。在这种情况下calculatorform.ui文件用于创建ui_calculatorform.h文件,该文件可由SOURCES声明中列出的任何文件使用。

注意:您可以使用Qt Creator创建计算器窗体项目。它会自动生成main.cpp,UI和.pro文件,然后可以修改这些文件。

直接方法

  • ui_calculatorform.h直接引入main.cpp
#include "ui_calculatorform.h"

main函数通过构造一个标准的QWidget来创建计算器小部件,我们用它来承载calculatorform.ui文件。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QWidget widget;
    Ui::CalculatorForm ui;
    ui.setupUi(&widget);

    widget.show();
    return app.exec();
}

此时,Ui::CalculatorFormui_calculatorform.h文件中的接口描述对象,它用于设置对话框的所有小部件以及其信号和槽之间的连接

直接方法提供了在应用程序中使用简单、自成一体的组件的快速简便方法。但是,使用Qt Designer创建的组件通常需要与应用程序代码的其余部分紧密集成。例如,上面提供的代码将进行编译和运行,但QSpinBox对象不会与QLabel交互,因为我们需要一个自定义插槽来执行添加操作并在QLabel中显示结果。为了实现这一目标,我们需要采用单一的继承方法。

单一继承方法

为了使用单一继承方法,我们对标准Qt小部件进行子类化,并包含表单用户界面对象的私有实例。这可以采取以下形式:

  • 成员变量
  • 指针成员变量

使用成员变量

在这种方法中,我们将Qt小部件实例化,并从构造函数中设置用户界面。以这种方法使用的组件将窗体使用的小部件和布局公开给Qt小部件子类,并提供一个标准系统,用于在用户界面和应用程序中的其他对象之间建立信号和槽连接,生成的Ui::CalculatorForm 是类的成员

此方法用于计算器表单示例。

为了确保可以使用用户界面,我们需要在引用Ui::CalculatorForm之前包含uic生成的头文件:

#include "ui_calculatorform.h"

这意味着.pro文件必须更新以包含calculatorform.h:

HEADERS     = calculatorform.h

子类的定义方式如下:

class CalculatorForm : public QWidget
{
    Q_OBJECT

public:
    explicit CalculatorForm(QWidget *parent = nullptr);

private slots:
    void on_inputSpinBox1_valueChanged(int value);
    void on_inputSpinBox2_valueChanged(int value);

private:
    Ui::CalculatorForm ui;
};

类的重要特征是私有ui对象,它提供用于设置和管理用户界面的代码

子类的构建函数通过调用ui对象setupUi函数来构造和配置对话框的所有小部件和布局。完成后,可以根据需要修改用户界面

CalculatorForm::CalculatorForm(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
}

我们可以通过添加on_<object name>前缀,以通常的方式连接用户界面小部件中的信号和槽。更多信息,请参考这里

这种方法的优点是简单使用继承来提供基于QWidget的界面,以及它对数据成员内用户界面小部件变量的封装。我们可以使用此方法定义同一小部件中的多个用户界面,每个界面都包含在自己的命名空间中,并叠加(或撰写)它们。

使用指针成员变量

Ui::CalculatorForm结构可以作为类的指针成员。头文件如下所示:

namespace Ui {
    class CalculatorForm;
}

class CalculatorForm : public QWidget
...
virtual ~CalculatorForm();
...
private:
    Ui::CalculatorForm *ui;
...

源文件如下:

#include "ui_calculatorform.h"

CalculatorForm::CalculatorForm(QWidget *parent) :
    QWidget(parent), ui(new Ui::CalculatorForm)
{
    ui->setupUi(this);
}

CalculatorForm::~CalculatorForm()
{
    delete ui;
}

这种方法的优点是可以向前声明用户界面对象,这意味着我们不必在头文件中包含生成的ui_calculatorform.h文件。然后可以更改表单,而无需重新编译依赖源文件。如果类受到二进制兼容性限制,这一点尤为重要。

对于库和大型应用程序,我们通常推荐这种方法。有关详细信息,请参见创建共享库

多重继承方法

使用Qt designer创建的窗体可以与基于QWidget的标准类一起子类化。这种方法使表单中定义的所有用户界面组件都可以在子类的范围内直接访问,并使用connect()函数以通常的方式进行信号和插槽连接。

此方法用于多重继承示例。

我们需要包含uic根据calculatorform.ui生成的头文件

#include "ui_calculatorform.h"

该类的定义方式与单继承方法中使用的类似,只是这次我们继承了QWidget和Ui::CalculatorForm,如下所示:

class CalculatorForm : public QWidget, private Ui::CalculatorForm
{
    Q_OBJECT

public:
    explicit CalculatorForm(QWidget *parent = nullptr);

private slots:
    void on_inputSpinBox1_valueChanged(int value);
    void on_inputSpinBox2_valueChanged(int value);
};

我们私有地继承Ui::CalculatorForm,以确保用户界面对象在我们的子类中是私有的。我们还可以用public或protected关键字来继承它,就像我们在前面的例子中可以将ui设置为public或protected一样。

子类的构造函数执行许多与单个继承示例中使用的构造函数相同的任务:

CalculatorForm::CalculatorForm(QWidget *parent)
    : QWidget(parent)
{
    setupUi(this);
}

在这种情况下,用户界面中使用的小部件可以像手工在代码中创建的小部件一样进行访问。我们不再需要ui前缀来访问它们。

对语言变化的反应

Qt通过发送QEvent::LanguageChange类型的事件来通知应用程序用户界面语言是否更改。为了调用用户界面对象的成员函数retranslateUi(),请在窗体类中重新实现QWidget::changeEvent()

void CalculatorForm::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
   }
}

运行时间窗体处理

窗体可以在允许是处理:生成动态生成的用户界面。这可以通过QtUiTools模块来完成,QtUiTools模块提供QUiLoader类来处理使用Qt Designer创建的表单

UiTool方法

在运行时处理表单需要包含UI文件的资源文件,此外,应用程序还需要配置为使用QtUiTools模块,这是通过在qmake项目文件中包含如下声明来实现的,以确保应用程序被正确编译和链接

QT += uitools

QUiLoader类提供了构建用户界面的表单加载器对象。此用户界面可以从任何QIODevice(例如QFile对象)中检索,以获取存储在项目资源文件中的表单。 QUiLoader::load() 使用文件中包含的用户界面描述根据窗体小部件

QtUiTools模块类可以使用以下指令包括在内:

#include <QtUiTools>

调用QUiLoader::load()函数,如文本查找器示例中的代码所示:

static QWidget *loadUiFile(QWidget *parent)
{
    QFile file(":/forms/textfinder.ui");
    file.open(QIODevice::ReadOnly);

    QUiLoader loader;
    return loader.load(&file, parent);
}

在运行时使用QtUiTools构建用户界面的类中,我们可以使用QObject::findChild()在表单中定位对象。例如,在下面的代码中,我们根据一些组件的对象名和小部件类型来定位它们:

   ui_findButton = findChild<QPushButton*>("findButton");
    ui_textEdit = findChild<QTextEdit*>("textEdit");
    ui_lineEdit = findChild<QLineEdit*>("lineEdit");

运行时处理表单使开发人员可以自由地更改程序的用户界面,只需更改UI文件即可。这在定制程序以满足各种用户需求时非常有用,例如超大图标或支持辅助功能的不同颜色方案。

自动连接

为编译时间或者运行时间窗体定义的信号和槽可以手动或者自动设置,使用QMetaObject在信号和适当命名的槽之间进行连接的能力

通常,在QDialog中,如果我们想在接受用户输入的信息之前对其进行处理,我们需要将OK按钮的clicked()信号连接到对话框中的一个自定义槽中。我们将首先展示一个对话框的示例,其中插槽是手动连接的,然后将其与使用自动连接的对话框进行比较。

没有自动连接的对话

我们以与以前相同的方式定义对话框,但现在除了构造函数之外,还包括一个插槽:

class ImageDialog : public QDialog, private Ui::ImageDialog
{
    Q_OBJECT

public:
    ImageDialog(QWidget *parent = 0);

private slots:
    void checkValues();
};

checkValues()槽将用于验证用户提供的值。

在对话框的构造函数中,我们像以前一样设置小部件,并将Cancel按钮的clicked()信号连接到对话框的reject()槽。我们还禁用了两个按钮中的autoDefault属性,以确保对话框不会干扰行编辑处理返回键事件的方式:

ImageDialog::ImageDialog(QWidget *parent)
    : QDialog(parent)
{
    setupUi(this);
    okButton->setAutoDefault(false);
    cancelButton->setAutoDefault(false);
    ...
    connect(okButton, SIGNAL(clicked()), this, SLOT(checkValues()));
}

我们将OK按钮的clicked()信号连接到对话框的checkValues()槽,我们实现如下:

void ImageDialog::checkValues()
{
    if (nameLineEdit->text().isEmpty())
        (void) QMessageBox::information(this, tr("No Image Name"),
            tr("Please supply a name for the image."), QMessageBox::Cancel);
    else
        accept();
}

这个自定义槽只做了确保用户输入的数据有效所需的最低限度的工作—它只接受为图像指定名称时的输入。

带自用连接的小部件和对话

尽管在对话框中实现一个自定义槽并在构造函数中连接它很容易,但是我们可以使用QMetaObject的自动连接工具将OK按钮的clicked()信号连接到子类中的槽。uic会自动在对话框的setupUi()函数中生成代码来执行此操作,因此我们只需要声明并实现一个名称符合标准约定的插槽:

void on_<object name>_<signal name>(<signal parameters>);

使用此约定,我们可以定义并实现一个响应鼠标单击OK按钮的插槽:

class ImageDialog : public QDialog, private Ui::ImageDialog
{
    Q_OBJECT

public:
    ImageDialog(QWidget *parent = 0);

private slots:
    void on_okButton_clicked();
};

自动信号和插槽连接的另一个例子是文本查找器,它的on_findButton_clicked()插槽。

我们使用QMetaObject的系统来启用信号和插槽连接:

QMetaObject::connectSlotsByName(this);

这使我们能够实现插槽,如下所示:

void TextFinder::on_findButton_clicked()
{
    QString searchString = ui_lineEdit->text();
    QTextDocument *document = ui_textEdit->document();

    bool found = false;

    // undo previous change (if any)
    document->undo();

    if (searchString.isEmpty()) {
        QMessageBox::information(this, tr("Empty Search Field"),
                                 tr("The search field is empty. "
                                    "Please enter a word and click Find."));
    } else {
        QTextCursor highlightCursor(document);
        QTextCursor cursor(document);

        cursor.beginEditBlock();
    ...
        cursor.endEditBlock();

        if (found == false) {
            QMessageBox::information(this, tr("Word Not Found"),
                                     tr("Sorry, the word cannot be found."));
        }
    }
}

信号和插槽的自动连接既提供了标准命名约定,也提供了一个明确的界面,供小部件设计人员使用。通过提供实现给定界面的源代码,用户界面设计人员可以检查其设计是否实际工作,而无需自己编写代码。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt Designer使用C++实现界面切换可以通过以下步骤实现: 1. 在Qt Designer创建两个QWidget,分别表示两个界面。 2. 使用QStackedWidget将两个QWidget放在一起,以实现在两个界面之间进行切换。 3. 在QWidget上添加QPushButton,点击该按钮时可以实现界面切换。 4. 在C++代码实现按钮的点击事件,通过QStackedWidget的setCurrentIndex()函数来实现界面切换。 下面是一个简单的例子: 1. 在Qt Designer创建两个QWidget,并使用QStackedWidget将它们放在一起。 2. 在第一个QWidget添加一个QPushButton,用于切换到第二个QWidget。 3. 在第二个QWidget添加一个QPushButton,用于切换到第一个QWidget。 4. 在C++代码实现按钮的点击事件,通过QStackedWidget的setCurrentIndex()函数来实现界面切换。 示例代码: ```cpp // main.cpp #include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } ``` ```cpp // mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QStackedWidget> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: QWidget *page1; QWidget *page2; QStackedWidget *stackedWidget; private slots: void on_pushButton1_clicked(); void on_pushButton2_clicked(); }; #endif // MAINWINDOW_H ``` ```cpp // mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // 创建两个界面 page1 = new QWidget(this); page2 = new QWidget(this); // 在第一个界面添加一个按钮 QPushButton *pushButton1 = new QPushButton("切换到第二个界面", page1); pushButton1->setGeometry(100, 100, 200, 50); // 在第二个界面添加一个按钮 QPushButton *pushButton2 = new QPushButton("切换到第一个界面", page2); pushButton2->setGeometry(100, 100, 200, 50); // 创建一个QStackedWidget,并将两个界面添加进去 stackedWidget = new QStackedWidget(this); stackedWidget->addWidget(page1); stackedWidget->addWidget(page2); // 设置第一个界面为默认界面 stackedWidget->setCurrentIndex(0); // 将QStackedWidget设置为主窗口的心窗口 setCentralWidget(stackedWidget); // 连接按钮的点击事件 connect(pushButton1, SIGNAL(clicked()), this, SLOT(on_pushButton1_clicked())); connect(pushButton2, SIGNAL(clicked()), this, SLOT(on_pushButton2_clicked())); } MainWindow::~MainWindow() { } void MainWindow::on_pushButton1_clicked() { // 切换到第二个界面 stackedWidget->setCurrentIndex(1); } void MainWindow::on_pushButton2_clicked() { // 切换到第一个界面 stackedWidget->setCurrentIndex(0); } ``` 在这个例子,我们创建了两个QWidget,分别表示两个界面。我们使用QStackedWidget将它们放在一起,并在每个QWidget添加了一个QPushButton,用于切换到另一个界面。在C++代码,我们实现了按钮的点击事件,并通过QStackedWidget的setCurrentIndex()函数来实现界面切换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值