以下通过编写一个简单的记事本程序,来学习如何使用Qt Widgets。
最终效果
创建项目
在菜单栏中选择 文件 > 新建文件或项目,打开引导窗口 New File or Project
在 New File or Project 窗口中选择 Application > Qt Widgets Application 后,点击 Choose... 按钮进入下一步,填写项目名称和项目路径
点击 下一步 后,选择构建工具,此处我们选择 qmake
点击 下一步 后,生成项目时的类文件信息,此处我们将 Class name 修改为 Notepad,Base class 这一项选择继承哪一个基类,可选择 QMainWindow、QWidget、QDialog。
- QMainWindow - 提供一个有菜单条、锚接窗口(例如工具条)和一个状态条的主应用程序窗口,是最常见的窗口形式,可以作为GUI程序的主窗口。
- QWidget - 是所有用户界面对象的基类。所有窗口或者控件都直接或间接继承自QWidget类。
-
QDialog - 是对话框窗口的基类。主要用于短期任务以及和用户进行简要通讯的顶级窗口。
点击 下一步 后,选择构建套件,在这里我已提前安装了Qt 5.13.1,选中即可
最后 下一步 > 完成,项目便创建完成。
Qt Widgets Application 向导会为我们创建好初始的项目文件:
- notepad.pro - 工程文件,用来告诉
qmake
如何创建Makefile
- main.cpp - 应用程序主源文件,main函数便写在该文件中
- notepad.h - Notepad类的头文件
- notepad.cpp - Notepad类的源文件
- notepad.ui - UI窗体描述文件,该文件是一个xml文件
点击运行按钮,启动一下
设计界面
创建完项目后,Qt Creator 为我们生成了一个notepad.ui
文件,该文件为UI窗体描述文件。
构建应用时,Qt Creator会启动uic
读取ui文件并创建出一个对应的头文件,本项目中的头文件是ui_notepad.h
。
在开始设计界面前,先准备好图片资源。
首先,我们需要添加图片资源,在当前项目目录/data/study/notepad
下创建目录images
并将需要用到的图片放置在其中。然后切换至编辑模式,在项目名上点击右键选择 Add New...
在新建文件对话框中选择 Qt > Qt Resource File 点击 Choose...,进入新建资源文件向导
填写资源文件的名称res
,并指定路径为当前项目路径/data/study/notepad
后,点击下一步 > 完成。
完成资源文件的创建后,在左侧项目浏览器中能看到一个名为res.qrc
的文件,现在我们打开该文件,将图片添加进去
下面,双击 notepad.ui
进入设计模式。
先添加一个文本框,在左侧选择 Input Widgets > Text Edit 后拖拽至主窗体中
选中主窗体,点击工具栏中的 垂直布局 或使用快捷键 Ctrl + L
双击 在这里输入 ,分别输入 &File
和 &Edit
,创建两个菜单 File 和 Edit。& 符号紧跟着的字母为快捷键,故此处 File 菜单快捷是 F,Edit 菜单快捷键是 E。
为菜单 File 添加子菜单 New、Open、Save、SaveAs、Exit。
为菜单 Edit 添加子菜单 Undo、Redo、Font、Copy、Cut、Paste。
观察设计器底部 Action Editor,这些是菜单的动作
双击每个动作,为它们添加图标,由于我们已经将图片资源加载进来了,现在可以在图标一项中使用 选择资源...
接下来,我们要为记事本添加工具栏。
在菜单与文本框之间的空白处右键,并选择 添加工具栏,下图中红色区域便是添加的工具栏
将 Action Editor 中的动作拖拽至工具栏中。
自此,设计部分结束,下面该进行编码。
编写代码
首先,打开 notepad.h
定义记事本的操作
#ifndef NOTEPAD_H
#define NOTEPAD_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class Notepad; }
QT_END_NAMESPACE
class Notepad : public QMainWindow
{
Q_OBJECT // (1)
public:
Notepad(QWidget *parent = nullptr);
~Notepad();
private slots: // (2)
void newFile();
void open();
void save();
void saveAs();
void exit();
void copy();
void cut();
void paste();
void undo();
void redo();
void selectFont();
private:
Ui::Notepad *ui;
QString currentFile;
};
#endif // NOTEPAD_H
(1) Q_OBJECT
是Qt中定义的宏,只有直接或间接继承 QObject
类才可以使用,只有使用了该宏才具有信号槽机制。当前类 Notepad
继承自 QMainWindow
,而 QMainWindow
继承自 QWidget
即间接继承了 QObject
,所有我们才能定义槽函数
(2) 以下从 void newFile()
至 void selectFont()
均为槽函数
打开 notepad.cpp
实现操作代码
#include "notepad.h"
#include "ui_notepad.h"
#include <QFileDialog>
#include <QFontDialog>
#include <QMessageBox>
#include <QTextStream>
Notepad::Notepad(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::Notepad)
{
ui->setupUi(this);
this->setCentralWidget(ui->textEdit);
connect(ui->actionNew, &QAction::triggered, this, &Notepad::newFile); // (1)
connect(ui->actionOpen, &QAction::triggered, this, &Notepad::open);
connect(ui->actionSave, &QAction::triggered, this, &Notepad::save);
connect(ui->actionSaveAs, &QAction::triggered, this, &Notepad::saveAs);
connect(ui->actionExit, &QAction::triggered, this, &Notepad::exit);
connect(ui->actionCopy, &QAction::triggered, this, &Notepad::copy);
connect(ui->actionCut, &QAction::triggered, this, &Notepad::cut);
connect(ui->actionPaste, &QAction::triggered, this, &Notepad::paste);
connect(ui->actionUndo, &QAction::triggered, this, &Notepad::undo);
connect(ui->actionRedo, &QAction::triggered, this, &Notepad::redo);
connect(ui->actionFont, &QAction::triggered, this, &Notepad::selectFont);
}
Notepad::~Notepad()
{
delete ui;
}
void Notepad::newFile()
{
currentFile.clear();
ui->textEdit->setText(QString());
}
void Notepad::open()
{
QString fileName = QFileDialog::getOpenFileName(this, "打开文件");
if (fileName.isEmpty()) {
return;
}
QFile file(fileName);
currentFile = fileName;
if (!file.open(QIODevice::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, "警告", "不能打开: " + file.errorString());
return;
}
setWindowTitle(fileName);
QTextStream in(&file);
QString text = in.readAll();
ui->textEdit->setText(text);
file.close();
}
void Notepad::save()
{
QString fileName;
if (currentFile.isEmpty()) {
fileName = QFileDialog::getSaveFileName(this, "保存");
currentFile = fileName;
} else {
fileName = currentFile;
}
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, "警告", "不能保存: " + file.errorString());
return;
}
setWindowTitle(fileName);
QTextStream out(&file);
QString text = ui->textEdit->toPlainText();
out << text;
file.close();
}
void Notepad::saveAs()
{
QString fileName = QFileDialog::getSaveFileName(this, "另存为...");
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, "警告", "不能保存: " + file.errorString());
return;
}
currentFile = fileName;
setWindowTitle(fileName);
QTextStream out(&file);
QString text = ui->textEdit->toPlainText();
out << text;
file.close();
}
void Notepad::exit()
{
QCoreApplication::quit();
}
void Notepad::copy()
{
ui->textEdit->copy();
}
void Notepad::cut()
{
ui->textEdit->cut();
}
void Notepad::paste()
{
ui->textEdit->paste();
}
void Notepad::undo()
{
ui->textEdit->undo();
}
void Notepad::redo()
{
ui->textEdit->redo();
}
void Notepad::selectFont()
{
bool fontSelected;
QFont font = QFontDialog::getFont(&fontSelected, this);
if (fontSelected)
ui->textEdit->setFont(font);
}
(1) 此处是绑定信号,将 New 菜单的 triggered 信号绑定至当前类中的 newFile 槽函数,当点击了 New 菜单后,newFile 函数便会执行。
之后,就可以运行了,当然,代码并不健壮,有兴趣的可以自己完善一下。