开始使用 Qt 编程Qt Widgets

在本主题中,我们将通过使用 C++ 和 Qt Widgets模块实现一个简单的记事本应用程序,从而传授基本的 Qt 知识。该程序是一个小型文本编辑器,允许您创建文本文件、保存、打印或重新打开并再次编辑。您还可以设置要使用的字体。

记事本应用程序

运行示例

要从 Qt Creator,打开Welcome 模式,然后从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行

创建记事本项目

Qt Creator 中设置新项目时,会有一个向导一步步引导您完成项目创建过程。向导会提示您输入特定类型项目所需的设置,然后为您创建项目。

注意: Qt Creator 中的用户界面文本和生成文件的内容取决于您使用的Qt Creator 版本。

Qt Creator 新项目对话框

要创建记事本项目,请选择File >New Project >Application (Qt) > 。 Qt Widgets Application>Choose ,然后按照向导的指示操作。在Class Information 对话框中,键入Notepad作为类名,并选择QMainWindow 作为基类。

班级信息对话框

向导会创建一个包含 Qt Widgets Application向导创建的项目包含一个主源文件和一组指定用户界面(记事本小部件)的文件:

  • CMakeLists.txt - 项目文件。
  • main.cpp - 应用程序的主源文件。
  • notepad.cpp - 记事本部件的记事本类的源文件。
  • notepad.h - 记事本部件的记事本类的头文件。
  • notepad.ui - 记事本部件的用户界面表单。

这些文件包含必要的模板代码,以便您能够构建和运行项目。我们将在下面的章节中详细介绍文件内容。

了解更多

关于这里
使用Qt CreatorQt Creator
创建其他应用程序Qt CreatorQt Creator 教程

主源文件

向导会在 main.cpp 文件中生成以下代码:

#include "notepad.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Notepad w;
    w.show();
    return a.exec();
}

我们将逐行查看代码。下面几行包括记事本部件和QApplication 的头文件。所有 Qt 类都有一个以其命名的头文件。

#include "notepad.h"

#include <QApplication>

下面一行定义了 main 函数,它是所有基于 C 和 C++ 的应用程序的入口点:

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

下面一行创建了一个QApplication 对象。该对象管理应用程序范围内的资源,是运行任何使用Qt Widgets 的 Qt 程序所必需的。它使用argc 命令行参数构建应用程序对象,并在argv 中运行(对于不使用Qt Widgets 的图形用户界面应用程序,可以使用QGuiApplication 代替)。

    QApplication a(argc, argv);

下面一行创建记事本对象。向导为该对象创建了类和用户界面文件。用户界面包含 Qt XML 中称为widgets 的可视化元素。部件的例子包括文本编辑、滚动条、标签和单选按钮。部件也可以是其他部件的容器,例如对话框或主应用程序窗口。

    Notepad w;

下一行显示的是记事本窗口。Widget 也可以作为容器。QMainWindow 就是一个例子,它通常包含多种类型的部件。默认情况下,窗口小部件是不可见的;函数show() 可使窗口小部件可见。

    w.show();

下面一行使QApplication 进入事件循环。Qt 应用程序运行时,会产生事件并发送给应用程序的 Widget。事件的例子包括鼠标按下和击键。

    return a.exec();

了解更多

关于 Qt这里
窗口和对话框部件窗口和对话框小工具
事件和事件处理事件系统

设计用户界面

向导会生成 XML 格式的用户界面定义:notepad.ui。在Qt Creator 中打开 notepad.ui 文件时,它会自动在集成的Qt Widgets Designer 中打开。

当您构建应用程序时,Qt Creator 会启动 QtXMLUser Interface Compiler (uic),它读取 .ui文件并创建相应的 C++ 头文件 ui_notepad.h。

使用Qt Widgets Designer

向导会创建一个使用QMainWindow 的应用程序。它有自己的布局,可以添加菜单栏、停靠窗口小部件、工具栏和状态栏。中间区域可以放置任何类型的窗口部件。向导会将记事本 widget 放置在这里。

