QT入门、移植、部分底层分析及函数和类的介绍

教程

QT5基础教程(介绍,下载,安装,第一个QT程序)_小熊coder的博客-CSDN博客

手册

D:\Qt\Qt5.9.5\5.9.5\mingw53_32\bin\assistant.exe

所有函数 | Qt 5.15 - 中文文档编制,帮助,手册,教程 (digitser.top) 所有类 | Qt 5.9.9 - 中文文档编制,帮助,手册,教程 (digitser.top)

功能

Qt 除了可以绘制漂亮的界面(包括控件、布局、交互),还包含很多其它功能,比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等,这些 Qt 都已经内置了。

大部分应用程序都可以使用 Qt 实现,除了与计算机底层结合特别紧密的,例如驱动开发,它直接使用硬件提供的编程接口,而不能使用操作系统自带的函数库。

Qt 主要用于桌面程序开发嵌入式开发

QT入门

QT5基础教程(介绍,下载,安装,第一个QT程序)_小熊coder的博客-CSDN博客

Qt是什么?Qt简介(非常全面) (biancheng.net)(含教程,收费)

项目文件简介

QT入门、移植、部分底层分析及函数和类的介绍_第1张图片

pro文件详解

深入Qt--pro文件解析_行循自然-kimi的博客-CSDN博客

pro是项目管理文件

任何一个 Qt 项目都至少包含一个 pro 文件,此文件负责存储与当前项目有关的配置信息,比如:

项目中用到了哪些模块?
项目中包含哪些源文件,哪些头文件,它们的存储路径是什么?
项目使用哪个图片作为应用程序的图标?
项目最终生成的可执行文件的名称是什么?

所谓模块,可以简单地理解为文件夹或者压缩包,内部包含多个功能相近的类。作为一款成熟的 GUI 框架,Qt 提供了大量的类,根据这些类的功能,Qt 将它们分成了几个组,每个组称为一个模块。打开 Qt Creator 的帮助界面并搜索“All modules”,可以看到 Qt 的所有模块。

一个项目中可能包含上百个源文件,Qt 编译这些源文件的方法是:先由 qmake 工具根据 pro 文件记录的配置信息生成相应的 makefile 文件,然后执行 make 命令完成对整个项目的编译。也就是说,pro 文件存储的配置信息是用来告知编译器如何编译当前项目的,所以一个 Qt 项目要想完美运行,既要保证各个源文件中程序的正确性,还要保证 pro 文件中配置信息的合理性。

对于一个刚刚创建好的 Qt 项目,pro 文件并不是空的,而是包含一些基本的配置信息。实际开发中,Qt 会自动修改 pro 文件的内容,但有时也需要我们手动修改,例如程序中用到某个第三方库时,就需要我们手动修改 pro 文件。

接下来,我们就为大家详细地讲解 pro 文件中各个配置信息的含义,以及如何手动修改 pro 文件。

pro配置跨平台信息
win32 {
    INCLUDEPATH += $$PWD/xxx/win32
    LIBS += $$PWD/xxx/win32/xxx.lib 
}
​
unix {
    INCLUDEPATH += $$PWD/xxx/unix
    LIBS += $$PWD/xxx/unix/xxx.so 
}
​
linux{
    message(linux)
    INCLUDEPATH += $$PWD/xxx/unix
    LIBS += $$PWD/xxx/unix/xxx.so 
}

$$PWD 是pro所在目录,是相对目录,建议用相对目录;

pro自定义平台编译配置
DEFINES += ARM
#DEFINES += X86
#DEFINES += LINUX
​
if(contains(DEFINES,ARM)){
    message("arm")
    LIBS += $$PWD/xxx.a
    LIBS += -L/home/xxx/ -lxxx
}elif(contains(DEFINES,LINUX)){
    message("linux")
    LIBS += $$PWD/xxx.a
}else{
     message("x86")
    LIBS += $$PWD/xxx.lib
}

或者可以

命令配置
qmake -spec xxx

例如,

-spec win32-g++选项参数是指使用 win32-g++ 定制脚本来生成 Makefile,对于不同的编译器和操作系统平台会使用不同的定制脚本,win32-g++ 是专门针对 Windows 系统 MinGW 编译环境的定制脚本(实际的定制脚本位于 QTDIR/mkspecs/win32-g++ 文件夹里)

当然,也可用

qmake.conf配置

例如,以下不同平台的qmake.conf

#
# qmake configuration for linux-g++
#
​
MAKEFILE_GENERATOR      = UNIX
CONFIG                 += incremental
QMAKE_INCREMENTAL_STYLE = sublib
​
include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)
load(qt_config)
#
# qmake configuration for building with aarch64-linux-gnu-g++
#
​
MAKEFILE_GENERATOR      = UNIX
CONFIG                 += incremental
QMAKE_INCREMENTAL_STYLE = sublib
​
include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)
​
# modifications to g++.conf
QMAKE_CC                = aarch64-poky-linux-gcc
QMAKE_CXX               = aarch64-poky-linux-g++
QMAKE_LINK              = aarch64-poky-linux-g++
QMAKE_LINK_SHLIB        = aarch64-poky-linux-g++
​
# modifications to linux.conf
QMAKE_AR                = aarch64-poky-linux-ar cqs
QMAKE_OBJCOPY           = aarch64-poky-linux-objcopy
QMAKE_NM                = aarch64-poky-linux-nm -P
QMAKE_STRIP             = aarch64-poky-linux-strip
​
load(qt_config)

