QT编程

下载QT

http://download.qt.io/official_releases/qt/5.12/5.12.0/

image-20200913175437819

下载那个exe,然后安装好.

image-20200913180336560
image-20200913180400705

新建项目

左上角文件-> 新建项目

image-20200913181029681

然后这里用Dialog

image-20200913181212608

配置链接

点那个配置链接的图标,然后从slidebar 拖拽到dial组件,就可以生成一个信号

image-20200913181812844
文档

在开始搜索assist,打开assistant MinGW 就可以打开Qt的文档

image-20200913183304138

程序发布

首先设置成release版本

image-20200913183934227

然后构建,打开构建的目录

image-20200913184027503

然后再release文件夹下面可以找到 .exe 文件

进入Qt控制台

输入 windeployqt + ".exe文件"

image-20200913184207549

然后就可以直接跑了

用qmake编译程序

快速生成makefile

1
qmake -makefile

生成project .pro 文件

1
qmake -project

编译

1
make -j4

UI设计基础

窗口的种类

MainWindow

有菜单栏, 有QWidget

它包含 DockWidget: 可以拖拽,附着的窗口

Statusbar: 状态栏

Toolbar:工具栏

image-20200913192551767
QWidget

只有一个QWidget

可以放大放小

Dialog

不能放大放小

模态与非模态窗口

模态窗口

只能操作当前窗口

非模态窗口

可以切换到其他窗口

半模态窗口

堵塞部分功能

信号与槽

发送某个按键的信号,响应某个槽函数

创建一个槽

选择一个组件,右键,转为槽

image-20200913193643607
1
2
3
4
void Dialog::on_pushButton_clicked()
{
ui->label->setText("New Text changed");
}

然后就可以在自动创建的这个方法里面修改别的属性

.h 文件中,我们可以发现,定义了一个 Q_QBJECT 的宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Dialog : public QDialog
{
Q_OBJECT // 若不定义这个就响应不了信号与槽

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

private slots:
void on_pushButton_clicked();

private:
Ui::Dialog *ui;
};

#endif // DIALOG_H

手动创建信号和槽

.h 文件里定义自己的signal 和 slot

1
2
3
4
5
signals:
void mypushButtonSignal();
private slots:
void on_pushButton_clicked();
void mypushButtonSlot();

然后可以右键直接添加定义

image-20200913194654194

emit 可以发送自定义信号

1
2
3
4
5
void Dialog::on_pushButton_clicked()
{
//ui->label->setText("New Text changed");
emit mypushButtonSignal();
}

然后用connect绑定一下

1
2
connect(this, SIGNAL(mypushButtonSignal()),
this, SLOT(mypushButtonSlot()));

另一种connect,关闭窗口

1
connect(ui->pushButton_2, &QPushButton::clicked, this, &QMainWindow::close);

控件

Button

pushButton
  • setText 改变文字
  • setMenu 设置菜单
  • clicked 信号

练习1 单击按钮改变文字

.h 文件里面定义一个变量, 并初始化

1
2
3
4
5
6
7
8
private:
Ui::MainWindow *ui;
bool deviceStatus;

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
deviceStatus(false),
ui(new Ui::MainWindow)

然后加一个槽

1
2
3
4
5
6
7
8
9
10
void MainWindow::on_pushButton_clicked()
{
if (deviceStatus == false) {
ui->pushButton->setText("断开");
this->deviceStatus = true;
} else {
ui->pushButton->setText("连接");
this->deviceStatus = false;
}
}
改变背景图片

右键pushButton 改变样式表

添加borderimage

image-20200913225226327

然后选择自己要的图片,确定即可。

改变图标

在源代码里改:在构造函数里写这个即可

1
ui->pushButton_2->setIcon(QIcon(":/image/TypeII.png"));
加入资源

右键项目名,Add New.. 然后选择 Qt Resource File

image-20200913220344392
ToolButton

ToolButton 一般放在Toolbar里面

现在菜单栏里面写几个目录,然后

在这里可以改变图标

image-20200913223847986

然后可以把这些action拖拽到工具栏上面

image-20200913223951167

这些action也可以添加槽,一般添加 triggered 槽

在 .h 文件里面添加头文件

1
#include <QDebug>

然后就可以在控制台输出

1
2
3
4
void MainWindow::on_actionPaste_triggered()
{
qDebug() << "paste";
}
改变窗口的图标

在 .pro 文件末尾加入图标路径

