合肥项目关于CTK和Qt还有C++的一些知识点

CTK与QT开发

QT

1.Qt是一个跨平台的 C++ 开发框架,主要用来开发图形用户界面(Graphical User Interface,GUI)程序,当然也可以开发不带界面的命令行(Command User Interface,CUI)程序。

2.Qt 虽然经常被当做一个 GUI 库,用来开发图形界面应用程序,但这并不是 Qt 的全部;Qt 除了可以绘制漂亮的界面(包括控件、布局、交互),还包含很多其它功能,比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等,这些 Qt 都已经内置了。

3.总的来说,Qt 主要用于桌面程序开发和嵌入式开发。

4.用 Qt 来开发 Windows 桌面程序有以下优点:

  • 简单易学:Qt 封装的很好,几行代码就可以开发出一个简单的客户端,不需要了解 Windows API。
  • 资料丰富:资料丰富能够成倍降低学习成本,否则你只能去看源码,关于 DirectUI、Htmlayout、aardio 的资料就很少。
  • 漂亮的界面:Qt 很容易做出漂亮的界面和炫酷的动画,而 MFC、WTL、wxWidgets 比较麻烦。
  • 独立安装:Qt 程序最终会编译为本地代码,不需要其他库的支撑,而 Java 要安装虚拟机,C# 要安装 .NET Framework。
  • 跨平台:如果你的程序需要运行在多个平台下,同时又希望降低开发成本,Qt 几乎是必备的。

5.Qt是开源的库

6.Qt Creator是一个集成开发环境

7.QT Creator 创建项目时提供三种基类:QMainWindow、QWidget、QDialog。

QMainWindow是带有菜单栏和工具栏的主窗口。

QWidget类是所有用户界面对象的基类,新建Qt GUI 应用时选择QWidget为基类,不仅如此,其实所有的窗口部件都继承自QWidget。

QDialog是各种对话框的基类。

8. ui 是一个指向界面类的指针,使用ui-> 用来访问这个界面类里面的控件。

9 快捷键

注释 ctrl + /

运行 ctrl + r

编译 ctrl + B

整行移动 ctrl + shift + ↑或者↓

帮助文档 F1

自动对齐 ctrl + i

创建QT程序

main 函数

QApplication a 应用程序对象,有且只有一个

myWidget w 实例化窗口对象

w.show() 调用show函数 显示窗口

return a.exec()让应用程序对象进入消息循环机制中,代码阻塞到当前行

按钮控件常用API

  • 创建QPushButton * btn = new QPushButton
  • 设置父亲 setParent(this)
  • 设置文本 setText("文字")
  • 设置位置 btn->move(200,300)
  • 重新指定窗口大小 resize(100,200)
  • 按钮定制大小 btn->resize(50,100)
  • 设置窗口标题 setWindowTitle("这是标题")
  • 设置窗口固定大小 setFixedSize(200,300)

知识点1 :: 作用域符号

简单理解:"::"在C++中表示作用域和所属关系,且该运算符的等级最高。有下面几种用法:

一、(类)作用域符号

"::"符号前面一般是类的名称,后面一般是该类的成员名称。C++为避免不同类间含有相同名称的成员而采用了这种作用域的方式进行区分。代码示例如下:

 class A{
 public:
     int member;
 };
 class B{
 public:
     int member;
 };
 A::member = 1;  //A类中的member
 B::member = 2;  //B类中的member

二、全局作用域符号

全局变量在局部函数中与某个变量重名,可以用"::"区分,代码示例如下:

 int num = 0;    //全局变量
 void b_add(s){
     int num = s;     //局部变量
     num = num + num;   //本行代码中的num全部为局部变量
    ::num = ::num + num;   //本行代码中前两个num为全局变量,后一个为局部变量
 }

三、作用域分解运算符

声明一个类并且在类里声明了一个成员函数,但没有在类的声明里给出这个成员函数的定义,那么在类外定义成员函数时就要加上"::"符号,表示成员函数属于这个类。代码示例如下:

 class CA{
 public:
    int ca_var;
    int add(int a, int b);
    int add(int a);
 };
 int CA::add(int a, int b)   //类外定义成员函数
 {
    return a + b;
 }
 int CA::add(int a)
 {
    return  a + CA::ca_var; //类外用到成员变量
 }

四、命名空间作用域符号

用来注明所使用的类、函数属于哪一个命名空间,代码示例如下:

 std::cout << "Hello,World" << std::endl;

下面再来说一下std::相关的一些东西:

1.std::是个名称空间标示符C++标准库中的函数或者是对象都是在命名空间std中定义的,当我们要使用标准函数库中的函数或对象都要使用std限定。

例如:对象cout是标准函数库所提供的对象,而标准库在名字空间中被指定为std,所以在使用cout的时候要加上std::。这样编译器就会明白我们调用的cout是名字空间std中的cout。

2.一般来说,要调用C++标准库时要写上std;使用非标准库文件iostream.h不用写

 #include<iostream>
 int main(){
     std::cout<<"我喜欢C++";
     std::cout<<std::endl;      //换行
     return 0;
 }
 ​
 #include<iostream.h>
 int main()
 {
     cout<<"我喜欢C++";
     cout<<endl;
     return 0;
 }

当然在main函数前面加上下面的代码,cout和endl前面也不需要加std::。

 #include<iostream>
 using std::cout;
 using std::endl;

还有种更简单的方法:

 #include<iostream>
 using namespace std; //告诉编辑器我们将要使用空间std中的函数或者对象

知识点2

<*.h>表示优先中系统目录中查找 该头文件 比如#include <stdio.h> 这是系统中的

"*.h"表示优先从当前目录中查找 该头文件 比如#include "head.h" 这是你自己写的

知识点3

<string.h>是旧的C 头文件,对应的是基于char*的字符串处理函数;<string>是包装了std 的C++头文件,对应的是新的string 类;<cstring>是对应于旧C头文件的std 版本。

信号和槽

connect(信号发送者,发送的信号,信号接收者,槽函数);

例如:

connect(btn,&QPushButton::click,this,&QWidget::close)

注意这里是函数地址,加&

自定义信号和槽

1.自定义信号

1.1写到signals下

1.2返回void 只需要声明 不需要实现

1.3可以有参数,可以重载

2.自定义槽函数

2.1可以写到 slots下或者public或者全局下都行

2.2返回void 需要声明 也需要实现

2.3可以有参数 可以重载

3.案例:下课了,老师饿了发送信号,学生接受信号请客吃饭。

 /////////////////////////teacher.h///////////////////////////
 class Teacher : public QObject
 {
     Q_OBJECT
 public:
     explicit Teacher(QObject *parent = nullptr);
 signals:
     //自定义信号 写到这里
     //没有返回值 只需要声明,不需要实现,可以有参数。可以重载
     void hungry();
     void hungry(QString foodName);
 public slots:
 };
 ​
 /////////////////////////student.h////////////////////////
 ​
 class Student : public QObject
 {
     Q_OBJECT
 public:
     explicit Student(QObject *parent = nullptr);
 signals:
 ​
 public slots:
     //早期Qt版本只能写到slots下,高版本可以写到public或者全局下
     //没有返回值 需要声明 也需要实现 可以有参数 可以重载
     void treat();
     void treat(QString foodName);
 };
 ///////////////////////////widget.h////////////////////////
 class Widget : public QWidget
 {
     Q_OBJECT
 ​
 public:
     Teacher * wls;
     Student * qy;
     explicit Widget(QWidget *parent = 0);
     ~Widget();
     void afterClass();
 };
 /////////////////////student.cpp/////////////需要实现
 void Student::treat(){
     qDebug()<<"请老师吃饭"<<endl;
 }
 //////////////////////widget.cpp/////////////////////
 //创建一个老师对象,再创建一个学生对象
 {
     this->wls = new Teacher;
     this->qy = new Student;
     // void (Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
     //void (Student:: *studentSlot)(QString) = &Student::treat;
     connect(wls,&Teacher::hungry,qy,&Student::treat);
     //connect(wls,teacherSignal,qy,studentSlot);
     afterClass();
 }
 void Widget::afterClass(){
     emit wls->hungry();  
 }