工具栏

QT入门、移植、部分底层分析及函数和类的介绍_第2张图片

坐标系

左上角0,0 x以右为正,y以下为正

对象树

当创建的对象在堆区时候,如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类,可以不用管理释放的操作,将对象会放入到对象树中,回收的时候一并回收,一定程度上简化了内存回收机制

信号和槽

Qt信号和槽机制详解 (biancheng.net)

信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来

(例子)通过信号和槽机制,我们可以将按钮和窗口关联起来,实现“点击按钮会使窗口关闭”的效果

在 Qt 中,用户和控件的每次交互过程称为一个事件,比如“用户点击按钮”是一个事件,“用户关闭窗口”也是一个事件每个事件都会发出一个信号,例如用户点击按钮会发出“按钮被点击”的信号,用户关闭窗口会发出“窗口被关闭”的信号。

QT入门、移植、部分底层分析及函数和类的介绍_第3张图片

信号和槽机制底层是通过函数间的相互调用实现的。

每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。例如,“按钮被按下”这个信号可以用 clicked() 函数表示,“窗口关闭”这个槽可以用 close() 函数表示,信号和槽机制实现“点击按钮会关闭窗口”的功能,其实就是 clicked() 函数调用 close() 函数的效果。

信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:

  • 信号函数用 signals 关键字修饰槽函数用 public slots、protected slots 或者 private slots 修饰signals 和 slotsQt 在 C++ 的基础上扩展的关键字,专门用来指明信号函数和槽函数

  • 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)

注意,并非所有的控件之间都能通过信号和槽关联起来,信号和槽机制只适用于满足以下条件的控件:

  • 控件类必须直接或者间接继承自 QObject 类。Qt 提供的控件类都满足这一条件,这里提供一张 Qt常用类的继承关系的高清图片,感兴趣的读者可以简单了解一下。

  • 控件类中必须包含 private 属性的 Q_OBJECT 宏。

将某个信号函数和某个槽函数关联起来,需要借助 QObject 类提供的 connect() 函数

connect()函数实现信号和槽

connect() 是 QObject 类中的一个静态成员函数,专门用来关联指定的信号函数和槽函数

关联某个信号函数和槽函数,需要搞清楚以下 4 个问题:

  • 信号发送者是谁?

  • 哪个是信号函数?

  • 信号的接收者是谁?

  • 哪个是接收信号的槽函数?

例如,用新版 connect() 函数关联 But 按钮的 clicked() 信号函数和 widget 窗口的 close() 槽函数,实现代码为:

connect(&But, &QPushButton::clicked, &widget, &QWidget::close); //(信号发送者,信号,信号接收者,槽)

可以看到,新版 connect() 函数指定信号函数和槽函数的语法格式是&+函数所在类+函数名

一个 connect() 函数只能关联一个信号函数和一个槽函数,程序中可以包含多个 connect() 函数,能实现以下几种效果:

  • 关联多个信号函数和多个槽函数;

  • 一个信号函数可以关联多个槽函数,当信号发出时,与之关联的槽函数会一个接一个地执行,但它们执行的顺序是随机的,无法人为指定哪个先执行、哪个后执行;

  • 多个信号函数可以关联同一个槽函数,无论哪个信号发出,槽函数都会执行。

此外,connect() 函数的 method 参数还可以指定一个信号函数,也就是说,信号之间也可以相互关联,这样当信号发出时,会随之发出另一个信号

emit 关键字

对于 Qt 提供给我们的信号函数,其底层已经设置好了信号发出的时机,例如按下鼠标时、点击 Enter 回车键时等等。对于自定义的信号,我们需要自己指定信号发出的时机,这就需要用到 emit 关键字

自定义信号函数

class MyWidget:public QWidget{
    //Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
    Q_OBJECT
//自定义信号函数
signals:
    void MySignal(QString mess);
public:
    void emitSignal(){
        emit MySignal(message);
    }
private:
    QString message;
};

自定义槽函数

槽函数既可以普通全局函数、也可以是类的成员函数静态成员函数友元函数虚函数,还可以用 lambda 表达式表示。

和信号函数不同,槽函数必须手动定义(实现)。槽函数可以在程序中直接调用,但主要用来响应某个信号。自定义一个槽函数时,需要注意以下几点:

槽函数的返回值必须和信号函数相同,由于信号函数的返回值一定是 void,所以槽函数的返回值也必须为 void; 对于带参的信号函数,槽函数可以选择接收所有参数,则参数的类型、顺序、个数都必须与信号函数相同;也可以选择接收前几个参数,这些参数的类型、顺序都必须与信号函数相同;还可以选择不接受任何参数。 槽函数的参数个数只能比信号函数少,不能比信号函数多; 槽函数的参数不能有默认值