1
RC_ICONS += ./image/favicon.ico

就可以改变程序的图标了.

Label

调用图片
1
2
3
QPixmap mImage = QPixmap(":/image/TypeI.png");
ui->label->setPixmap(mImage);
ui->label->setScaledContents(true); // 自动缩放大小

也可以直接在界面上面选择

调用GIF

添加头文件 QMovie, 也可以右键Refactor添加

1
2
3
4
QMovie *pMovie = new QMovie(":/image/tenor.gif");
ui->label_3->setMovie(pMovie);
ui->label_3->setFixedSize(640, 480);
pMovie->start();

TextEdit

富文本

image-20200914122934063

PlainTextEdit

剪贴动作

1
connect(cutAct, &QAction::triggered, textEdit, &QPlainTextEdit::cut);

纯文本

image-20200914122945591

有没有改变内容

1
textEdit->document()->isModified()

SpinBox

spinBox 和 double SpinBox

image-20200914123123871
  • accelerate 属性

    可以设置加速效果

  • maximum, minimum 可以设置范围

  • singleStep 设置步长

  • suffix和prefix可以设置后缀和前缀

  • decimals设置小数点位数

DateTime

设置当前时间

1
setDateTime(QDateTime::currentDateTime());
  • dateTimeChanged() 时间日期改变

Scrollbar

Dial
  • notchedVisible 显示精度
  • pageStep 是按pageUp 和PageDown的步数

LCD Number

数字时钟
  • DigitCount显示数字个数

  • 显示某个数字

1
ui->lcdNumber->display(QString("333"));

如果要用时间, 要加入这2个头文件

1
2
3
4
5
6
7
#include <QTime>
#include <QDateTime>
#include <QTimer> // 计时器

QTime time = QTime::currentTime(); // QTime("12:55:34.890")
QDateTime datetime = QDateTime::currentDateTime(); // QDateTime(2020-09-14 12:56:06.153 西欧夏令时 Qt::LocalTime)
QDateTime datetimeutc = QDateTime::currentDateTimeUtc(); // QDateTime(2020-09-14 10:57:19.997 UTC Qt::UTC)

输出时间的格式

1
qDebug() << time.toString("hh:mm:ss");

在 .h 里面放一个 private属性

1
QTimer timer;

然后再构造器里面设置时间

1
2
3
4
5
6
7
timer.start(1000);
connect(&timer, &QTimer::timeout, this, &MainWindow::timeoutSlot);

void MainWindow::timeoutSlot()
{
ui->lcdNumber->display(QTime::currentTime().toString("hh:mm:ss"));
}

然后就可以完成一个时钟效果

Progressbar

进度条设置进度

1
2
3
4
5
6
ui->progressBar->setValue(88);

int total = 1666;
int current = 666;
ui->progressBar->setMaximum(total);
ui->progressBar->setValue(current);
底部状态栏

只有mainwindow 才有状态栏

1
2
ui->statusBar->showMessage("my message"); // 显示信息
ui->statusBar->showMessage("my message", 1000); // 显示1s

把进度条加入状态栏

1
2
3
4
QProgressBar *pProgressbar = new QProgressBar(this);
ui->statusBar->addPermanentWidget(pProgressbar);
pProgressbar->setValue(55);
pProgressbar->setAlignment(Qt::AlignCenter);

ToolBox

里面可以添加标签页,标签页里面也可以放其他东西进去

创建menu

1
QMenu *fileMenu = menuBar() -> addMenu(tr("&File")); // 创建菜单并添加一项

menu加入动作

1
2
3
4
5
fileMenu->addAction(saveAsAct);
// 之间添加动作,并返回那个动作
QAction *exitAct = fileMenu->addAction(exitIcon, tr("&Exit"), this, &QWidget::close);
// 无icon 加入动作
QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about);

添加分割符

1
fileMenu->addSeparator();

TooBar

1
QToolBar *fileToolBar = addToolBar(tr("File")); // 添加一个 FIle 到工具栏

Icon

第一个参数是名字,第二个参数是文件

1
const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png")); // 定义图标 图标名字,文件

不定义图标

1
const QIcon saveAsIcon = QIcon::fromTheme("document-save-as");

Action

1
QAction *newAct = new QAction(newIcon, tr("&New"), this); // 定义动作

其他操作

