目录
前言:
由于硬件开发中,免不了要做一些调试,别人的调试工具,好用是好用,但不是很美观,没有针对性和个性,所以就想着自己开发一些上位机,既可以硬件进行调试,又加入自己喜欢的元素,看着全部都是自己的劳动成果,岂不美哉。萌生想法,说干就干,一步一个脚印,QT务必拿下。
一、QT简介
Qt是一个跨平台的C++应用程序框架, 支持Windows、 Linux、 MacOS X、 Android、 iOS、 Windows Phone、 嵌入式系统等。 也就是说, Qt可以同时支持桌面应用程序开发、 嵌入式开发和移动开发, 覆盖了现有的所有主流平台。 开发者只需要编写一次代码, 而后在发布到不同平台之前重新编译即可。
我学QT主要是想为以后往Linux方向接触,Windows下一般用c#开发窗口程序,但Linux下不能。而QT开发,就避免这种情况,一次开发,全平台通用,岂不Nice。
我选用QT的另一个原因是Qt的文档设计得相当优秀, 可以到https://doc.qt.io/上一睹芳容。 Qt的文档完备且详细地覆盖了Qt的方方面面,比如开发Java,要经常去API手册中看相关类介绍和函数介绍啥的。而QT这些都不用单独去找,它自身就含有这些,QT中每一个类和方法都被详尽描述, 巨细靡遗, 举例充实。通过Qt Assistant工具可以方便地从一个类或者方法跳转到其他的类。 文档还包含了一个初学者教程和一些典型应用的例子, 方便用户查阅。 省去了开发中找资料的大部分时间。
二、安装QT软件
1、下载QT软件的网址:
https://download.qt.io/archive/qt/
上面下载的是离线包(推荐),当然也可以下载在线安装的包,但速度很慢,建议不要这样。
2、安装QT参考文章:安装QT
3、安装注意的地方:
1) 可能自从QT5.12版本后,安装中的上个页面就不能跳过了,必须去官网注册一个QT账号,然后填上下面的账号名和密码,才会出现NEXT(下一步)的选项。
2) 最伤的莫过于安装QT时的组件的选择安装了,全部安装,太占硬盘,又不会选择安装,对小白来说很不友好。我第一次也是丈二和尚摸不着头脑。
大致介绍可参考一位博主文章: 组件介绍
在选择要安装的组件时,需要注意的是,QT可以用两种编译器编译QT程序,一个是Visual Studio,另外一个是MinGW,是Minimalist GNUfor Windows的缩写。
如果选择了Visual Studio作为编译器,就必须安装对应版本的Visual Studio,刚开始我是安装Visual Studio2015,但是发现VS 太大了,十几个G,安装之后C盘所剩无几。虽然安装时选到了其他盘,但是大部分仍然还是被装到了C盘,卸载的时候也很无奈,得从控制面板中一个个卸载。
虽然安装QT时选择了MSVC组件,但是Visual Studio编译器并没有一起安装,因为Visual Studio是微软的软件,QT是不敢把它集成到自己的安装包中,但是如果选择了MinGW,就会在安装上MinGW编译器,因为MinGW是开源的.
所以我就选择时,第一个选择的的就是MinGW ***** 64bit。Tools中还是默认选择的QT creator那个。然后就一路NEXT就行。
安装好了,可以去网上找相关资料,试着新建一个工程项目,看是否安装好了。
三、软件学习
1、初始化main.cpp文件介绍
2、.pro工程文件介绍
在对某些功能进行编程时,需要在.pro文件下添加相应模块(例如网路编程就需要添加Network模块)
3、mywidget.h文件介绍
3、mywidget.cpp文件介绍
四、编程学习
1、信号与槽
(1)、自定义信号和槽函数
实例:
创建一个项目,用默认的widget类作为父窗口,再创建一个subwidget类作为子窗口。最后实现功能:在父窗口中添加一个按钮A,在子窗口中添加一个按钮B;点击按钮A时,父窗口隐藏,子窗口显示;点击按钮B时,子窗口隐藏,父窗口显示。
代码实例:
- widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPushButton>
#include "subwidget.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void widgetClose(); //槽函数:关闭父窗口,显示子窗口
void subwidgetClose(); //槽函数:显示父窗口,关闭子窗口
private:
Ui::Widget *ui;
QPushButton *btn_widget; //定义一个按钮类
//Subwidget *subwidget; //定义一个子窗口类
Subwidget subwidget;
};
#endif // WIDGET_H
- widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//subwidget = new Subwidget(this);
btn_widget = new QPushButton("显示子窗口",this);
btn_widget->move(100,100);
this.resize(200,200);
connect(btn_widget,&QPushButton::clicked,this,&Widget::widgetClose); //将按钮的点击信号和槽函数绑定
connect(&subwidget,&Subwidget::close,this,&Widget::subwidgetClose); //将子窗口的close信号和槽函数绑定
}
Widget::~Widget()
{
delete ui;
}
void Widget::widgetClose()
{
this->hide();
subwidget.show();
// subwidget->show();
}
void Widget::subwidgetClose()
{
this->show();
subwidget.hide();
// subwidget->hide();
}
- subwidget.h
#ifndef SUBWIDGET_H
#define SUBWIDGET_H
#include <QWidget>
#include <QPushButton>
class Subwidget : public QWidget
{
Q_OBJECT
public:
explicit Subwidget(QWidget *parent = nullptr);
void emitSignal(); //定义一个槽函数,向外发送给信号
private:
QPushButton *btn_subwidget; //定义一个按钮类
signals:
void close(); //自己定义一个信号
};
#endif // SUBWIDGET_H
- subwidget.cpp
#include "subwidget.h"
Subwidget::Subwidget(QWidget *parent) : QWidget(parent)
{
btn_subwidget = new QPushButton("显示父窗口",this);
btn_subwidget->move(100,100); //移动按钮的位置
this.resize(200,200); //指定当前窗口的大小
connect(btn_subwidget,&QPushButton::clicked,this,&Subwidget::emitSignal); //将按钮点击信号和槽函数关连
}
void Subwidget::emitSignal()
{
emit close(); //向外发送一个关闭子窗口的信号
}
注意:
1、widget类中创建了一个subwidget类,所以可以在widget窗口中对当前widget窗口和subwidget子窗口进行直接操作,但是在subwidget类中,却不能对那个widget窗口进行操作(可以好好想一想)。所以如果需要在subwidget类中对widget类进行操作,就可以利用信号机制,即subwidget发送一个信号给widget类,然后通知widget对自身进行操作。
(2)、自定义信号的重载
//如果发送的信号需要携带参数,那么槽函数也就需要接收此参数。
1、对信号进行重载:
signals:
void signal();
void signal(int);
void signal(QString);
2、将重载的信号和对应的槽函数进行绑定:
方法一:通过QT4版本的SIGNAL()和SLOT()功能。
方法二:通过将重载信号赋值给有参的指针函数。
void (MainWindow:: *s0)(int) = &MainWindow::signal;
connect(this,s0,[=](){
qDebug()<<"signal0"
});
void (MainWindow:: *s1)(int) = &MainWindow::signal;
connect(this,s1,[=](int i){
qDebug()<<i;
});
void (MainWindow:: *s2)(QString) = &MainWindow::signal;
connect(this,s2,[=](QString str){
qDebug()<<str
});
方法三:通过OverLoad方式。
connect(this, QOverload<>::of(&MainWindow::signal),[=](){
qDebug()<<"signal0";
});
connect(this, QOverload<int>::of(&MainWindow::signal),[=](int i){
qDebug()<<i;
});
connect(this, QOverload<QString>::of(&MainWindow::signal),[=](QString str){
qDebug()<<str;
});
对信号进行重载,发送有参信号时需要指定其中的参数:
emit signal();
emit signal(1);
emit signal("aaaa");
此时如果再用上面的连接语句,编译器就不知道对应的信号和槽是有参还是无参。
//此种连接不行了
connect(&subwidget,&Subwidget::close,this,&Widget::subwidgetClose); //将子窗口的close信号和槽函数绑定
//换成如下(利用函数指针):
//定义一个指向subwidget类的有参函数指针,并将信号函数的地址赋值给它,此时编译器就知道这个函数指针指向了有参信号函数。
void (subwidget::*subwidgetSignal)(QString) = &subwidget::close;
//同理,定义一个指向有参槽函数的函数指针
void (widget::*subwidgetSignal)(QString) = &Widget::subwidgetClose;
//此时调用如下语句,就指向了有参的信号和槽函数了
connect(&subwidget,&Subwidget::close,this,&Widget::subwidgetClose); //将子窗口的close信号和槽函数绑定
注意:
带参数的信号和对应的带参数的槽函数,参数类型要一一对应。
信号中的参数可以比对应连接的槽函数参数多,但是反之不行!
(3)、信号连接槽函数
1、QT4版本的connect函数
还有就是,在上面重载有参信号和槽函数时,不是需要创建有参/无参的函数指针吗?
如果用QT4版本的信号和槽函数写法,完全不用那么麻烦,直接将参数类型带到函数里面就行,比如SIGNAL(signal(int))。
2、QT5版本的connect函数
- 2.1、通过编写槽函数的方式:
connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method);
即connect(信号发送者对象指针,发送者对象的信号函数地址,信号接收者对象指针,接收者对象的槽函数地址); - 2.2、通过lamda表达式:
connect(const QObject *sender, const char *signal,{/ * 这里写槽函数的功能代码 * /});
注意:在UI设计界面中,选中控件,然后通过鼠标右键转到槽函数,这样的槽函数不需要通过connect将信号和该槽函数进行绑定,系统会自动绑定。比较方便。这样的槽函数系统命名都有规律:
即:1,以on_开头 2,加上组件名称,如pushButton_1(组件的名称一定要正确,否则不会自动关联),3,在加上操作_clicked()。
void MainWindow::on_pushButton_clicked()
{
}
(4)、信号连接信号
我们一般用connect函数用于将一个信号和另一个槽函数进行连接。除此之外,我们也可以将一个信号和另一个信号进行连接。
例如:在自定义信号和槽函数那个程序里,subwidget窗口中,有一个按钮,当我们点击按钮后,按钮会触发click信号,我们将按钮的click信号和emitSignal槽函数进行连接,然后在emitSignal槽函数发送close信号的。(这种方法需要定义一个点击按钮的槽函数,比较麻烦)
所以有了信号连接信号的功能,我们可以直接将按钮的click信号和subwidget的close信号进行连接:
connect(btn_subwidget,&QPushButton::clicked,this,&Subwidget::close); //将按钮点击信号和close信号连接
(5)、断开信号连接
程序中需要将某些信号和槽函数断开连接,可以用 disconnect() 函数,其中参数和connect写法一样。
总结:
2、菜单栏、工具栏、状态栏和浮动窗口
(1)、菜单栏
注意创建菜单栏有两种方式:
(1)、创建一个可以指定父亲的菜单栏
QMenuBar * menuBar = new QMenuBar(this);
this->setMenuBar(menuBar);
(2)、创建默认父亲是mainwindow的菜单栏
QMenuBar * menuBar = menuBar();
this->setMenuBar(menuBar);
对于这种创建方式,当创建菜单项时,可以进行简化为:
QMenu fileMenu = menuBar()->addMenu("文件");
(2)、工具栏
(3)、状态栏
(4)、浮动窗口
(5)、实例代码
代码:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QToolBar>
#include <QPushButton>
#include <QStatusBar>
#include <QLabel>
#include <QDockWidget>
#include <QTextEdit>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//注意:创建的菜单栏、工具栏、状态栏、浮动窗口都需要加入到mainwindow中去!!!
//1、新建菜单栏
QMenuBar *menuBar = new QMenuBar(this); //新建菜单栏对象
this->setMenuBar(menuBar); //将菜单栏加入到mainwindow中去
//在菜单栏中添加菜单项
QMenu *fileMenu = menuBar->addMenu("文件");
QMenu *editMenu = menuBar->addMenu("编辑");
//修改菜单栏的背景颜色
//this->setStyleSheet("QMenu::item:selected{background-color:#ff0000;} QMenuBar{background-color:#ee00ff;}");//紫色
//对菜单项添加动作
QAction *fileOpenAction = fileMenu->addAction("打开");
QAction *fileSaveAction = fileMenu->addAction("保存");
QAction *editCopyAction = editMenu->addAction("复制");
//给菜单的动作添加槽函数
connect(fileOpenAction,&QAction::triggered,this,[=](){qDebug()<<"打开成功!";});
//2、新建工具栏
QToolBar *toolBar = new QToolBar(this); //新建工具栏对象
this->addToolBar(toolBar); //将工具栏对象加入到mainwindow中去
//toolBar->setAllowedAreas(Qt::LeftToolBarArea|Qt::RightToolBarArea); //设置工具栏可以存在的区域
//为工具栏中添加动作
toolBar->addAction("编辑");
toolBar->addAction("项目");
//为工具栏添加按钮控件
QPushButton *btn = new QPushButton("工具栏控件",this);
toolBar->addWidget(btn);
//3、新建状态栏
QStatusBar *statusBar = new QStatusBar(this); //新建一个状态栏对象
this->setStatusBar(statusBar); //将状态栏添加到mainwindow中去
//给状态栏添加控件
QLabel *label1 = new QLabel("状态栏的一个标签控件",this);
QLabel *label2 = new QLabel("状态栏的一个标签控件,属于固定控件",this);
statusBar->addWidget(label1);
statusBar->addWidget(new QLabel("标签2"));
statusBar->addPermanentWidget(label2);
statusBar->addPermanentWidget(new QLabel("固定标签2"));
//4、新建浮动窗口
QDockWidget *dockWidget = new QDockWidget(); //新建一个浮动窗口对象
this->addDockWidget(Qt::RightDockWidgetArea,dockWidget); //将浮动窗口添加到mainwindow中
//给浮动栏中添加控件
QTextEdit * textEdit = new QTextEdit(this);
dockWidget->setWidget(textEdit);
}
MainWindow::~MainWindow()
{
delete ui;
}
运行结果:
3、资源文件添加
在程序中,有的控件需要显示ICON图标,
一种方式就是指定图片文件的路径,如果图片路径被改变,需要重新指定。
二种就是将图片文件放在工程文件下,然后将这些图片添加到工程的资源文件中去(推荐)。这里举例第二种:
注:为什么添加文件的时候需要先添加前缀?
因为一个大的工程项目,往往需要用到很多资源文件,通过前缀可以对资源文件进行分类。
使用资源文件:
//对ui中菜单项下的Action事件添加资源文件下的图片
ui->actionopen->setIcon(QIcon(":/pictures/2.png"));
//对ui中的标签控件添加资源文件下的图片
ui->label_2->setStyleSheet("border-image: url(:/pictures/2.png)");
或者
ui->label_2->setPixmap(QPixmap(":/pictures/2.png"));
注:添加资源文件——“: + 前缀 + 文件名”
4、模态和非模态对话框
在对话框存在时,不能对其它窗口进行操作,即为模态对话框。反之为非模态。
1、模态对话框:
实现点击按钮,弹出一个模态对话框。
connect(ui->pushButton,&QPushButton::clicked,this,[=](){
QDialog *dialog = new QDialog(this);
qDebug()<<"模态对话框打开了";
// dialog->open(); //非阻塞
dialog->exec(); //阻塞在这里,等待关闭
dialog.setAttribute(Qt::WA_DeleteOnClose);
qDebug()<<"模态对话框关闭了";
});
2、非模态对话框:
实现点击按钮,弹出一个模态对话框。
connect(ui->pushButton,&QPushButton::clicked,this,[=](){
QDialog *dialog = new QDialog(this);
dialog->show();
dialog.setAttribute(Qt::WA_DeleteOnClose);
});
关闭对话框后,对话框对象并没有被删除,所以需要一条语句使得关闭对话框后能够删除该对象资源: dialog.setAttribute(Qt::WA_DeleteOnClose);
5、对话框
1、消息对话框QMessageBox:
例如:
2、其它对话框
(1)、颜色对话框QColorDialog:
(2)、文件对话框QFileDialog:
(3)、字体对话框QFontDialog:
6、listWidget列表控件
1、用addItem()方法一次添加一条列表
变为水平居中:
2、用addItems()方法一次添加多条列表:
注:这种方法,不可以实现列表水平居中。