class MyWidget:public QWidget{
    //Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
    Q_OBJECT
signals:
    void MySignal(QString mess1,QString mess2);
public:
    void emitSignal(){
        emit MySignal(message1,message2);
    }
    //类的成员函数
    void recSlot1(QString mess){
        qDebug() << "执行 recSlot1() 成员函数,输出" << mess;
    }
//指明定义的是槽函数
public slots:
    void recSlot2(QString mess1,QString mess2){
        qDebug() << "执行 recSlot2() 槽函数,输出"<< mess1 << " " << mess2;
    }
public:
    QString message1;
    QString message2;
};
//全局函数
void recSlot3(){
    qDebug() << "执行 recSlot3() 全局函数";
}
​

自定义信号和槽实例

//main.cpp
#include 
#include 
#include 
class MyWidget:public QWidget{
    //Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
    Q_OBJECT
//信号函数
signals:
    void MySignal(QString mess1,QString mess2);
public:
    //发射信号的函数
    void emitSignal(){
        emit MySignal(message1,message2);
    }
    //普通类成员函数
    void recSlot1(QString mess){
        qDebug() << "执行 recSlot1() 成员函数,输出" << mess;
    }
//槽函数
public slots:
    void recSlot2(QString mess1,QString mess2){
        qDebug() << "执行 recSlot2() 槽函数,输出"<< mess1 << " " << mess2;
    }
public:
    QString message1;
    QString message2;
};
//全局函数
void recSlot3(){
    qDebug() << "执行 recSlot3() 全局函数";
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //创建主窗口
    MyWidget mywidget;
    mywidget.message1 = "C语言中文网";
    mywidget.message2 = "http://c.biancheng.net";
    //类的成员函数作为槽函数
    QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot1);
    //信号函数和槽函数相连接
    QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot2);
    //全局函数作为槽函数
    QObject::connect(&mywidget,&MyWidget::MySignal,&recSlot3);
    mywidget.show();
    //发射 Signal 信号
    mywidget.emitSignal();
    return a.exec();
}
//MyWidget类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。
#include "main.moc"

信号和槽实例

#include "mainwindow.h"
#include 
#include 
#include 
​
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;                   //添加窗口
    QPushButton but("按钮",&w);      //定义按钮控件
    but.setGeometry(100,100,100,50);  //设置尺寸大小 x,y,长,宽
​
    //信号与槽,实现用户点击按钮时,widget窗口关闭
    QObject::connect(&but, &QPushButton::clicked, &w, &QWidget::close);
​
    //显示窗口
    w.show();
​
    return a.exec();
}
​

QFile文件操作

QFile 类支持对文件进行读取、写入、删除、重命名、拷贝等操作,它既可以操作文件文件,也可以操作二进制文件。

使用 QFile 类操作文件之前,程序中需引入头文件。创建 QFile 类的对象,常用的构造函数有:

QFile::QFile()
QFile::QFile(const QString &name)

参数 name 用来指定要操作的目标文件,包含文件的存储路径和文件名,存储路径可以使用绝对路径(比如 “D:/Demo/test.txt”)或者相对路径(比如"./Demo/test.txt"),路径中的分隔符要用 “/” 表示。

通常情况下,我们会调用第二个构造函数,直接指明要操作的文件。对于第一个构造函数创建的 QFile 对象,需要再调用 setFileName() 方法指明要操作的文件。

与 C++ 读写文件的规则一样,使用 QFile 读写文件之前必须先打开文件,调用 open() 成员方法即可,常用的语法格式为:

bool QFile::open(OpenMode mode)
file.readAll()
​

文本标签

QLabel文本框,可用于显示文本、图片、超链接、动画...

QLabel(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags())
QLabel(const QString &text, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags())

各个参数的含义分别是:

text 参数用于指定文本框中显示的文字parent 参数用于指定文本框的父窗口WindowFlags 是一种枚举类型,f 参数用来设置文本框的一些系统属性和外观属性,默认值为 Qt::Widget,表示当不指定父窗口时,文本框将作为一个独立窗口,反之则作为父窗口中的一个控件。f 参数的可选值有很多,比如 Qt::Window 表示文本框将作为一个独立的窗口,它自带边框和标题栏,Qt::ToolTip 表示文本框将作为一个提示窗口,不带边框和标题栏等等,这里不再一一列举。 需要注意的是,第一个构造函数中的 parent 和 f 参数都有默认值,因此 QLabel 类还隐含了一个默认构造函数。也就是说,创建 QLable 对象时可以不传递任何参数,或者只给 txt 参数传递一个字符串,就可以成功创建一个文本框。通常情况下,我们会给 text 和 parent 参数传递相应的值,即在创建文本框的同时指定文本内容和父窗口。

窗口居中显示

label.setText("hello world");
        label.setParent(&w);
        label.setAlignment(Qt::AlignCenter);        //设置文本框内容居中显示
        label.topLevelWidget();
        label.move ( (w.width() - label.width() ) / 2, (w.height() -                        label.height()) / 2) ;
        qDebug() << label.pos().x() << label.pos().y();
        label.show();

字体大小