4.在这个案例中需要设置下课这个信号发出。方法是在Widget.cpp实现下课这么个函数,注意这里emit发送信号

 void Widget::afterClass(){
     emit wls->hungry();
 }

5.当自定义信号和槽函数出现重载

需要用函数指针 明确指向函数的地址

  //创建一个老师对象,再创建一个学生对象
     this->wls = new Teacher;
     this->qy = new Student;
     void (Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
     void (Student:: *studentSlot)(QString) = &Student::treat;
     //connect(wls,&Teacher::hungry,qy,&Student::treat);
     connect(wls,teacherSignal,qy,studentSlot);
     afterClass();

QString 输出的话是带着引号滴“ ” 转为char*

先用.ToUtf8()转为QByteArray

再用.Data()转为char*

6.信号可以连接信号

void (Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
void (Student:: *studentSlot2)(void) = &Student::treat;
connect(wls,teacherSignal2,qy,studentSlot2);
connect(btn,&QPushButton::clicked,wls,teacherSignal2);

7.断开信号用 disconnect(wls,teacherSignal2,qy,studentSlot2)

8.拓展

  • 一个信号可以连接多个槽函数
  • 多个信号可以连接同一个槽函数
  • 信号和槽函数的参数 必须类型一一匹配
  • 信号和槽函数参数个数不是必须一样多 信号的参数个数可以多于槽函数的参数个数 (例如老师想吃宫保鸡丁 并且吃两份 槽函数可以不接收第二个参数)

知识点4 函数指针

1.函数指针概念

函数指针:首先它是一个指针,一个指向函数的指针,在内存空间中存放的是函数的地址;

int Add(int x,int y)
{
return x+y;
}
int main()
{
printf("%p\n",&Add);//打印一下函数Add()的地址
printf("%p\n",Add);//数组名等于数组首元素地址,那函数名是等于函数地址吗?
return 0;
}

在这里插入图片描述

原来函数名是等于函数地址的!

2.函数指针的定义: 函数的返回值类型(*指针名)(函数的参数列表类型)

int a = 10;
int*pa = &a;//这里pa是一个指针 pa=pa的数值=a的地址 *p意思是找到某一个地址里面的数 也就是10
int Add(int x, int y)
{
return x+y;
}
int main()
{
int (*pf)(int, int) = &Add;//函数指针定义,返回值类型和参数类型与函数Add()相同
}
//创建一个老师对象,再创建一个学生对象
this->wls = new Teacher;
this->qy = new Student;
void (Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
void (Student:: *studentSlot)(QString) = &Student::treat;
//connect(wls,&Teacher::hungry,qy,&Student::treat);
connect(wls,teacherSignal,qy,studentSlot);
afterClass();
void Add(int x, int y)
{
return x+y;
}
int main()
{
int (*pf)(int,int)=&Add;
int ret=(*pf)(3,5);
}

解析: int ret=(*pf)(3,5),此时就相当于通过函数名调用: int ret=Add(3,5);},我们又知道:函数名是等于&函数名的,所以int (*pf)(int,int)=&Add,可改成:int (*pf)(int,int)=Add;此时Add等价于pf,所以:int ret=(*pf)(3,5);语句可改成:int ret=pf(3,5);等价于int ret=Add(3,5),故我们知道了对于:int ret=(*pf)(3,5);语句来说,*是没有意义的,有一个或多个或者没有都不影响;

知识点5 Lambda表达式

1 概念

在 C++ 11 和更高版本中,Lambda 表达式(通常称为 Lambda)是一种在被调用的位置或作为参数传递给函数的位置**定义匿名函数对象(闭包)**的简便方法。 Lambda 通常用于封装传递给算法或异步函数的少量代码行。

#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}

在上面的实例中std::sort函数第三个参数应该是传递一个排序规则的函数,但是这个实例中直接将排序函数的实现写在应该传递函数的位置,省去了定义排序函数的过程,对于这种不需要复用,且短小的函数,直接传递函数体可以增加代码的可读性。

2 语法定义

基本形式就是**[] () { }**

Lambda 表达式的结构元素说明。

[capture](parameters) mutable ->return-type{statement}
例如:
[](int a, int b) -> int { return a + b; };

参数介绍:

  • [capture]:捕捉列表标识一个Lambda的开始,这部分必须存在,不能省略。捕捉列表总是出现在lambda表达式的开始处。事实上,[]是lambda引出符。编译器根据该引出符判断接下来的代码是否是lambda函数。捕捉列表能够捕捉上下文中的变量供lambda函数使用。
  • (parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号()一起省略。
  • mutable:mutable修饰符。默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。
  • ->return_type:返回类型。用追踪返回类型形式声明函数的返回类型。出于方便,不需要返回值的时候也可以连同符号->一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
  • {statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。在lambda函数的定义式中,参数列表和返回类型都是可选部分,而捕捉列表和函数体都可能为空,C++中最简单的lambda函数只需要声明为:[]{};
  1. []。没有任何函数对象参数。
  2. [=]。函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。平时编程的话一般就用这个值传递
  3. [&]。函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式(相当于是编译器自动为我们按引用传递了所有局部变量)。
  4. [this]。函数体内可以使用 Lambda 所在类中的成员变量。
  5. [a]。将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 const 的,要修改传递进来的拷贝,可以添加 mutable 修饰符。
  6. [&a]。将 a 按引用进行传递。
  7. [a,&b]。将 a 按值传递,b 按引用进行传递。
  8. [=,&a,&b]。除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
  9. [&,a,b]。除 a 和 b 按值进行传递外,其他参数都按引用进行传递。
int a = 10;
QString str1 = "汉字博大精深";
connect(pBtn4, &QPushButton::clicked, [=](bool checked){
qDebug() << a <<str1;
qDebug() << checked;
qDebug() << "Hua Windows Lambda Button";
});

注意,这里connect第三个参数是this,第四个参数是Lambda表达式的话,this可以省略掉

QMainWindow 菜单栏和工具栏

#include<QMenuBar>
#include<QToolBar>
#include<QDebug>
#include<QAction>
#include<QPushButton>
//重置窗口大小
resize(600,400);
//菜单栏创建 最多只能有一个
QMenuBar *bar = menuBar();
//将菜单栏放到窗口中
setMenuBar(bar);
//创建菜单
QMenu *fileMenu = bar->addMenu("文件");
QMenu *editMenu = bar->addMenu("编辑");
//创建菜单项
QAction *newAction1=fileMenu->addAction("新建");
//添加分割线
fileMenu->addSeparator();
QAction *newAction2 = fileMenu->addAction("打开");
//工具栏 可以有多个
QToolBar *toolbar = new QToolBar(this);
addToolBar(Qt::LeftToolBarArea,toolbar); addToolBar(默认停靠区域,工具栏指针)
//后期设置 只允许左右停靠
toolbar->setAllowedAreas(Qt::LeftToolBarArea|Qt::RightToolBarArea);
//设置浮动
toolbar->setFloatable(false);
//设置移动(总开关)
toolbar->setMovable(false);
//工具栏可以设置内容
toolbar->addAction(newAction1);
toolbar->addSeparator();
toolbar->addAction(newAction2);
//将控件设置到工具栏中
QPushButton *btn = new QPushButton("open",this);
toolbar->addWidget(btn);
//设置状态栏 最多有一个
QStatusBar *statusbar = statusBar();
//添加到窗口中
setStatusBar(statusbar);
//放标签控件
QLabel *label1 = new QLabel("提示信息",this);
statusbar->addWidget(label1);
QLabel *label2 = new QLabel("右侧提示信息",this);
statusbar->addPermanentWidget(label2); //放右侧信息
//铆接部件(浮动窗口)可以有多个
QDockWidget *dock = new QDockWidget("浮动",this);
addDockWidget(Qt::BottomDockWidgetArea,dock); addDockWidget(默认停靠区域,浮动窗口指针)
//设置后期允许停靠区域,只允许上下
dock->setAllowedAreas(Qt::TopDockWidgetArea|Qt::BottomDockWidgetArea);
//设置中心部件
QTextEdit * edit = new QTextEdit(this);
setCentralWidget(edit);
这里如果是只能有一个的部件 就用set设置,可以有好几个的就用add添加进窗口

资源文件

  • 将图片文件拷贝到项目位置下
  • 右键项目 -> 添加新文件 -> Qt -> Qt resource File ->给资源文件起名
  • res 生成 res.qrc
  • open in editor 编辑资源
  • 添加前缀 添加文件
  • 使用 " : + 前缀名 + 文件名 "

对话框

模态对话框 ----- 不可以对其他窗口进行操作

非模态对话框 ----- 可以对其他窗口进行操作

//点击新建按钮,出来一个对话框
connect(newAction1,&QAction::triggered,[=](){
//模态创建 阻塞
QDialog dlg(this);
dlg.resize(200,100);
dlg.exec(); //这句话标志着创建的就是模态对话框
qDebug()<<"模态对话框弹出了"<<endl;
//非模态对话框创建
QDialog *dlg2 = new QDialog(this);
//注意这里有一个问题啊 这里new了一个对话框对象,开辟了堆内存,如果用户一直新建关闭
//重复操作 会导致出现多个对话框内存占用 但是又没有释放掉
dlg2->setAttribute(Qt::WA_DeleteOnClose);//使用这条语句进行改进
dlg2->resize(200,100);
dlg2->show(); //这句话标志着创建的就是非模态对话框
qDebug()<<"非模态对话框弹出了"<<endl;
});

标准对话框之消息对话框

QMessageBox::information(this,"info","今天天气不错");
QMessageBox::warning(this,"warning","警告警告");
QMessageBox::question(this,"问题","你好吗",QMessageBox::Cancel|QMessageBox::Save,QMessageBox::Save);
//(父亲,标题,内容,两个按钮分别是什么,默认选中的按钮)
//程序怎么知道我点的是保存还是取消呢 比较该函数返回的数值和 其中一个按钮
if(QMessageBox::Cancel ==QMessageBox::question(this,"问题","你好吗",QMessageBox::Cancel|QMessageBox::Save,QMessageBox::Save) ){
qDebug()<<"取消"<<endl;
}
else{
qDebug()<<"保存"<<endl;
}

其他标准对话框

颜色对话框 QColorDialog::getColor()

文件对话框 QFileDialog::getOpenFileName(父亲,标题,默认路径,过滤文件)

字体对话框 QFontDialog::getFont()

知识点6 调用函数时 什么时候用 A.fun() 什么时候用 A->fun()

  • 如果对象是在栈区创建 用 .

    int main(){
    类名 对象名;
    对象名.成员函数
    }
  • 如果对象在堆区创建 记住有new关键字 这里的对象是指向类对象的一个指针

    类名 * 对象 = new 类名();
    对象名->成员函数

界面布局

按钮组:

  • QPushButton 常用按钮
  • QToolButton 工具按钮 用于显示图片,也可以图和文字同时显示 修改风格 toolButtonStyle 凸起风格 autoRaise
  • radioButton 单选按钮 设置默认值 ui->rbtnMan->setChecked(true)
  • checkbox 多选按钮

列表容器 QListWidget

//利用QListWidgetItem写一行内容
QListWidgetItem *item = new QListWidgetItem("铁马冰河入梦来");
//把这一句话放入listwidget
ui->listWidget->addItem(item);
//设置居中
item->setTextAlignment(Qt::AlignHCenter);
//直接放置多行语句
//QStringList QList<QString>
QStringList list;
list<<"锄禾日当午"<<"汗滴禾下土"<<"谁知盘中餐"<<"粒粒皆辛苦";
ui->listWidget->addItems(list);

树控件 TreeWidget

     ui->treeWidget->setHeaderLabels(QStringList()<<"英雄"<<"英雄介绍");
     //设置根节点
  QTreeWidgetItem *item1 = new QTreeWidgetItem(QStringList()<<"力量");
     QTreeWidgetItem *item2 = new QTreeWidgetItem(QStringList()<<"敏捷");
     QTreeWidgetItem *item3 = new QTreeWidgetItem(QStringList()<<"智力");
  //添加根节点到树控件上
     ui->treeWidget->addTopLevelItem(item1);
     ui->treeWidget->addTopLevelItem(item2);
     ui->treeWidget->addTopLevelItem(item3);
     //在力量这一栏追加子节点
     QStringList hero1;
     hero1<<"猪八戒"<<"前排坦克,吸收第一波团战伤害";
     QTreeWidgetItem *ll = new QTreeWidgetItem(hero1);
     item1->addChild(ll);

image

表格控件 QTableWidget

Qt常用控件TableWidgethttps://blog.csdn.net/qq_38255284/article/details/116382135

     //TableWidget控件
     //设置列数
     ui->tableWidget->setColumnCount(2);
     //设置水平表头
     ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"姓名"<<"年龄");
     //设置行数
     ui->tableWidget->setRowCount(5);
     QStringList nameList;
     nameList<<"亚索"<<"刀妹"<<"青钢影"<<"卡萨丁"<<"卢锡安";
     int a[5]={200,100,150,500,6000};
     for(int i=0;i<5;i++){
         int j=0;
         ui->tableWidget->setItem(i,j++,new QTableWidgetItem(nameList[i]));
         //需要Int 转QString
         ui->tableWidget->setItem(i,j++,new QTableWidgetItem(QString::number(a[i])));
    }

image

自定义封装控件

  • 添加新文件 -Qt-设计师界面类-(.h .cpp .ui)
  • .ui设计中 放一个 QSlider控件和QSpainBox控件
  • 下面要在Widget中使用自定义控件,拖拽一个widget,点击 提升为--添加--提升,如果在全局包含打上对勾,下次还想封装另一个控件在点击提升为的时候会出来提示上次提升的类。
  • 实现功能 改变数字 滑动条跟着移动 滑动条滑动 数字也会改变
  • 提供getNum()和getNum()对外接口

鼠标事件

  • 鼠标进入事件 enterEvent

  • 鼠标离开事件leaveEvent

  • 鼠标按下 mousePressEvent(QMouseEvent ev)

  • 鼠标释放 mouseReleaseEvent

  • 鼠标移动 mouseMoveEvent

  • x坐标获取 ev->x() y坐标获取 ev->y()

  • ev->button() 可以判断所有按键 Qt::LeftButton Qt::RightButton

  • ev->buttons()判断组合按键 判断move时候的左右键 结合&操作符

  • 格式化字符串 QString("你好%1,谢谢%2").arg(111).arg(222)

    qDebug()<<QString("你好%1谢谢%2").arg("秦月").arg(222);

    输出:"你好秦月谢谢222"

知识点7 static总结

经过static修饰的变量,存储在内存的全局静态区。且被static修饰的变量只能在本模块的所有函数引用。

内存中的存储区域如下:

堆区:是由程序员手动申请(new)与释放(delete)的内存区域。从低地址向高地址申请;内存空间大、存储地址不连续,一般是链式的;速度较慢。 栈区:由编译器自动分配和释放,主要存储函数的参数值、函数内部的变量的值、函数调用的空间。从高地址向低地址申请;容量有限;速度较快;存储地址连续,会溢出。 代码区:又叫文本段(.text),存放着程序的机器代码,可执行指令就是存储在这里的,这里的代码是只读的。 全局区(静态区):全局变量和静态变量是存储在这里的。初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bbs)。系统结束后由系统释放。 常量区:常量字符串放在这里,程序结束后,由系统进行释放。

这里重点说一下静态局部变量

局部变量前添加static关键字,则该变量称为静态局部变量。

#include <iostream>
#include <stdio.h>
void Func()
{
static int a = 5;
printf("a = %d\n", a);
a++;
}
int main()
{
for (int i = 0; i < 5; i++)
{
Func(); //打印结果:5 6 7 8 9
}
system("pause");
return 0;
}

通常情况下,在一个函数作用域内定义一个变量,每次运行到该函数时,系统会给局部变量分配内存。当函数结束时,该变量的内存会被系统回收至栈内存当中。

但是静态局部变量具有以下特点:

1)内存存放在程序的全局数据区中;

2)静态局部变量在程序执行到该对象声明时,会被首次初始化。其后运行到该对象的声明时,不会再次初始化,这也是为什么上面程序测试函数每次输出的值都是递增的原因(只会被初始化一次)