要在Qt Widgets Designer 中添加 Widget,请执行以下操作

  1. Qt Creator Edit 模式下,双击Projects 视图中的 notepad.ui 文件,在集成的Qt Widgets Designer 中启动该文件。
  2. 将 widget 文本编辑器 (QTextEdit) 拖放到表单中。
  3. Ctrl+A(或Cmd+A) 选择部件,然后单击Lay out Vertically (或按Ctrl+L) 应用垂直布局 (QVBoxLayout)。
  4. Ctrl+S(或Cmd+S) 保存更改。

现在用户界面的外观如下Qt Widgets Designer

你可以在代码编辑器中查看生成的 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Notepad</class>
 <widget class="QMainWindow" name="Notepad">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>400</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Notepad</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QTextEdit" name="textEdit"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
    ...

下面一行包含 XML 声明,其中指定了文档中使用的 XML 版本和字符编码:

<?xml version="1.0" encoding="UTF-8"?>

文件的其余部分指定了一个ui 元素,该元素定义了一个记事本小部件:

<ui version="4.0">

UI 文件与记事本类的头文件和源文件一起使用。我们将在后面的章节中介绍用户界面文件的其余部分。

记事本头文件

向导为记事本类生成了一个头文件,其中包含必要的 #includes、一个构造函数、一个析构函数和 UI 对象。该文件如下所示:

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class Notepad;
}
QT_END_NAMESPACE

class Notepad : public QMainWindow
{
    Q_OBJECT

public:
    explicit Notepad(QWidget *parent = nullptr);
    ~Notepad();

private:
    Ui::Notepad *ui;
    QString currentFile;
};

下面一行包含QMainWindow ,它提供了一个主应用程序窗口:

#include <QMainWindow>

下面几行在 Ui 命名空间中声明了记事本类,这是uic 工具从 .ui 文件生成的用户界面类的标准命名空间:

namespace Ui {
class Notepad;
}

类声明包含Q_OBJECT 宏。它必须放在类定义的首位,并将我们的类声明为QObject 。当然,它也必须继承于QObjectQObject 在普通 C++ 类的基础上增加了几种能力。值得注意的是,类名和槽名可以在运行时查询。还可以查询槽的参数类型并调用它。

class Notepad : public QMainWindow
{
    Q_OBJECT

下面几行声明了一个构造函数,它有一个默认参数,名为parent 。值 0 表示该部件没有父部件(它是一个顶级部件)。

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

下面一行声明了一个虚拟析构函数,用于释放对象在生命周期中获取的资源。根据 C++ 的命名约定,析构函数的名称与其相关类的名称相同,并在前缀加一个斜杠 (~)。在QObject 中,析构函数是虚函数,以确保通过指向基类的指针删除对象时,派生类的析构函数能被正确调用。

    ~Notepad();

下面几行声明了一个成员变量,它是指向记事本 UI 类的指针。成员变量与特定类相关联,可访问该类的所有方法。

private:
    Ui::Notepad *ui;
    QString currentFile;
};

记事本源文件

向导为记事本类生成的源文件如下:

#include "notepad.h"
#include "ui_notepad.h"

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

}

以下各行包括向导生成的记事本类头文件和uic 工具生成的用户界面头文件:

#include "notepad.h"
#include "ui_notepad.h"

下面一行定义了Notepad 构造函数:

Notepad::Notepad(QWidget *parent) :

下面一行调用QMainWindow 构造函数,它是记事本类的基类:

    QMainWindow(parent),

下面一行创建了用户界面类实例,并将其赋值给ui 成员:

    ui(new Ui::Notepad)

下面一行设置用户界面:

{
    ui->setupUi(this);

在析构函数中,我们删除了ui

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

项目文件

向导为我们生成了以下项目文件CMakeLists.txt

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

cmake_minimum_required(VERSION 3.16)
project(notepad LANGUAGES CXX)

find_package(Qt6
    REQUIRED COMPONENTS Core Gui Widgets
    OPTIONAL_COMPONENTS PrintSupport
)

qt_standard_project_setup()

qt_add_executable(notepad
    main.cpp
    notepad.cpp notepad.h notepad.ui
)

set_target_properties(notepad PROPERTIES
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)

target_link_libraries(notepad PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Widgets
)

if(TARGET Qt6::PrintSupport)
    target_link_libraries(notepad PRIVATE Qt6::PrintSupport)
endif()

# Resources:
set(notepad_resource_files
    "images/bold.png"
    "images/copy.png"
    "images/create.png"
    "images/cut.png"
    "images/edit_redo.png"
    "images/edit_undo.png"
    "images/exit.png"
    "images/font.png"
    "images/info.png"
    "images/italic.png"
    "images/new.png"
    "images/open.png"
    "images/paste.png"
    "images/pencil.png"
    "images/print.png"
    "images/save.png"
    "images/save_as.png"
    "images/underline.png"
)

qt_add_resources(notepad "notepad"
    PREFIX
        "/"
    FILES
        ${notepad_resource_files}
)

install(TARGETS notepad
    BUNDLE  DESTINATION .
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

qt_generate_deploy_app_script(
    TARGET notepad
    OUTPUT_SCRIPT deploy_script
    NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})

项目文件指定了项目中包含的源文件、头文件和用户界面文件。

了解更多

关于这里
使用Qt Widgets DesignerQt Widgets Designer 手册
布局布局管理,部件和布局,布局示例
Qt 自带的小部件Qt Widget 图库
主窗口和主窗口类应用程序主窗口主窗口示例
QObjects 和 Qt 对象模型(这对理解 Qt 至关重要)对象模型
qmake 和 Qt 构建系统qmake 手册

添加用户交互

要为编辑器添加功能,我们首先要在工具栏上添加菜单项和按钮。

点击 "在此键入",并添加新建、打开、保存、另存为、打印和退出选项。这样就在下面的操作编辑器中创建了 5 行。要将操作连接到插槽,右键单击操作并选择Go to slot >triggered() ,然后完成给定插槽的代码。

如果我们还想将操作添加到工具栏上,可以为每个QAction 指定一个图标,然后将QAction 拖到工具栏上。分配图标的方法是在相关操作的图标属性中输入图标名称。将QAction 拖到工具栏后,单击图标即可启动相关槽。

完成方法newDocument()

void Notepad::newDocument()
{
    currentFile.clear();
    ui->textEdit->setText(QString());
}

currentFile 变量是一个全局变量,包含当前正在编辑的文件,而clear() 则清除文本缓冲区。currentFile 变量在 notepad.h 的私有部分中定义:

private:
    Ui::Notepad *ui;
    QString currentFile;

打开文件

notepad.ui 中,右击actionOpen ,选择Go to Slot

完成方法open()

void Notepad::open()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Open the file");
    if (fileName.isEmpty())
        return;
    QFile file(fileName);
    currentFile = fileName;
    if (!file.open(QIODevice::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, "Warning", "Cannot open file: " + file.errorString());
        return;
    }
    setWindowTitle(fileName);
    QTextStream in(&file);
    QString text = in.readAll();
    ui->textEdit->setText(text);
    file.close();
}

QFileDialog::getOpenFileName QFile 对象 将所选 作为参数。我们还将所选文件存储到全局变量 中,以备后用。我们用 打开文件,将其作为只读文本文件。如果文件无法打开,程序将发出警告并停止运行。myfile file_name currentFile file.open

我们为参数myfile 定义一个QTextStream instream 。文件myfile 的内容被复制到QString text 中。setText(text)text 填入编辑器的缓冲区。

保存文件

我们创建保存文件的方法与打开文件的方法相同,右键单击actionSave ,然后选择Go to Slot

void Notepad::save()
{
    QString fileName;
    // If we don't have a filename from before, get one.
    if (currentFile.isEmpty()) {
        fileName = QFileDialog::getSaveFileName(this, "Save");
        if (fileName.isEmpty())
            return;
        currentFile = fileName;
    } else {
        fileName = currentFile;
    }
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, "Warning", "Cannot save file: " + file.errorString());
        return;
    }
    setWindowTitle(fileName);
    QTextStream out(&file);
    QString text = ui->textEdit->toPlainText();
    out << text;
    file.close();
}