QLabel *lb    = new QLabel(tr("examp"));
QFont ft;
ft.setPointSize(14);
lb->setFont(ft);

设置文本

 QString str;
 label.setText(str);                         //设置文本

控件和事件

控件

Qt 控件又称组件或者部件,指用户看到的所有可视化界面以及界面中的各个元素,比如按钮、文本框、输入框等。

为了方便程序员开发,Qt 提供了很多现成的控件。打开某个带 ui 文件的 Qt Widgets Application 项目,ui 文件的 Widget Box 一栏展示了 Qt 提供的几乎所有控件:

QT入门、移植、部分底层分析及函数和类的介绍_第4张图片

每个控件类都包含一些常用的属性和方法

可以通过继承某个控件类的方式自定义一个新的控件

  • QMainWindow 类生成的窗口自带菜单栏、工具栏和状态栏,中央区域还可以添加多个控件,常用来作为应用程序的主窗口

  • QDialog 类生成的窗口非常简单,没有菜单栏、工具栏和状态栏,但可以添加多个控件,常用来制作对话框

事件

Qt 事件指的是应用程序和用户之间的交互过程,例如用户按下某个按钮,点击某个输入框等等

一个 Qt 界面程序要想接收事件,main() 函数中就必须调用 exec() 函数,它的功能就是使程序能够持续不断地接收各种事件

Qt 程序可以接收的事件种类有很多,例如鼠标点击事件、鼠标滚轮事件、键盘输入事件、定时事件等。每接收一个事件,Qt 会分派给相应的事件处理函数来处理。所谓事件处理函数本质就是一个普通的类成员函数,以用户按下某个 QPushButton 按钮为例,Qt 会分派给 QPushButton 类中的 mousePressEvent() 函数处理。

事件处理函数通常会完成两项任务,分别是:

  • 修改控件的某些属性,比如当用户按下按钮时,按钮的背景颜色会发生改变,从而提示用户已经成功地按下了按钮;

  • 运用信号和槽机制处理事件

QLineEdit单行输入框

创建

QLineEdit(QWidget *parent = Q_NULLPTR)
QLineEdit(const QString &contents, QWidget *parent = Q_NULLPTR)
#include 
#include 
#include 
using namespace std;
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //创建一个窗口,作为输入框的父窗口
    QWidget widget;
    //设置窗口的标题
    widget.setWindowTitle("QWidget窗口");
   
    //接下来,分别创建两个输入框,用于让用户分别输入账号和密码
    //创建账号输入框
    QLineEdit lineEdit(&widget);
    //指定输入框位于父窗口中的位置
    lineEdit.move(100,100);
    //设置提示信息
    lineEdit.setPlaceholderText("请输入账号...");
    //让输入框显示“一键清除”按钮
    lineEdit.setClearButtonEnabled(true);
   
    //创建密码输入框
    QLineEdit lineEditPass(&widget);
    lineEditPass.setPlaceholderText("请输入密码...");
    lineEditPass.move(100,150);
    //指定文本显示方式,保护用户账号安全
    lineEditPass.setEchoMode(QLineEdit::Password);
   
    //指定窗口的尺寸和显示文字的大小
    widget.resize(500,300);
    widget.setFont(QFont("宋体",16));
    widget.show();
    return a.exec();
}
​

QListWidget列表框

//main.cpp#include #include #include #include #include using //main.cpp
#include 
#include 
#include 
#include 
#include 
using namespace std;
class QMyLabel:public QLabel{
    Q_OBJECT
public slots:
    void rsetText(QListWidgetItem *item);
};
void QMyLabel::rsetText(QListWidgetItem *item){
    this->setText(item->text());
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //创建一个窗口,作为输入框的父窗口
    QWidget widget;
    //设置窗口的标题
    widget.setWindowTitle("QWidget窗口");
    widget.resize(500,500);
    QListWidget listWidget(&widget);
    listWidget.resize(500,400);
    listWidget.setFont(QFont("宋体",14));
    listWidget.addItem("C语言中文网");
    listWidget.addItem("http://c.biancheng.net");
    listWidget.addItem(new QListWidgetItem("Qt教程"));
    QMyLabel print;
    print.setText("选中内容");
    print.setParent(&widget);
    print.resize(500,100);
    print.move(0,400);
    print.setAlignment(Qt::AlignCenter);
    QObject::connect(&listWidget,&QListWidget::itemClicked,&print,&QMyLabel::rsetText);
    widget.show();
    return a.exec();
}
//QMyLabel类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。
#include "main.moc"
​

布局管理

代码