3)如果静态局部变量没有被显式初始化,则其值会自动被系统初始化为0;

4)局部静态变量不能被其作用域之外的其他模块调用,其调用范围仅限于声明该变量的函数作用域当中;

定时器

class Widget : public QWidget
{
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void timerEvent(QTimerEvent *event);//定义定时器事件
int id1;//第一个定时器的唯一标识
int id2;//第二个定时器的唯一标识
};
//需求:我想设置两个Label 第一个每一秒+1 第二个每2秒+1
//这里怎么区分两个计时器要执行什么操作呢? 每个定时器都有一个唯一编号 timerId
id1 = startTimer(1000);//第一个参数是多少间隔执行一次 单位是毫秒
id2 = startTimer(2000);
void Widget::timerEvent(QTimerEvent *event){
if(event->timerId() == id1){
static int num=1;
//QString::number是将数数字(整数、浮点数、有符号、无符号等)转换为QString类型
ui->label->setText(QString::number(num++));
}
else if(event->timerId() == id2){
static int num2=1;
//QString::number是将数数字(整数、浮点数、有符号、无符号等)转换为QString类型
ui->label_2->setText(QString::number(num2++));
}
}

QString::number是将数字(整数、浮点数、有符号、无符号等)转换为QString类型

#include<QTimer>
//设置定时器第二种方式
//先利用QTimer类 new 一个定时器
QTimer *timer1 = new QTimer(this);
//定时器开始执行(每隔0.5秒)
timer1->start(500);
//定时器会每隔0.5秒发出信号 连接事件
connect(timer1,&QTimer::timeout,[=](){
static int num=1;
//QString::number是将数数字(整数、浮点数、有符号、无符号等)转换为QString类型
ui->label_3->setText(QString::number(num++));
});
//点击暂停按钮 停止定时器
connect(ui->btn_stop,&QPushButton::clicked,[=](){
timer1->stop();
});
//点击启动按钮,恢复定时器
connect(ui->btn_start,&QPushButton::clicked,[=](){
timer1->start(500);
});