1
2
3
4
5
newAct->setShortcuts(QKeySequence::New); // 定义标准快捷键 会加入 Ctrl+N 提示
newAct->setStatusTip(tr("Create a new file")); // 定义动作的提示
connect(newAct, &QAction::triggered, this, &MainWindow::newFile); // 绑定处理槽函数
fileMenu->addAction(newAct); // 把Action 加入File菜单
fileToolBar->addAction(newAct); // 把Action 加入工具栏

文件管理

1
2
#include <QDir>
#include <QFileInfo>

判断文件是否存在

1
2
QDir mDir("D:/Fyind");
qDebug() << mDir.exists();

创建文件夹

1
2
3
4
5
6
7
8
QString url = QString("D:/Fyind/testDir");
QDir mDir(url);
if (!mDir.exists()) {
mDir.mkdir(url);
qDebug() << "make a new Directory";
} else {
qDebug() << "allready exists";
}

当前运行目录

1
2
qDebug() << mDir.current();
qDebug() << mDir.currentPath();

家目录和根目录

1
2
qDebug() << mDir.homePath();
qDebug() << mDir.rootPath();

文件夹名字和绝对路径

1
2
qDebug() << mDir.dirName();
qDebug() << mDir.absolutePath();

遍历文件夹

1
2
3
4
5
6
foreach (QFileInfo mItem, mDir.entryInfoList()) {
if (mItem.isDir())
qDebug() << "Dir " << mItem.filePath();
else if (mItem.isFile())
qDebug() << "File " << mItem.filePath();
}

使用过滤器

1
2
3
4
QStringList filters;
filters << "*.java";
mDir.setNameFilters(filters);
qDebug() << mDir.entryList(filters, QDir::Files);

文件读写

1
#include <QFile>

打开并写入文件

1
2
3
4
5
6
7
8
9
QFile file("afile");

if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << file.errorString();
} else {
qDebug() << "open success";
}
file.write("hello \n This is a greeting message");
file.close();

读取文件内容

1
2
3
4
5
6
7
8
9
10
11
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << file.errorString();
} else {
qDebug() << "open success2";
}

while (!file.atEnd()) {
QByteArray line = file.readLine();
qDebug() << "read some data: " << line;
}
file.close();

文件指针

1
2
3
4
5
6
7
8
9
10
11
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
qDebug() << file.errorString();
} else {
qDebug() << "open success2";
}

qDebug() << file.pos();
file.seek(9);
file.write("i am here");
qDebug() << file.pos();
file.close();

读取文件信息

1
#include <QFileInfo>

获得大小

1
2
QFileInfo info("afile");
qDebug() << "size: " << info.size();

标准对话框

MessageBox

1
2
3
4
QMessageBox::warning(this, tr("Application"),
tr("can write file %1:\n%2.") // 格式化数据
.arg(QDir::toNativeSeparators(filename), file.errorString()));

询问文件是否保存

1
2
3
4
5
6
const QMessageBox::StandardButton ret =
QMessageBox::warning(this, tr("Application"),
tr("The document has been modified.\n"
"Do you want to save your changes?"),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);

Qt数据结构

QList

Qt中的列表

1
2
#include <QAbstractTableModel>
#include <QList>

深入QT

从空项目构建

新建的时候选择,其他项目,空的QT项目

image-20200923094637770

新建完之后,连pro文件都是空的,需要自己编写.

添加QT依赖

1
2
QT += widgets
requires(qtConfig(filedialog))

添加源文件main.cpp

image-20200923095245507

然后添加

1
2
3
4
5
6
7
#include <QApplication>

int main(int argc, char* argv[]) {
QApplication app(argc, argv);

return app.exec();
}

然后添加一个 MainWindow 的class

image-20200923095623606

然后main.cpp里再加入

1
2
MainWindow mainWin;
mainWin.show();

就可以运行生成一个最简单的app了

在 mainwindow.h里面加入

1
2
3
4
5
6
#include <QMainWindow>

QT_BEGIN_NAMESPACE
class QPlainTextEdit;
QT_END_NAMESPACE

就可以在下面定义QT的东西了

1
2
3
private:
QPlainTextEdit *TextEdit;
QString curFile;

构建GUI

TextEdit

在mainwindow.cpp里面添加

1
2
3
4
5
6
7
8
9
10
11

#include <QtWidgets>

#include "mainwindow.h"

MainWindow::MainWindow()
: textEdit(new QPlainTextEdit) // 初始化textEdit
{
setCentralWidget(textEdit);
}

国际化字符串

QObject::tr()