#include 
#include 
#include 
#include 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //创建主窗口
    QWidget widget;
    widget.setWindowTitle("QHBoxLayout水平布局");
    //创建水平布局管理器
    QHBoxLayout *layout=new QHBoxLayout;
    //设置布局管理器中所有控件的布局方向为从右往左依次排列
    layout->setDirection(QBoxLayout::RightToLeft);
    //连续创建 3 个文本框,并设置它们的背景和字体大小
    QLabel lab1("Label1");
    lab1.setStyleSheet("QLabel{background:#dddddd;font:20px;}");
    lab1.setAlignment(Qt::AlignCenter);
    QLabel lab2("Label2");
    lab2.setStyleSheet("QLabel{background:#cccccc;font:20px;}");
    lab2.setAlignment(Qt::AlignCenter);
    QLabel lab3("Label3");
    lab3.setStyleSheet("QLabel{background:#ffffff;font:20px;}");
    lab3.setAlignment(Qt::AlignCenter);
    //将 3 个文本框和 2 个空白列添加到管理器中,它们的拉伸系数比是 2:1:2:3:3
    layout->addStretch(2);
    layout->addWidget(&lab1,1);
    layout->addWidget(&lab2,2);
    layout->addWidget(&lab3,3);
    layout->addStretch(3);
    //将布局管理器添加到 widget 窗口中
    widget.setLayout(layout);
    widget.show();
    return a.exec();
}
​

QLayout: Attempting to add QLayout “to ***“, which already has a layout

【QT】QLayout: Attempting to add QLayout “to ***“, which already has a layout的终极解决方法_Small-K的博客-CSDN博客

QT获取屏幕数量,指定屏幕

你可以通过QGuiApplication::screens()函数获取连接到你的系统上的所有屏幕。这个函数返回一个QList,每个QScreen都代表着一个物理屏幕。

以下是获取所有屏幕数量的示例代码:

#include 
#include 
​
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
​
    QList screens = QGuiApplication::screens();
    int numScreens = screens.count();
​
    qDebug() << "Number of screens: " << numScreens;
​
    return app.exec();
}

如果你想要指定显示某个屏幕,可以使用QWindowsetScreen()方法。这个方法接受一个QScreen*参数,用于将一个QWindow移动到特定的屏幕上。

以下是将一个QWindow移动到特定屏幕的示例代码:

#include 
#include 
#include 
​
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
​
    // 获取第二个屏幕
    QList screens = QGuiApplication::screens();
    QScreen *secondScreen = screens.value(1);
​
    // 创建一个新的窗口
    QWindow window;
    window.setGeometry(0, 0, 400, 400);
​
    // 将窗口移动到第二个屏幕
    window.setScreen(secondScreen);
​
    // 显示窗口
    window.show();
​
    return app.exec();
}

在这个示例中,我们首先获取了所有屏幕,然后使用value()方法获取了第二个屏幕(索引从0开始)。接着,我们创建一个新的窗口,并使用setScreen()方法将这个窗口移动到第二个屏幕上。

注意,这个示例假设你的系统上至少连接了两个屏幕。如果你只连接了一个屏幕,程序将会出现异常。在你的代码中你需要添加一些异常处理方法,以确保程序的健壮性。

QT跨平台

Qt介绍1---QPA(Qt Platform Abstraction)qt_qpa_platform西瓜瓜的博客-CSDN博客

QPA是qt的平台抽象,主要用于实现跨平台 QPA使得Qt对不同平台的支持变得更加灵活,当需要支持一个新平台时,只需为该平台编写一个QPA插件

跨平台实现的相关文件

QT入门、移植、部分底层分析及函数和类的介绍_第5张图片

QPA插件

QT入门、移植、部分底层分析及函数和类的介绍_第6张图片

Qt5运行时需要通过“-platform”来制定QPA插件,如果不指定就默认使用QPA插件,在QT_QPA_PLATFORM变量制定,比如:

 ./app -platform eglfs
 ./app -platform linuxfb

eglfs linuxfb 均为绘图插件

  1. eglfs,在绘图的时候会使用GPU渲染UI,如果平台有GPU,尽量使用eglfs。

  2. libqlinuxfb,linux标准的显示框架,会打开/dev/fb0节点进行绘图和显示。

QT编译原理解析

Qt之编译原理解析 - 怪小子 - 博客园 (cnblogs.com)

qmake工作的一些内容

<1> C++没有信号和槽是Qt的特性,及ui库相关的模块,所以Qt会将项目源文件扩展项扩展为 C++ 代码,这样就可以使用 C++的编译器来编译Qt的项目 <1.1> ui 扩展为 ui_*.h _ <1.2> qrc 扩展为 qrc_*.cpp <1.3> 引用了 Qt 的元对象系统头文件扩展为 moc_.cpp <2> 现在Qt项目中的所有代码都是C++源代码了,这里就可以直接使用C++编译器把源代码编译成二进制文件 <3> 执行编译的链接步骤,输出可执行文件

QT入门、移植、部分底层分析及函数和类的介绍_第7张图片

案例

不用qmake,手动编译

准备 >> 新建一个默认的 Qt Gui 程序,将其源代码拷贝到准备好的编译目录,有以下文件: <1> main.cpp <2> widget.cpp <3> widget.h <4> widget.qrc <5> widget.ui <6> widget.pro widget.pro.user 删掉,我们用不着