事件分发器

事件过滤器

绘图事件

class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void paintEvent(QPaintEvent *event);
};
void Widget::paintEvent(QPaintEvent *event){
//实例化画家对象,this指定的是绘图设备
QPainter painter(this);
//设置画笔
QPen pen(QColor(255,0,0));
//设置画笔线的宽度
pen.setWidth(4);
//设置画笔颜色
pen.setStyle(Qt::DashDotLine);
//设置画刷
QBrush brush(Qt::green);
//使用画刷
painter.setBrush(brush);
//使用系统画笔
painter.setPen(pen);
//画线
painter.drawLine(100,100,200,200);
//画圆或者椭圆 参数1 圆心坐标 参数2 x轴上的半径 y轴上的半径
painter.drawEllipse(QPoint(100,200),100,50);
//画矩形
painter.drawRect(100,100,100,100);
//画文字
painter.drawText(QRect(50,100,200,50),"好好学习天天向上");
}

绘图高级设置

image

class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void paintEvent(QPaintEvent *event);
int posx=0;
};
////////需求1 点击按钮 移动图片 超出屏幕后重新从头开始///////////
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//需求1: 点击移动按钮 移动图片
connect(ui->pushButton,&QPushButton::clicked,[=](){
//如果要手动调用这个painterEvent事件函数 用update()
update();
});
}
void Widget::paintEvent(QPaintEvent *event){
QPainter painter(this);
posx+=5;
if(posx>this->width())
{
posx=0;
}
painter.drawPixmap(posx,20,100,100,QPixmap(":/pix.jpg"));
}
////////////需求2 设置定时器 自动移动 超出屏幕后 重新开始//////////////
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//先 new一个timer指针
QTimer *timer = new QTimer(this);
//绑定timer信号 到connect
connect(timer,&QTimer::timeout,[=](){
update();
});
//timer定时器每隔1秒发出信号
timer->start(1000);
}
void Widget::paintEvent(QPaintEvent *event){
QPainter painter(this);
posx+=20;
if(posx>this->width())
{
posx=0;
}
painter.drawPixmap(posx,20,100,100,QPixmap(":/pix.jpg"));
}

绘图设备 QPaintDevice

QPixmap QImage QBitmap(只有黑白色) QPicture QWidget

  • QPixmap 对不同的平台做了显示的优化

    • QPixmap pix(300,300) 这是一个300*300的画布
    • pix.fill(填充颜色)
    • 利用画家 QPainter painter(&pix) 往pix画布上画画
    • 保存画的图片 pix.save(“电脑路径”)
  • QImage 可以对像素进行访问并且修改

    • 使用和QPixmap差不多后面多了个参数 QImage img(300,300,QImage::Format_RGB32)
    • 其他流程和QPixmap一样
    • 可以对像素进行修改
    • QPainter painter(this); QImage img; img.load(":Image/Luffy.png"); //修改像素点 for(int i=50;i<100;i++) { for(int j=50;j<100;j++){ QRgb value=qRgb(255,0,0); img.setPixel(i,j,value); } } painter.drawImage(0,0,img);
  • QPicture 记录和重现绘图的指令