QFile 对象 链接到全局变量 ,该变量包含我们正在处理的文件。如果我们无法打开 ,则会发出一条错误信息并停止该方法。我们创建一个 。编辑器缓冲区的内容会转换为纯文本,然后写入 。myfile current_file myfile QTextStream outstream outstream

以其他名称保存文件

void Notepad::saveAs()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Save as");
    if (fileName.isEmpty())
        return;
    QFile file(fileName);

    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        QMessageBox::warning(this, "Warning", "Cannot save file: " + file.errorString());
        return;
    }
    currentFile = fileName;
    setWindowTitle(fileName);
    QTextStream out(&file);
    QString text = ui->textEdit->toPlainText();
    out << text;
    file.close();
}

该步骤与保存文件的步骤相同,唯一不同的是,这里需要输入一个新的文件名来创建文件。

打印文件

如果要使用打印功能,需要在项目文件中添加PrintSupport

find_package(Qt6
    REQUIRED COMPONENTS Core Gui Widgets
    OPTIONAL_COMPONENTS PrintSupport
)

notepad.cpp 中,我们声明了一个名为printDev 的 QPrinter 对象:

void Notepad::print()
{
#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer)
    QPrinter printDev;
#if QT_CONFIG(printdialog)
    QPrintDialog dialog(&printDev, this);
    if (dialog.exec() == QDialog::Rejected)
        return;
#endif // QT_CONFIG(printdialog)
    ui->textEdit->print(&printDev);
#endif // QT_CONFIG(printer)
}

我们将启动一个打印机对话框,并将所选打印机存储在对象printDev 中。如果我们点击Cancel 但没有选择打印机,则方法返回。实际的打印机命令通过ui->textEdit->print 发送,QPrinter 对象作为参数。

选择字体

void Notepad::selectFont()
{
    bool fontSelected;
    QFont font = QFontDialog::getFont(&fontSelected, this);
    if (fontSelected)
        ui->textEdit->setFont(font);
}

我们用QFontDialog 声明一个布尔值,表明我们是否选择了字体。如果是,我们就用ui->textEdit->setFont(myfont) 设置字体。

复制、剪切、粘贴、撤销和重做

如果你选择了一些文本,并想将其复制到剪贴板,你可以调用ui->textEdit 中的相应方法。同样的方法也适用于剪切、粘贴、撤销和重做。

本表显示了要使用的方法名称。

任务调用的方法
复制ui->textEdit->copy()
剪切ui->textEdit->cut()
粘贴ui->textEdit->paste()
撤销ui->textEdit->undo()
重做ui->textEdit->redo()

了解更多信息

关于这里
文件和 I/O 设备QFile,QIODevice
tr() 和国际化Qt Linguist 手册,为翻译编写源代码,Qt 的国际化

从命令行构建和运行

要从命令行构建示例应用程序,请为其创建一个构建目录。切换到构建目录并运行qt-cmake 来配置项目以便构建。如果项目配置成功,生成的文件将帮助您构建项目。

md <build_directory>
cd <build_directory>
<qt_installation_directory>\bin\qt-cmake -GNinja <source_directory>
<generator>

这些命令会在构建目录中创建一个可执行文件。CMake 工具会读取项目文件,并生成如何构建应用程序的说明。然后,生成器使用这些指令生成可执行的二进制文件。

例如,要在 Windows 上构建记事本示例,使用 Ninja 作为生成器时,请输入以下命令:

md notepad-build
cd notepad-build
C:\Qt\6.9.0\msvc2019_64\bin\qt-cmake -GNinja C:\Examples\notepad
ninja

如果不使用 Ninja 作为生成器,请使用独立于生成器的 CMake 命令来构建应用程序,而不要使用ninja

cmake --build

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.