操作步骤:

  <1> 新建编译输出目录
  mkdir build
  cp .../src_dir/*.* .../build/
  cd .../build/
  <2> 扩展ui文件为C++格式
​
  /opt/Qt5.12.8/5.12.8/gcc_64/bin/uic widget.ui -o ui_widget.h
  <3> 扩展qrc文件为C++格式
​
  /opt/Qt5.12.8/5.12.8/gcc_64/bin/rcc widget widget.qrc -o qrc_widget.cpp
  <4> 扩展widget.h到moc_widget.cpp
​
  /opt/Qt5.12.8/5.12.8/gcc_64/bin/moc widget.h -o moc_widget.cpp
​
  <5> 当前所有的代码都为C++代码了,可以进行编译了
  <5.1> 编译main.cpp
  g++ -c -pipe -g -std=gnu++11 -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/include -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtWidgets -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtGui -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtCore -I. -isystem /usr/include/libdrm -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/mkspecs/linux-g++ -o main.o main.cpp
​
  <5.2> 编译 widget.h
  g++ -c -pipe -g -std=gnu++11 -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/include -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtWidgets -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtGui -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtCore -I. -isystem /usr/include/libdrm -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/mkspecs/linux-g++ -o widget.o widget.cpp
​
  <5.3> 编译qrc_widget.cpp
  g++ -c -pipe -g -std=gnu++11 -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/include -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtWidgets -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtGui -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtCore -I. -isystem /usr/include/libdrm -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/mkspecs/linux-g++ -o qrc_widget.o qrc_widget.cpp
​
  <5.4> 编译moc_widget.cpp
  g++ -c -pipe -g -std=gnu++11 -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/include -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtWidgets -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtGui -I/opt/Qt5.12.8/5.12.8/gcc_64/include/QtCore -I. -isystem /usr/include/libdrm -I. -I/opt/Qt5.12.8/5.12.8/gcc_64/mkspecs/linux-g++ -o moc_widget.o moc_widget.cpp
​
  <6> 链接
  g++ -Wl,-rpath,/opt/Qt5.12.8/5.12.8/gcc_64/lib -o testting main.o widget.o qrc_widget.o moc_widget.o   /opt/Qt5.12.8/5.12.8/gcc_64/lib/libQt5Widgets.so /opt/Qt5.12.8/5.12.8/gcc_64/lib/libQt5Gui.so /opt/Qt5.12.8/5.12.8/gcc_64/lib/libQt5Core.so -lGL -lpthread 
​
  <7> Qt程序打包
  省略

QT编译流程

qmake.conf 文件位于 Qt 安装目录下的 mkspecs 文件夹中。该文件夹中包含了一系列针对不同平台和编译器的配置文件,每个文件和平台/compiler 组合相关联。

qmake 在构建过程中被调用时,它会自动查找适用于当前平台和编译器qmake.conf 文件。这意味着 qmake.conf 的选择是自动完成的,无需手动指定

在执行 qmake 命令时,qmake 将加载所需的 qmake.conf 文件,并根据其中的设置和规则来解析项目文件(通常是 .pro 文件)。 qmake.conf 中的变量和选项将影响生成的 Makefile 的内容,从而确定了项目的构建行为。

流程:

①qmake编译pro文件生成Makefile ②make编译代码

QT事件底层解析

Qt 事件机制,底层实现原理qt底层原理燕山暮雪的博客-CSDN博客

QApplication::exec()

调用QApplication::exec()时,程序进入了Qt的事件循环

 1 while (!exit_was_called)
 2 {
 3     while (!posted_event_queue_is_empty)
 4     {
 5         process_next_posted_event();             //处理所有的posted事件
 6     }
 7     while (!spontaneous_event_queue_is_empty)
 8     {
 9         process_next_spontaneous_event();        //处理所有spontaneous事件
10     }
11     while (!posted_event_queue_is_empty)
12     {
13         process_next_posted_event();             //处理所有posted事件
14     }
15 }

exec层层解剖

qapplication.cpp (qtbase/src/widgets/kernel)

QT入门、移植、部分底层分析及函数和类的介绍_第8张图片

往下走qguiapplication.cpp (qtbase/src/gui/kernel)

QT入门、移植、部分底层分析及函数和类的介绍_第9张图片

QT_NO_ACCESSIBILITY是一个宏定义,它用于禁用Qt框架中的辅助功能支持。辅助功能是指通过屏幕阅读器等辅助工具来帮助用户访问和操作应用程序的功能。通过定义QT_NO_ACCESSIBILITY宏,可以在编译时禁用这些辅助功能,从而减少应用程序的体积和内存占用。

QAccessible::setRootObject(qAPP)

void QAccessible::setRootObject(QObject *object)
{
    if (rootObjectHandler) {
        rootObjectHandler(object);
        return;
    }
​
    if (QPlatformAccessibility *pfAccessibility = platformAccessibility())
        pfAccessibility->setRootObject(object);
}

大概是初始化根对象。

QCoreApplication::exec() (qtbase/src/corelib/kernel)

int QCoreApplication::exec()
{
    //检查QCoreApplication的实例是否存在
    if (!QCoreApplicationPrivate::checkInstance("exec"))
        return -1;  
​
    //获取当前线程的QThreadData对象,并与self对象(指向QCoreApplication的指针)的d_func()函数返回的             threadData进行比较。如果不相等,输出警告消息并返回-1,表示exec()函数必须在主线程中调用   
    QThreadData *threadData = self->d_func()->threadData;
    if (threadData != QThreadData::current()) {
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
        return -1;
    }
    //检查threadData->eventLoops是否为空
    if (!threadData->eventLoops.isEmpty()) {
        qWarning("QCoreApplication::exec: The event loop is already running");
        return -1;
    }
​
    threadData->quitNow = false;
    QEventLoop eventLoop;
    self->d_func()->in_exec = true;
    self->d_func()->aboutToQuitEmitted = false;
    int returnCode = eventLoop.exec();
    threadData->quitNow = false;
​
    //清理执行相关的资源
    if (self)
        self->d_func()->execCleanup();
​
    //返回事件循环的返回码returnCode,标志着程序执行的结果
    return returnCode;
}

QEventLoop eventLoop; (qtbase/src/corelib/kernel) returnCode = eventLoop.exec();

int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadRelaxed();
​
    //we need to protect from race condition with QThread::exit
    QMutexLocker locker(&static_cast(QObjectPrivate::get(threadData->thread.loadAcquire()))->mutex);
    if (threadData->quitNow)
        return -1;
​
    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }
​
    struct LoopReference {
        QEventLoopPrivate *d;
        QMutexLocker &locker;
​
        bool exceptionCaught;
        LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)
        {
            d->inExec = true;
            d->exit.storeRelease(false);
​
            auto threadData = d->threadData.loadRelaxed();
            ++threadData->loopLevel;
            threadData->eventLoops.push(d->q_func());
​
            locker.unlock();
        }
​
        ~LoopReference()
        {
            if (exceptionCaught) {
                qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
                         "exceptions from an event handler is not supported in Qt.\n"
                         "You must not let any exception whatsoever propagate through Qt code.\n"
                         "If that is not possible, in Qt 5 you must at least reimplement\n"
                         "QCoreApplication::notify() and catch all exceptions there.\n");
            }
            locker.relock();
            auto threadData = d->threadData.loadRelaxed();
            QEventLoop *eventLoop = threadData->eventLoops.pop();
            Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
            Q_UNUSED(eventLoop); // --release warning
            d->inExec = false;
            --threadData->loopLevel;
        }
    };
    LoopReference ref(d, locker);
​
    // remove posted quit events when entering a new event loop
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())
        QCoreApplication::removePostedEvents(app, QEvent::Quit);
​
#ifdef Q_OS_WASM
    // Partial support for nested event loops: Make the runtime throw a JavaSrcript
    // exception, which returns control to the browser while preserving the C++ stack.
    // Event processing then continues as normal. The sleep call below never returns.
    // QTBUG-70185
    if (threadData->loopLevel > 1)
        emscripten_sleep(1);
#endif
​
    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
​
    ref.exceptionCaught = false;
    return d->returnCode.loadRelaxed();
}

QT移植IMX8MP 评估板

要想在ARM平台上运行Qt程序,得满足以下两个点: 1、ARM平台上搭建Qt运行环境 2、获得通过交叉编译后的Qt可执行文件

(SDK已搭建)

思路:windows上的工程复制到虚拟机,然后用qmake生成makefile,最后make编译

1、windowns 下的 Qt 工程拷贝到linux 2、source sdk 环境 3、进入Qt 工程目录执行 qmake 4、执行 make

QT程序

函数

toint函数
rotation.toInt()    //转整型

Qt5中,toInt方法的参数有什么含义?-编程语言-CSDN问答

availabGeometry

Qt 中 availableGeometry 函数是一个用于获取屏幕上可用工作区域的函数。工作区域是指屏幕上不包括任务栏、Dock 等系统窗口的区域。

availableGeometry 函数的定义如下:

QRect QDesktopWidget::availableGeometry(const QWidget *widget = Q_NULLPTR) const

该函数是 QDesktopWidget 类的成员函数,通过该函数可以获取整个屏幕或指定窗口的工作区域。其中,参数 widget 可以用于指定在哪个屏幕上获取工作区域。如果 widget 为空,则返回整个屏幕可用的工作区域。如果 widget 不为空,它会返回其所在屏幕的可用工作区域。

例如,以下代码获取了整个屏幕的可用工作区域:

QDesktopWidget *desktop = QApplication::desktop();
QRect availableRect = desktop->availableGeometry();

availableGeometry 函数返回一个 QRect 类型的对象,该对象包含了工作区域的坐标和大小。

注意,由于在某些情况下可以有多个屏幕,所以如果您想获取指定屏幕的工作区域,建议使用 QDesktopWidget::screenGeometry 函数,该函数与 availableGeometry 函数类似,但不包括系统窗口和工具栏。

QRect screenRect = desktop->screenGeometry(screenId);

其中,screenId 为要获取的屏幕的 ID。从 0 开始编号,表示主屏幕。

QPixmap的scaled函数

QPixmap scaled函数_qpixmap::scaled_Choisen的博客-CSDN博客

QPixmap::scaled(const QSize & size, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation) const

根据给定的尺寸来放大缩小QPixmap

IgnoreAspectRatio 矩形框有多大,图片就缩放成多大,不限制原图片的长宽比

KeepAspectRatio 保持原图片的长宽比,且不超过矩形框的大小

KeepAspectRatioByExpanding 根据矩形框的大小最大缩放图片

Qt::SmoothTransformation 不失真

QPixmap::scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation) const

返回匹配目标设置后的QPixmap,宽高为给定值

qobject_cast

qobject_cast用法 - findumars - 博客园 (cnblogs.com)

 qobject_cast <对象类型>( QObject * object )

本方法返回object向下的转型,如果转型不成功则返回0,如果传入的object本身就是0则返回0。

在使用时有两个限制:

1# 类型必须继承自QObject。

2# 在声明时必须有Q_OBJECT宏。

使用场景1:

当某一个Object emit一个signal的时候,它就是一个sender, 系统会记录下当前是谁emit出这个signal的,

所以你在对应的slot里就可以通过 sender()得到当前是谁invoke了你的slot。

有可能多个 Object的signal会连接到同一个signal(例如多个Button可能会connect到一个slot函数onClick()),

因此这是就 需要判断到底是哪个Object emit了这个signal,根据sender的不同来进行不同的处理.

在槽函数中:

QObject * obj = sender(); //返回发出信号的对象,用QObject类型接收

QPushButton *button_tmp = qobject_cast(obj); //向下转型为按钮类型

...对此按钮的其他操作

GraphicsView 框架

在Graphics View绘图架构中,主要涉及到下面三个类的使用:

场景类(QGraphicsScene):该类提供绘图场景(Scene),场景是不可见的,是一个抽象的管理图形项的容器,可向场景中添加图形项,获取场景中的某个图形项等,主要功能如下: 提供管理大量图元的快速接口; 传播事件到场景中的每个图元; 提供未经变换的渲染功能,主要用于打印; 管理图元状态,如图元选择和焦点处理;

视图类(QGraphicsView):该类提供绘图的视图(View)组件用于显示场景中的内容。可以为一个场景设置几个视图,用于对同一个数据集提供不同的观察方式;

图形项类(QGraphicsItem):该类提供了一些基本的图形元件,也可在此基础上自定义图形项,它支持各种事件的响应,如鼠标事件、键盘事件、拖放事件等,以实现图形的交互功能,下面列举一些标准图元:

QGraphicsEllipseItem 椭圆 QGraphicsLineItem 直线 QGraphicsPathItem 路径 QGraphicsPixmapItem 图像 QGraphicsPolygonItem 多边形 QGraphicsRectItem 矩形 QGraphicsSimpleTextItem 简单文本 QGraphicsTextItem 文本浏览 QGraphicsItem是图元的基类,用户可以继承QGraphicsItem实现自定义的图元; GraphicsView 坐标系 Graphics View基于笛卡尔坐标系。item在场景中的位置与几何形状通过x,y坐标来表示。当使用未经变形的视图来观察场景时,场景中的一个单位等于屏幕上的一个像素。在Graphics View绘图架构中涉及到了3个坐标系,即场景坐标、视图坐标及图形项坐标。

QGraphicsProxyWidget

Qt图形视图框架:QGraphicsProxyWidget详解_友善啊,朋友的博客-CSDN博客

1、QGraphicsProxyWidget 类提供了一个代理层,用于在 QGraphicsScene 中嵌入 QWidget。

2、QGraphicsProxyWidget 在两个对象之间转发事件,并在 QWidget 的基于 int 的几何坐标和 QGraphicsWidget 的基于 qreal 的几何坐标之间进行转换。

3、QGraphicsProxyWidget 支持 QWidget 的所有核心功能,包括Tab切换焦点、键盘输入、拖放和弹出窗口等。

4、QGraphicsProxyWidget 通过为每个弹出窗口创建子代理来处理嵌入式小部件的弹出窗口子项。这意味着当嵌入的 QComboBox 显示其弹出列表时,会自动创建一个新的 QGraphicsProxyWidget,嵌入弹出窗口并正确定位。

QRect类

Qt:QRect类_OceanStar的学习笔记的博客-CSDN博客

构造矩形

矩形通常表示为左上角和大小。QRect的大小(宽度和高度)总是等价于数学矩形(构成其呈现的基础)。

QRect可以用一组左、顶、宽和高整数来构造,也可以用QPoint和QSize来构造。下面的代码创建了两个相同的矩形。

QRect r1(100, 200, 11, 16);
QRect r2(QPoint(100, 200), QSize(11, 16));
QPixmap

【QT5】QPixmap的使用_&Mr.Gong的博客-CSDN博客

QPixmap主要用于绘图,针对图像显示而最佳化设计

QImage主要是为图像I/O、图像访问及像素修改而设计。但若用QPixmap加载大图片的时候,会占用很大内存,适合加载小图片;一个几十K的图片,加载进来后会放大很多倍。

若图片过大,可用QImage进行加载,然后转成QPixmap用户绘制,QPixmap绘制效果最好。

QByteArray
QString
字符串+数字打印
QString str;
str = QString("%1 was born in %2.").arg("Joy").arg(1993);     //str =  "Joy was born in 1993.";

本文参考多篇博客,已标出链接,侵删。

转载请标明出处,谢谢!

你可能感兴趣的:(Linux应用开发,qt)