知识点8 C/C++文件路径正斜杠/和反斜杠\的使用

反单斜杠\在C或C++以及C# 中是转义前导字符,例如 \n 代表换行。为了避免歧义,路径中的 \ 必须用 \\或者/代替。

正斜杠,又称左斜杠,符号是 / ; 反斜杠,也称右斜杠,符号是 \ 。

有时我们会看到这样的路径写法,"C:\Windows\System",也就是用两个反斜杠来分隔路径,这种写法在网络应用或编程中经常看到,事实上,上面这个路径可以用"C:/Windows/System"来代替,不会出错。但是如果写成了"C:\Windows\System",那就可能会出现各种奇怪的错误了。至于上述问题出现的原因,要从字符串解析这方面来分析。学过编程的人都应该知道,在C里面,输出字符串时,如果想输出一个换行,那就要加上'\n'这个标志,类似的,输出一个TAB,就加上 '\t',也就是说,反斜杠("")这个符号会把跟在它后面的字符结合起来转义成其它字符。根据这个原理,如果想输出双引号('"'),就需要输入 "' ,这样才会将包含了双引号的字符串正确的写入内存中。那么如果想输入一个反斜杠呢?很简单,只要敲 \ 就可以了。

看到这里或许有些人已经看出眉目了,如果"C:\Windows\System"这个路径字符串交给C编译器编译,实际写入内存的字符串并没有包含反斜杠"\",甚至紧跟在反斜杠后面的字母也一起被转义成了其它的字符,再次调用的话势必会出问题。 ​ 字符串解析不仅仅局限于C编译器,Java编译器、一些配置文件的解析、Web服务器等等,都会遇到对字符串进行解析的这个问题,由于传统的Windows采用的是单个斜杠的路径分隔形式,导致在对文件路径进行解析的时候可能发生不必要的错误,所以就出现了用双反斜杠 \ 分隔路径的形式。不管解析引擎是否将反斜杠解析成转义字符,最终在内存中得到的都是 “\” ,结果也就不会出问题了。

举个例子:
"D:\QTProjects\File"
可以写成:
"D:\\QTProjects\\File"
"D:/QTProjects/File"

QFile 文件读写操作

#include "widget.h"
#include "ui_widget.h"
#include<QFileDialog>
#include<QFile>
#include<QTextCodec>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//点击选取文件按钮 弹出文件对话框
connect(ui->pushButton,&QPushButton::clicked,[=](){
QString path = QFileDialog::getOpenFileName(this,"说明文字","D:\\QTProjects\\File");
//将路径放到lineEdit中
ui->lineEdit->setText(path);
//编码格式 设置编码格式
//QTextCodec *code = QTextCodec::codecForName("utf-8");
//ui->textEdit->setText(code->toUnicode(arry));
//读取txt文件内容到textEdit中
QFile file(path); //参数就是要读取的文件路径
file.open(QIODevice::ReadOnly);
//////////////一行行读////////////////
QByteArray array;
while(!file.atEnd()){
array+=file.readLine();
}
/////////或者直接全读了/////////////////
QByteArray arry = file.readAll();
//将读到的数据放到textEdit
ui->textEdit->setText(array);
file.close();
//进行写文件
file.open(QIODevice::Append);
file.write("hello,world!");
file.close();
});
}
//QFileInfo 文件信息类
QFileInfo info(path);
qDebug()<<"文件大小(字节)"<<info.size()<<"后缀名"<<info.suffix();
qDebug()<<"文件名称"<<info.fileName();
qDebug()<<"创建日期"<<info.created().toString("yyyy/MM/dd hh:mm:ss");
qDebug()<<"最后修改日期"<<info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
});

image

知识点9 用一层for循环实现二维矩阵

重点是用 % 和 / 比如 %4 用0 1 2 3 得到的结果和 4 5 6 7 得到的结果是一样的 可以保证每一行都在同样的 x坐标位置(纵向位置)

/4 用0 1 2 3 得到的结果是0 4 5 6 7 得到的结果都是1 可以保证行跟行之间有间隔

//利用一层for循环写矩阵
for(int i=0;i<20;i++){
//创建每一个关卡按钮
MyPushButton *guanqiaBtn = new MyPushButton(":/res/LevelIcon.png");
guanqiaBtn->setParent(this);//放在当前界面
guanqiaBtn->move(33+i%4*70,130+i/4*80);
}

image

tips Qt帮助文档

路径是:Qt----5.9.1---随便点开一个----bin-----assistant.exe就是了

Qt 添加背景音乐

1.首先,pro中添加 Qt+=multimedia

2.1第一种方法

这个方法各种格式的音乐都能播放,注意路径要用/或者\\

 #include <QMediaPlayer>
 ​
 ​
 QMediaPlayer *player = new QMediaPlayer(this);//设置背景音乐
 player->setMedia(QUrl::fromLocalFile("E:/test.mp3路径名"));
 player->setVolume(50);//音量
 connect(ui->actionPlay,&QAction::triggered,[=](){
        player->play();
    });//开始播放,也可以用按钮的方式,这里用的是菜单栏中的action
 connect(ui->actionStop,&QAction::triggered,[=](){
       player->stop();
    });//停止播放
 /* 注意:
 如果不能播放,下载需要一个DirectShow解码器
 Qt 中的多媒体播放,底层是使用DirectShowPlayerService,需要一个DirectShow解码器,例如LAV Filters
 LAV Filters的下载地址如下:http://files.1f0.de/lavf/LAVFilters-0.65.exe
 将文件下载之后,安装到Qt的安装目录,即可实现MP3的播放,如果安装之后仍然无法播放,请尝试重启
 */
 ​

2.2第二种方法

这个方法 只能播放wav的文件 首先把mav的音乐当做资源添加到你的资源文件 mp3转换为mav的网站: https://online-audio-converter.com/cn/这个打开会比较慢 https://www.media.io/zh/

 #include<QSound>
 ​
 QSound *startSound = new QSound(":/res/TapButtonSound.wav",this);
 startSound->play();
 startsound->setLoops(-1);//循环次数,-1代表一致循环

还有如果就是qrc 资源文件里面不能放太大的图片或者什么音频文件 如果资源文件qrc过大,超出分配的内存范围,编译的时候打开任务管理器,会发现内存飙升。就会编译的时候报错 out of memory allocating 1073745919 bytes

删掉那个大文件就行了。

知识点 10 虚函数

虚函数,是指被virtual关键字修饰的成员函数。

在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};

虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

例子:

#include<iostream>
using namespace std;
class A
{
public:
void print()
{
cout<<"This is A"<<endl;
}
};
class B : public A
{
public:
void print()
{
cout<<"This is B"<<endl;
}
};
int main()
{
//为了在以后便于区分,我这段main()代码叫做main1
A a;
B b;
a.print();
b.print();
return 0;
}

输出结果分别是“This is A”、“This is B”。

通过class A和class B的print()这个接口,可以看出这两个class因个体的差异而采用了不同的策略,但这并不是多态性行为(使用的是不同类型的指针),没有用到虚函数的功能。现在把main()处的代码改一改。

int main()
{
//main2
A a;
B b;
A *p1 = &a;
A *p2 = &b;//注意这里是 也是A
p1->print();
p2->print();
return 0;
}

运行一下看看结果,结果却是两个This is A。