使用 tr("ddd") 构建的字符串具有国际化翻译功能, 在字符串中加 & 可以定义后面的一个字为快捷键字符 Alt + 那个字符

添加菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MainWindow::MainWindow()
: textEdit(new QPlainTextEdit)
{
setCentralWidget(textEdit);

createActions(); // 创建菜单,状态
}


void MainWindow::createActions() {
menuBar()->addMenu(tr("&Open")); // 添加一项,不返回指针
QMenu *filemenu = menuBar() -> addMenu(tr("&File")); // 在菜单中添加一项,返回指针

}

添加一个新建文件

1
2
3
4
5
6
7
8
9
QMenu *fileMenu = menuBar() -> addMenu(tr("&File")); // 创建菜单并添加一项
QToolBar *fileToolBar = addToolBar(tr("File")); // 创建工具栏
const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png")); // 定义图标
QAction *newAct = new QAction(newIcon, tr("&New"), this); // 定义动作
newAct->setShortcuts(QKeySequence::New); // 定义标准快捷键 会加入 Ctrl+N 提示
newAct->setStatusTip(tr("Create a new file")); // 在状态栏里面显示信息
connect(newAct, &QAction::triggered, this, &MainWindow::newFile); // 绑定处理槽函数
fileMenu->addAction(newAct); // 把Action 加入File菜单
fileToolBar->addAction(newAct); // 把Action 加入工具栏
image-20200923105810030

创建状态栏

1
2
3
void MainWindow::createStatusBar() {
statusBar()->showMessage(tr("ready"));
}

复制粘贴

控制变灰和变彩色

1
2
3
4
5
cutAct->setEnabled(false);
copyAct->setEnabled(false); // 图标变灰
// 如果可以复制粘贴就让它变成彩色
connect(textEdit, &QPlainTextEdit::copyAvailable, cutAct, &QAction::setEnabled);
connect(textEdit, &QPlainTextEdit::copyAvailable, copyAct, &QAction::setEnabled);

另存为文件

1
2
3
4
5
6
7
8
9
bool MainWindow::saveAs()
{
QFileDialog dialog(this); // 文件对话框
dialog.setWindowModality(Qt::WindowModal); // 可改变窗口
dialog.setAcceptMode(QFileDialog::AcceptSave); // 设置接受类型
if (dialog.exec() != QDialog::Accepted) // 没有点接受
return false;
return saveFile(dialog.selectedFiles().first()); // 拿到名字
}

打开文件

1
2
3
4
5
6
7
void MainWindow::open() {
if (maybeSave()) {
QString filename = QFileDialog::getOpenFileName(this);
if (!filename.isEmpty())
loadFile(filename);
}
}

改变内容自动加*

1
2
3
4
5
6
7
8
9

// 改变内容的
connect(textEdit->document(), &QTextDocument::contentsChanged,
this, &MainWindow::documentWasModified);

void MainWindow::documentWasModified() { // 改变内容加上*
setWindowModified(textEdit->document()->isModified());
}

关闭事件询问是否保存

关闭事件

1
2
protected:
void closeEvent(QCloseEvent *event) override;

自动保存配置

保存配置

1
2
3
4
5
6
void MainWindow::writeSettings()
//! [37] //! [39]
{
QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
settings.setValue("geometry", saveGeometry());
}

读取配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void MainWindow::readSettings()
//! [34] //! [36]
{
QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray();
if (geometry.isEmpty()) {
const QRect availableGeometry = QApplication::desktop()->availableGeometry(this);
resize(availableGeometry.width() / 3, availableGeometry.height() / 2);
move((availableGeometry.width() - width()) / 2,
(availableGeometry.height() - height()) / 2);
} else {
restoreGeometry(geometry);
}
}

在初始化的时候读取配置就好了,在窗口结束的时候写入配置

此外,在main.cpp里面设置版本信息

1
2
3
QCoreApplication::setOrganizationName("Fyind Inc");
QCoreApplication::setApplicationName("SimpleNotepad");
QCoreApplication::setApplicationVersion("0.0.1");

添加终端支持

1
2
3
4
5
6
7
8
9
10
11
12
QCommandLineParser parser;
parser.setApplicationDescription(QCoreApplication::applicationName());
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument("file", "The file to open.");
parser.process(app);

MainWindow mainWin;
if (!parser.positionalArguments().isEmpty())
mainWin.loadFile(parser.positionalArguments().first());

mainWin.show();