开始第一个Qt Widgets

以下通过编写一个简单的记事本程序,来学习如何使用Qt Widgets。
最终效果


最终效果

创建项目

在菜单栏中选择 文件 > 新建文件或项目,打开引导窗口 New File or Project

创建项目

New File or Project 窗口中选择 Application > Qt Widgets Application 后,点击 Choose... 按钮进入下一步,填写项目名称和项目路径

项目名称和路径

点击 下一步 后,选择构建工具,此处我们选择 qmake

构建工具

点击 下一步 后,生成项目时的类文件信息,此处我们将 Class name 修改为 Notepad,Base class 这一项选择继承哪一个基类,可选择 QMainWindowQWidgetQDialog

  • QMainWindow - 提供一个有菜单条、锚接窗口(例如工具条)和一个状态条的主应用程序窗口,是最常见的窗口形式,可以作为GUI程序的主窗口。
  • QWidget - 是所有用户界面对象的基类。所有窗口或者控件都直接或间接继承自QWidget类。
  • QDialog - 是对话框窗口的基类。主要用于短期任务以及和用户进行简要通讯的顶级窗口。


    类信息

点击 下一步 后,选择构建套件,在这里我已提前安装了Qt 5.13.1,选中即可

kits

最后 下一步 > 完成,项目便创建完成。

项目结构

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

双击每个动作,为它们添加图标,由于我们已经将图片资源加载进来了,现在可以在图标一项中使用 选择资源...

编辑动作

选择资源

接下来,我们要为记事本添加工具栏。
在菜单与文本框之间的空白处右键,并选择 添加工具栏,下图中红色区域便是添加的工具栏

添加工具栏

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 函数便会执行。

之后,就可以运行了,当然,代码并不健壮,有兴趣的可以自己完善一下。

代码已上传至 https://github.com/abeir/qt-study

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341