问题来了,p2明明指向的是class B的对象但却是调用的class A的print()函数,这不是我们所期望的结果,那么解决这个问题就需要用到虚函数。

#include<iostream>
using namespace std;
class A
{
public:
virtual void print(){cout<<"This is A"<<endl;}
};
class B : public A
{
public:
virtual void print(){cout<<"This is B"<<endl;}
};
int main()
{
//为了在以后便于区分,我这段main()代码叫做main1
A a;
B b;
A *p1 = &a;
A *p2 = &b; //指向派生类的基类指针
p1->print();
p2->print();
return 0;
}

毫无疑问,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了吗?回答是Yes,我们只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,class B的print()也成了虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了(语法上可加可不加,不加的话编译器会自动加上,但为了阅读方便和规范性,建议加上)。

现在重新运行main2的代码,这样输出的结果就是This is A和This is B了。 现在来消化一下,我作个简单的总结,指向派生类的基类指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。

知识点11 QStringList

QStringList list;
list << "Bill Murray" << "John Doe" << "Bill Clinton";

合肥项目知识点记录

1.

 ​
 对于一个tableView ui->tableView->xxxxx下面这些方法
 //取消全部选中
 ​
 clearSelection ()
 ​
 //全部选中
 ​
 selectAll()
 ​
 //选中行 注意:当第i行处于选中状态时,执行此方法会使i行取消选中
 ​
 selectRow(i)

2.error: C1083: 无法打开包括文件: “QSqlDatabase”: No such file or directory

此类错误的原因及相对应解决方法:

  • 头文件是不是拼错了,可以通过帮助文档进行检验
  • model没有加,比如如果想要用网络通信相关的头文件时需要勾选或添加network’ 打开pro文件——>Qt +=network

在这里插入图片描述

  • 当进行Qt += network以后没有qmake执行(这就是我错误的原因) 右键项目——>执行qmake (当时就是用这个解决的)

在这里插入图片描述

  • 还有缓存,将项目清空以后重新build

3. 遇到这个问题,先无脑右键项目 qmake一下

image

4.

             //获取选中行的内容
             QItemSelectionModel *selections = ui->ufc_tableView->selectionModel();
             QModelIndexList selected = selections->selectedIndexes();
             foreach (QModelIndex index, selected) {
                 qDebug() << index.row() << index.column() << index.data();
            }
        });

5. Mysql 创建数据库字符集与排序规则

一、utf8utf8mb4 区别

新版本数据库默认编码格式是 utf8mb4,utf8mb4 比 utf8 多了 emoji 编码支持,建议普通表使用 utf8 如果这个表需要支持 emoji 就使用 utf8mb4,也可以全部用 utf8mb4,utf8mb4 完全向下兼容 utf8。

二、字符集和排序规则

字符集

当数据库需要适应不同的语言就需要有不同的字符集,如果不指定字符集的话,那么就会使用数据库的默认的字符集,每种字符集都有自己默认的排序规则。

mysql 默认字符集为 utf8 时,默认排序规则为 utf8_general_ci

mysql 默认字符集为 utf8mb4 时,默认排序规则为 utf8mb4_general_ci

排序规则

每一种字符集都会有自己的排序规则(collation),排序规则后缀一般分为 cs、ci:

cs: case sensitive 翻译过来就是对大小写敏感

ci: case insensitive 翻译过来就是对大小写不敏感

在不确定大小写的情况,通常选择 ci,要求不敏感,可以选择的范围多一点。

6.Qt 之 qDebug()打印和QString中文乱码

1.关闭自动插入空格

QDebug &QDebug::nospace()

qDebug() << "Hello" << "world!";
qDebug().nospace() << "Hello" << "world!";
输出:
Hello world!
Helloworld!

2.关闭引号字符,转义字符

禁用在 QChar,QString 和 QByteArray内容周围自动插入引号字符。当开启引号字符禁用时,这些类型的打印将不带引号字符,也不会转义不可打印的字符。

QDebug &QDebug::noquote()

qDebug() << QString("Hello world!");qDebug().noquote() << QString("Hello world!");
输出:
"Hello world!"
Hello world!

3.乱码总结

总体来说,解决方法就一个:你必须知道源码的编码和执行的编码。

执行的编码我们也可以这样修改:

#pragma execution_character_set("utf-8")

QT的QString默认情况下只能正确显示UTF-8编码的字符串,因此,我们必须保证,要显示的字符串首先要转换为UTF-8,再进行显示。当然,我们也可以用代码人为的设定QString的编码。

然而,默认情况下,QT的编辑器是GB2312编码,QString是UTF-8编码,这时就会乱码,可以使用下文的方法一,把编码转为QString支持的编码

最简洁的方法就是:把源码改为UTF-8编码,这样不用做任何转换,直接就能正确显示,例如:

QString str="这是汉字";
ui->textEdit->append(str);

如果源码为GB2312,QString默认为为UTF-8,这时,要这样写才不会乱码:

QString str= QString::fromLocal8Bit("这是汉字");
ui->textEdit->append(str);

这个例子是把本地编码(我们的操作系统得编码大多为GB2312),转换为QString支持的编码。

扩展

BOM:byte order mark,定义字节顺序,因为网络传输中分为两种,大头和小头。uft-8不需要bom表明字节顺序,但可以用BOM来表示编码方式,windows就是采用bom来标记文本文件的编码方式的。

bom是为utf-16和utf-32准备的,用于标记字节顺序。微软在utf-8中使用bom是因为这样可以把UTF-8和ASCII等编码区分开来,但这样的文件在windows之外的操作系统里会带来问题。

7.关于信号和信号槽函数更多知识

1 就QDialog模式对话框传递数据给主窗口这种效果,想实现这样的信息传递逻辑

2.下面的代码展示以这个项目功能为例

点击新建窗口的确定按钮,信息提交到数据库,并且主页面刷新展示最新的信息
image

//newscencedialog.h
#include <QDialog>
#include<scene.h>
namespace Ui {
class NewScenceDialog;
}
class NewScenceDialog : public QDialog
{
Q_OBJECT
public:
explicit NewScenceDialog(QWidget *parent = nullptr);
~NewScenceDialog();
signals:
void ok();//新建一条信息 点击确定按钮 无需实现
public slots:
private:
Ui::NewScenceDialog *ui;
};
#endif // !__NEW_SCENCEDIALOG
//newscencedialog.cpp
#include "newscencedialog.h"
#include "ui_NewScenceDialog.h"
#pragma execution_character_set("utf-8")
NewScenceDialog::NewScenceDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NewScenceDialog)
{
ui->setupUi(this);
Scene *scene = new Scene();
connect(ui->sceneId,&QLineEdit::textChanged,[=](){//绑定场景id
scene->sceneId=ui->sceneId->text().toInt();
});
connect(ui->sceneName,&QLineEdit::textChanged,[=](){//绑定场景名称
scene->sceneName=ui->sceneName->text();
//qDebug()<<scene->sceneName;
});
// connect(ui->sceneSeaArea,&QTextBrowser::textChanged,[=](){//绑定场景海域
// scene->sceneSeaArea="{[120,30],[120,44]}";
// });
scene->sceneSeaArea="{[120,30],[120,44]}";
connect(ui->rehearsalRoundTime,&QLineEdit::textChanged,[=](){//绑定演练回合时间
scene->rehearsalRoundTime=ui->rehearsalRoundTime->text().toInt();
});
connect(ui->roundOperationTime,&QLineEdit::textChanged,[=](){//绑定回合操作时间
scene->roundOperationTime=ui->roundOperationTime->text().toInt();
});
scene->sceneNotes="空中作战";
///////////////重点是这一块////////////////////////
connect(ui->submit,&QToolButton::clicked,[=](){
scene->AddOneSceneData(scene);
this->close();
emit ok();//同时发ok信号 给主窗口信息
});
}
//sencemgr.h
#ifndef SENCEMGR_H
#define SENCEMGR_H
#include<scene.h>
#include <QWidget>
#include <QSqlDatabase>
#include <QSqlError>
#include<QSqlQuery>
#include"newscencedialog.h" //包含新建窗口那个.h 不能新建窗口那个.h也同时包含这个 ->会出错
namespace Ui {
class SenceMgr;
}
class SenceMgr : public QWidget
{
Q_OBJECT
public:
explicit SenceMgr(QWidget *parent = 0);
~SenceMgr();
void new_scence_slot();
Scene *scene=NULL;
NewScenceDialog * pNSD=NULL;
public slots:
void refreshSceneData();
private:
Ui::SenceMgr *ui;
};
#endif // SENCEMGR_H
//sencemgr.cpp
#include "sencemgr.h"
#include "ui_sencemgr.h"
#include<QDebug>
#pragma execution_character_set("utf-8")
SenceMgr::SenceMgr(QWidget *parent) :
QWidget(parent),
ui(new Ui::SenceMgr)
{
ui->setupUi(this);
Scene *scene = new Scene;
scene->dataBaseInit();//数据库初始化
refreshSceneData();
connect(ui->btn_new_sence,&QPushButton::clicked,this,&SenceMgr::new_scence_slot);
}
void SenceMgr::new_scence_slot()
{
//在这里创建一个新建窗口对象
this->pNSD = new NewScenceDialog;
//绑定 前面新建窗口类 有个ok信号 收到信号 调用刷新窗口函数
/*这里要注意 如果想用SIGNAL() SLOT() 这种形式,必须前面.h文件 写到对应的signals: 和public slots: 底下。如果不这样,就随意*/
connect(this->pNSD,SIGNAL(ok()),this,SLOT(refreshSceneData())); //第一种写法
connect(this->pNSD,&NewScenceDialog::ok,this,&SenceMgr::refreshSceneData);//第二种写法
pNSD->exec();
}
void SenceMgr::refreshSceneData(){
Scene *scene = new Scene;
QList<Scene*> SceneList;//存放查出来的场景对象信息
SceneList=scene->getSceneData();//从数据库查询全部场景对象信息
ui->tableWidget->setRowCount(SceneList.count());//设置表格行数
for(int i=0;i<SceneList.count();i++)
{
ui->tableWidget->setItem(i,0,new QTableWidgetItem(QString::number(SceneList.at(i)->sceneId))); //设置第i行第0列单元格内容
ui->tableWidget->setItem(i,1,new QTableWidgetItem(SceneList.at(i)->sceneName)); //设置第i行第1列单元格内容
ui->tableWidget->setItem(i,2,new QTableWidgetItem(SceneList.at(i)->sceneSeaArea)); //设置第i行第2列单元格内容
ui->tableWidget->setItem(i,3,new QTableWidgetItem(QString::number(SceneList.at(i)->rehearsalRoundTime))); //设置第i行第3列单元格内容
ui->tableWidget->setItem(i,4,new QTableWidgetItem(QString::number(SceneList.at(i)->roundOperationTime))); //设置第i行第4列单元格内容
ui->tableWidget->setItem(i,5,new QTableWidgetItem(SceneList.at(i)->sceneNotes)); //设置第i行第5列单元格内容
}
}

8.QList的知识

QList<Scene*> Scene::getSceneData(int id)//获取全部场景数据
{
QList<Scene*> SceneList;
QString sql;
QSqlQuery query;//创建QSqlQuery对象
if(id==-1)
{
sql ="select * from scene";
}
else
{
sql =QString("select * from scene where scene_id=%1").arg(id);
}
query.exec(sql);
while(query.next())
{
Scene *scene_one = new Scene();//这里一定要在while里面不断的new 一个新的对象
scene_one->setData(query.value(0).toInt(),query.value(1).toString(),query.value(2).toString(),query.value(3).toInt().....);
SceneList.append(scene_one);
}
return SceneList;
}

遍历:

https://blog.csdn.net/txwtech/article/details/126943226

https://blog.csdn.net/qq_43680827/article/details/123433763

https://blog.csdn.net/weixin_41937297/article/details/121980147

https://blog.csdn.net/txwtech/article/details/126943226

https://blog.csdn.net/Newmamahaha/article/details/123277360

9.Qt网络通信知识

9.1 QByteArray和QString区别

Qt中对字符处理已经有QString,为什么还要引出QByteArray,因为QString中一个字符占两个字节,而传统的标准C/C++中Char/String都是一个字节为单位,而Qt库中是无缝兼容标准C/C++语法的,而在实际使用中,多机通信、交换数据等等都是使用QByteArray,这样才能达到跨平台,跨语言,跨设备,特别是嵌入式设备,大多采用C语言编程,自然是标准C库,Qt想与之通信就必须使用QByteArray,可以省去诸多转换麻烦。

如果要做字符串方面的处理,都应当使用 QString ,除非是嵌入式系统里面内存有限,不得不用 QByteArray 的情况。对于本地化编码的字符串,可以用 QString::fromLocal8Bit 函数将字符串源转为 QString 对象;对于 UTF-8 编码的字符串,可以用 QString::fromUtf8 函数实现转换。如果要反过来转换,就 用对应的 to*** 函数。通常情况下有这些函数就够用了。

对于文件读写和网络数据收发,这些一般都是用 QByteArray 对象作为输入输出缓冲区,并且可以利用 QDataStream 做串行化,将多个数据打包成 QByteArray 。通常情况下,对于纯字节数据的处理使用 QByteArray 。

9.2 Qt Http通信和JSON数据获取与解析

QT生成与解析JSON数据,包含JSON数组 https://blog.csdn.net/rong11417/article/details/104252927

Qt的Http通信 http://www.360doc.com/content/18/0227/21/46636353_732989852.shtml

第一步 在.pro文件中添加 QT += network

image

第二步 以通过WebAPI请求,进行想定场景列表获取为例子
 //assumescenesetting.h
 ​
 ​
 //////加入这三个头文件/////////////////
 /*该类用于协调网络操作,每当一个请求创建后,该类用来调度它,并发送信号来报告进度*/
 #include<QtNetwork/QNetworkAccessManager>  
 /*网络请求的应答,它会在请求被完成调度时由QNetworkAccessManager创建*/
 #include<QtNetwork/QNetworkReply>
 /*该类用于网络请求,作为请求相关的所有信息的容器*/
 #include<QtNetwork/QNetworkRequest>
 #include <QWidget>
 namespace Ui {
 class AssumeSceneSetting;
 }
 ​
 class AssumeSceneSetting : public QWidget
 {
     Q_OBJECT
 ​
 public:
     explicit AssumeSceneSetting(QWidget *parent = 0);
     ~AssumeSceneSetting();
     void getAssumeFileList();
 ​
 private slots:
     void finishedSlot(QNetworkReply *reply);
 private:
     Ui::AssumeSceneSetting *ui;
     QNetworkAccessManager *manager;
 };
 ​
 #endif // ASSUMESCENESETTING_H
 ​
 ​
 ​
 ​
 //assumescenesetting.cpp
 ​
 #include "assumescenesetting.h"
 #include "ui_assumescenesetting.h"
 #include<QDebug>
 AssumeSceneSetting::AssumeSceneSetting(QWidget *parent) :
     QWidget(parent),
     ui(new Ui::AssumeSceneSetting)
 {
     ui->setupUi(this);
     this->setWindowTitle("加载想定文件");
     ui->AssumeSceneFileList->setAlternatingRowColors(true);
 ​
     connect(ui->AssumeSceneFileList,&QListWidget::itemClicked,[=](){
         int id=ui->AssumeSceneFileList->currentRow();
         qDebug()<<"您选择加载"+ui->AssumeSceneFileList->currentItem()->text()+"文件";
    });
     manager = new QNetworkAccessManager(this);//新建一个网络协调
     /*网络请求完成后的信号处理*/
     connect(manager,&QNetworkAccessManager::finished,this,&AssumeSceneSetting::finishedSlot);
     //执行网络请求函数
     this->getAssumeFileList();
 }
 //点击按钮 获取想定文件列表
 void AssumeSceneSetting::getAssumeFileList()
 {
     QNetworkRequest request;//新建Request对象
     QString m_RequestHeader = "http://192.168.43.95:7777/hepy/api/file/list";
     request.setUrl(QUrl(m_RequestHeader));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
     //获取对应的参数数据
     QByteArray data;
     manager->post(request,data);
 }
 void AssumeSceneSetting::finishedSlot(QNetworkReply *reply){
     //reply->readAll()就是QByteArray类型 用QString(reply->readAll())可以转化成QString显示
     //qDebug()<<QString(reply->readAll());
     QJsonParseError jsonError;
     //fromJson 意思是里面的是Json数据 我要转化为JSON文档
     QJsonDocument doucment = QJsonDocument::fromJson(reply->readAll(), &jsonError);  // 转化为 JSON 文档
     if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError))        // 解析未发生错误
    {
         if (doucment.isArray()) // 如果JSON文档为数组
        {
             QJsonArray array = doucment.array();  // 转化为数组
             int nSize = array.size();  // 获取数组大小
             for (int i = 0; i < nSize; ++i)  // 遍历数组
            {
                 QJsonValue value = array.at(i);
                 if (value.type() == QJsonValue::String) //如果是String类型的
                {
                     QString strName = value.toString();
                     ui->AssumeSceneFileList->addItem(strName);//填入QListWidget
                }
            }
        }
    }
     reply->deleteLater();//用完之后删除该网络请求
 }
第三步 执行界面如下

image

10.事件循环

 事件循环首先是一个无限“循环”,程序在exec()里面无限循环,能让跟在exec()后面的代码得不到运行机会,直至程序从exec()跳出。从exec()跳出时,事件循环即被终止。QEventLoop::quit()能够终止事件循环。
 ​
 //开启事件循环,直到请求完成
     QEventLoop loop;
     connect(reply,&QNetworkReply::finished,&loop,&QEventLoop::quit);
     loop.exec();

image

11. Qt关闭窗口自动销毁对象

注意:这里要在点击事件的这个函数里面写新建下一个窗口对象 并且绑定返回按钮事件

 void AssumeSceneSetting::on_pushButton_startAction_clicked()
 {
     this->pc = new PerformerConfig;//创建推演方配置页面对象
     this->pc->setAttribute(Qt::WA_DeleteOnClose,true);//第一种方法:设置属性值,窗口在关闭时自动销毁对象,执行析构函数。
     connect(this->pc,&PerformerConfig::performerConfigPageBack,[=](){
         this->setGeometry(this->pc->geometry());
         this->pc->close();
         //delete this->pc;//第二种方法:手动delete窗口对象
         this->show();
    });
     this->pc->setGeometry(this->geometry());
     this->pc->show();
     this->hide();
 }

12. 复选框checkBox加入QButtonGroup中 统一管理

12.1 给QButtonGroup添加按钮

 // 创建按钮组对象
 QButtonGroup* btnGroup = new QButtonGroup(this);
 ​
 // 为按钮组添加三个按钮,并设置id(id可以不设置)
 btnGroup->addButton(ui->radioButton, 0);
 btnGroup->addButton(ui->radioButton_1, 1);//也可以换成chebox
 btnGroup->addButton(ui->radioButton_2, 2);
 //this->btnGroup->addButton(checkBox,checkBoxId);

如果不设置id,函数默认传入的值为-1,此时函数为自动为按钮设置id,自动设置的id为负数且从-2开始。

12.2 给按钮单独设置id

 btnGroup->setId(ui->radioButton, 0);

12.3 按钮组中按钮的互斥状态

 // 获取按钮组中按钮的互斥状态
 btnGroup->exclusive();
 // 设置按钮组中按钮的互斥状态
 btnGroup->setExclusive(false);

如不设置,获取按钮组中按钮的互斥状态默认为true,即同组中所有按钮互斥。

12.4 获取组内所有按钮

 QList<QAbstractButton*> btnList = btnGroup->buttons();

12.5 获取按钮的id

 //获取按钮的id,调用id函数,来获取。如果需要重新给按钮设置一个id,调用setId,来给按钮设置一个新的id。两个函数的原型分别为:
 int QButtonGroup::id(QAbstractButton *button) const
 void QButtonGroup::setId(QAbstractButton *button, int id)

12.6 获取当前按下的按钮

 //调用checkButton来获取按钮组容器中,选中的按钮,如果没有按钮选中,则返回nullptr。调用checkedId同时也可以获取当前选中的按钮的id。
 int QButtonGroup::checkedId() const
 QAbstractButton *QButtonGroup::checkedButton() const

12.7 信号和槽函数->同时绑定按钮组所有的复选框

      connect(this->btnGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked),[=](QAbstractButton *button){
          qDebug()<<"你选择的是第"<<this->btnGroup->checkedId()+1<<"行";
          qDebug()<<"选择推演角色为:"<<ui->tableWidget->item(this->btnGroup->checkedId(),0)->text();
      });

13.Qt全局变量的设置---多个页面(多个cpp)都可以修改和访问这个变量,使用static关键字

13.1在一个公用类里面的.h文件中的public底下声明一个static变量

image

13.2 随便找一个cpp文件 记住要包含tool.h,然后在所有函数外面 就是#include下边这样赋值

image

13.3 现在就可以在任何地方去用Tool::user_id这样去修改和输出这个全局变量了

image

14.复选框状态改变信号函数

image

connect(ui->checkBox, SIGNAL(stateChanged(int)), SLOT(slot_checkStateChanged(int))); //槽函数可以接收到这个值 进行判断现在是什么状态
void CRegulationTotalWidget::slot_checkStateChanged(int status)
{
bool isChecked = false;
if (status == 2)
{
isChecked = true;
}
}
//这里 0代表没有被选中,2代表被选中

15.Qt Sender()用法详解

相关博客链接:https://blog.csdn.net/yao_hou/article/details/104573507
image
 如果在由信号激活的插槽中调用该函数,返回指向发送信号的对象的指针,否则返回0,该指针仅在从该对象的线程上下文调用此函数的槽执行期间有效。
image

image

image

image

posted @   Qin_Yue  阅读(354)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示