C++ Qt 知识总结

  1. 面向过程的static:
    1. 函数中的静态变量 :当变量声明为static时,空间将在程序的生命周期内分配,其被存放在在全局数据区。即使多次调用该函数,静态变量的空间也只分配一次
    2. 静态区(全局区):静态变量和全局变量的存储区域是一起的,一旦静态区的内存被分配, 静态区的内存直到程序全部结束之后才会被释放。
#include <iostream> 
#include <string> 
using namespace std; 

void demo() 
{ 
    static int count = 0;  // 静态变量 ,调用demo函数时才会构造count变量,main函数结束时才析构
    cout << count << " "; 
    count++; 
} 

int main() 
{ 
    for (int i=0; i<5; ++i)  
        demo(); 
    return 0; 
}

// 输出:0 1 2 3 4
  1. 面向对象的static:
    1. 类中的静态变量 声明为static的变量只被初始化一次,因为它们在单独的静态存储中分配了空间,因此类中的静态变量由对象共享,可以减小对象的大小。对于不同的对象,不能有相同静态变量的多个副本。也是因为这个原因,静态变量不能使用构造函数初始化
    2. 类中的静态成员变量必须在类内声明,在类外定义(被const修饰的除外)
    3. 静态类 :和变量一样,静态类的生命周期直到程序的结束。在main结束后才会调用静态类的析构函数。
class Apple 
{ 
public: 
    static int i;  // 类内声明

    Apple() 
    { 
        // Do nothing 
    }; 
}; 

int Apple::i = 1;  // 类内定义
class Apple 
{ 
public: 
    // 被const修饰的static变量在c++11中可以在类内被直接初始化。
    static const int i=10; 
    Apple() 
    { 
        // Do nothing 
    }; 
};
  1. C++11 condition_variable使用方法
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <chrono>             // std::chrono::seconds
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status
std::condition_variable cv;
int value;
void do_read_value()
{
    std::cin >> value;
    cv.notify_one(); //只有键盘敲入一个字符,按下enter,才往下执行cv.notify_one();
}

int main ()
{
    std::cout << "Please, enter an integer (I'll be printing dots): \n";
    std::thread th(do_read_value);
    std::mutex mtx;
    std::unique_lock<std::mutex> lck(mtx);    		// 加锁互斥量,若lck构造时不传入mtx,会崩溃
	cv.wait_for(lck,std::chrono::seconds(1))// 当超时1s的时候,相当于收到了通知信号,就被唤醒干活了
    std::cout << "You entered: " << value << '\n';
    th.join();
    return 0;
}
  1. Qt中条件等待、异步转同步方式整理
#include <QThread>
#include <QDebug>
#include <QJsonObject>
#include <QTime>
#include <QTimer>
#include <QEventLoop>
#include <QMutex>
#include <QWaitCondition>
 
// Qt中条件等待、异步转同步方式整理
class ThreadTest : public QThread
{
    Q_OBJECT
public:
    explicit ThreadTest(QObject *parent = nullptr) : QThread(parent){}
    void run() override
    {
        m_stop = false;
        qDebug()<<"void run() override start";
        // 1. QTime
        m_time.restart();
        while ( !m_stop && m_time.elapsed() < m_waitInterval )
        {
            msleep(20);
        }
 
        // 2. QEventLoop
        m_loop = new QEventLoop;
        QTimer::singleShot(m_waitInterval, m_loop, SLOT(quit()));
        m_loop->exec();
 
        // 3. QWaitCondition
        m_mutex.lock();
        m_condition.wait(&m_mutex, m_waitInterval);
        m_mutex.unlock();
 
        qDebug()<<"void run() override end";
    }
 
    Q_INVOKABLE void stop()
    {
        m_stop = true;
        m_loop->quit();
        m_condition.wakeAll();
    }
 
private:
    bool m_stop;
    const int m_waitInterval = 5000;
    QTime m_time;
    QEventLoop *m_loop;
    QMutex m_mutex;
    QWaitCondition m_condition;
};
  1. Qml中组件构造顺序,析构顺序,发出Component.onCompleted、Component.onDestruction顺序
Window {
    width: 400
    height: 400
    visible: true
    Component.onCompleted: console.log("Component.onCompleted =========0")
    Component.onDestruction: console.log("Component.onDestruction =====================0")

    // 这里QYSimpleImage每次执行构造函数,都会将m_count + 1
    QYSimpleImage {
        Component.onCompleted: console.log("Component.onCompleted =========1")
        Component.onDestruction: console.log("Component.onDestruction =====================1")
        anchors.fill: parent
        QYSimpleImage{
            Component.onCompleted: console.log("Component.onCompleted =========2")
            Component.onDestruction: console.log("Component.onDestruction =====================2")
            anchors.fill: parent
            QYSimpleImage{
                Component.onCompleted: console.log("Component.onCompleted =========3")
                Component.onDestruction: console.log("Component.onDestruction =====================3")
                anchors.fill: parent
                QYSimpleImage{
                    Component.onCompleted: console.log("Component.onCompleted =========4")
                    Component.onDestruction: console.log("Component.onDestruction =====================4")
                    anchors.fill: parent
				}
			}
		}
	}
}
// 打印信息如下:
QYSimpleImage::QYSimpleImage m_count: 1
QYSimpleImage::QYSimpleImage m_count: 2
QYSimpleImage::QYSimpleImage m_count: 3
QYSimpleImage::QYSimpleImage m_count: 4
qml: Component.onCompleted =========0
qml: Component.onCompleted =========1
qml: Component.onCompleted =========2
qml: Component.onCompleted =========3
qml: Component.onCompleted =========4
qml: Component.onDestruction =====================4
qml: Component.onDestruction =====================3
qml: Component.onDestruction =====================2
qml: Component.onDestruction =====================1
qml: Component.onDestruction =====================0

// 组件析构顺序先内层,再外层(先子再父)
// Component.onCompleted信号发出顺序:先外层,再内层(先父再子)
// Component.onDestruction信号发出顺序:先内层,再外层(先子再父)
// qml父组件发送Component.onCompleted信号时,其子组件已经完成可写属性的赋值操作了
  1. QObject中enable属性会进行值的传递,若父类的enable为true,则子类也为true,若父类为false,子类也为false
  2. MouseArea事件传播特性:
MouseArea{	
	propagateComposedEvents: true //表示允许事件往下传递 
	hoverEnabled: true		// 若其他的组件也具有hover等效果,设置为true且执行onEntered onExited,事件就不会向下传播
	onEntered: {}
	onExited: {}
	onClicked: {
		// Do not handle this event, pass it to the next layer,如果为true,则不可以把事件传播到下一层,很有用
		mouse.accepted = false  
	}
}
MouseArea {
    anchors.fill: parent
    acceptedButtons: Qt.AllButtons
    propagateComposedEvents: true
    onPressed: {
        console.log("onPressed")
        mouse.accepted = false
    }
    onReleased: {
        console.log("onReleased")
        mouse.accepted = false
    }
    onClicked: {
        console.log("onClicked")
        mouse.accepted = false
    }
}

在propagateComposedEvents属性设置为true时:

  1. 若pressed的mouse.accepted = false,在点击事件后只有onpress会生效
  2. 若要让所有的事件都可以从上面穿透,将onPressed、onEntered等的mouse.accepted设置为false
  3. 正常点击事件顺序 : pressed->released->clicked,其中 clicked是组合事件
  1. 在使用tabview的qml组件时,想获得它的rows和columns,有两个方法:
    1. 使用C++定义的rowCount和columnCount,但是在刷新界面的时候老是会出现刷新一个item时宽高等信息不对称,暂时也不知道原理。隐藏着较多bug
    2. 用tableview内的rows和columns,就不会出现刷新某一个item时宽高等信息不对应的问题
  2. C++ new一个任意指针,delete不会有问题,但是如果p++之后,再次delete就会出错。若初始时将某个指针设置为nullptr,则可以无限delete而不会出错
  3. C 语言strcat函数:

char *strcat(char *dest, const char *src),两个指针必须使用\0作为结束符才可以正常拼接,包括平时使用char指针或者数组的时候都需要注意以\0作为结束符,否则在调用一些函数时无法得到正常的处理。

  1. C语言spintf函数:

int sprintf(char *str, char * format [, argument, …]);
如sprintf(rpn + strlen(rpn), " %s", number.c_str()),将number拼接至rpn+strlen(rpn)的位置,以空格作为间隔

  1. QDialog 与主页面无关 -> 先new类,再this->show();与主页面相关,-> 先new,this->exec(),delete this
  2. QTableWidget设置表头不可点击,ui->regionTable->horizontalHeader()->setSectionsClickable(false)
  3. QTableWidget currentcellchanged信号对于翻动QtableWidget很有用
  4. cv::Mat==操作符:
// 不相等为0,相等为255(nan与nan不相等)0显示为黑色,255显示为白色,
// temp == temp是指每一个单元格的值进行相等比较,相等为true,否则为false
cv::Mat tmpMask = (temp == temp);
  1. cv::Mat::setTo
//若tmpMask某个位置与temp的这个位置值相等,则设置为240,否则设置不成功,
// 把矩阵mask中元素不为0的点全部变为value值;
tmpMask.setTo(240,tmpMask==temp);
  1. 重写鼠标移动事件
    1. QGraphicsView::mouseMoveEvent:获取的坐标为物理坐标
    2. QGraphicsScene::mouseMoveEvent:获取的坐标为场景坐标
  2. git拉取失败解决方案:
    1. 方案一:
      1. git stash
      2. git pull
      3. git stash pop
  3. Qt 打包软件:
    1. 打包QWidget软件:cd /d D:\release windeployqt “xxx.exe”
    2. 打包QWidget的dll:cd /d D:\release windeployqt xxx.dll
    3. 打包含有QtQuick模块的软件:cd /d D:\release windeployqt xxx.exe --qmldir D:\Qt5.14.2\5.14.2\msvc2017_64\qml
  4. QWidget中或QtQuick中函数内调用对话框:

qml和C++内都可以使用Q_INVOLKABLE static void showMessageBox(int type, QString info);
显示QMessageBox时会阻塞,不会执行接下来的函数,直到关闭

  1. QtQuick中的Z值问题,可以将文本的z改为1或者0查看效果
Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
	Text {
        id: name
        z: 1
        anchors.centerIn: parent
        text: qsTr("text")
    }

    Rectangle {
        anchors.fill: parent
        color: "gray"
        anchors.margins: 20
        border.width: 2
        border.color: "green"
	}
}
  1. QtQuick中声明变量可获取某个item的指针:
ChartView {
      title: "Line"
      anchors.fill: parent
      antialiasing: true

      LineSeries {
          id: lineseries
          name: "LineSeries"
          XYPoint { x: 0; y: 0 }
          XYPoint { x: 1.1; y: 2.1 }
          XYPoint { x: 1.9; y: 3.3 }
          XYPoint { x: 2.1; y: 2.1 }
          XYPoint { x: 2.9; y: 4.9 }
          XYPoint { x: 3.4; y: 3.0 }
          XYPoint { x: 4.1; y: 3.3 }
      }
  }

Component.onCompleted: {
    //声明的变量可以代指id,并调用此ID的方法属性等等
    var testId = lineseries;
}
  1. C++包装的qml组件

如qml中的LineSeries其实质上是一个QLineSeries的实例,QLineSeries部分帮助文档如下:
LineSeries QML Type
Presents data in line charts. More…

Import Statement:
import QtCharts 2.3
Instantiates:
QLineSeries

在qml中的LineSeries的replace仅支持一个函数,但是直接将这个id传给C++,实质上将是将id转换为一个QLineSeries的指针,在C++里面的QLineSeries有很多个replace函数的重载

  1. AbstractSeries createSeries(enumeration type, string name, AbstractAxis axisX, AbstractAxis axisY):

此函数的返回值在帮助文档里是AbstractSeries类型,但返回的其实是一个指针(有可能确实是类,是var赋值时直接获取了它的指针)

  1. C++与QtQuick之间传递qquickItem:

在C++中将QQuickItem指针使用信号和槽的方式发送给qml,而qml中某个变量的定义为property var temp: null,当传递过来直接对temp赋值即可,若这个指针在C++中已经delete,则可以用if (temp===null)来判断指针是否有效,若有效,则可以调用Q_INVOKABLE标识的函数

  1. Qt release调试:

在Pro文件加入QMAKE_CXXFLAGS_RELEASE = Q M A K E C F L A G S R E L E A S E W I T H D E B U G I N F O < b r / > Q M A K E L F L A G S R E L E A S E = QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO<br />QMAKE_LFLAGS_RELEASE = QMAKECFLAGSRELEASEWITHDEBUGINFO<br/>QMAKELFLAGSRELEASE=QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO

  1. C++ const指针:
const char * ss= "xxxxxx";    // 这个表示的是指针指向的内容不可修改
char * const ss = "xxxxxx";  // 这个表示这是一个指针常量,再不能指向其他地址,比如ss= ...是不允许的
const char * const ss ="" ;  // 这个就是指针不可修改,指向的内容也不可以修改
  1. 测试函数运行时间:
// 在qml中测试函数运行时间
var start = new Date().getTime(); 
var end = new Date().getTime() 
console.log(end - start)
    
//C++中测试函数运行时间
clock_t start = clock();
clock_t end = clock();
qDebug()<<end - start<<"ms"
  1. C++枚举与枚举类区别:

enum class Color { black, white, red },仅可在使用Color 命名空间的值,如Color c = Color::white是正确的,
但是使用Color c = white是错误的。而定义enum Color { black, white, red }的话,两种表达方式都是正确的,
目的在于降低命名空间污染。enum class这种枚举变量被称为限定作用域的枚举

  1. 在C++调用某一个函数时,假设有两个类,先声明类a,再声明类b,函数执行完毕后先构造的函数后析构,后构造的先析构。
  2. Qml如果自定义QQuickPaintedItem,注意若qml外部设置x和y,或者使用anchors,则在内部的press时,0的起点是从x为0的地方,不是宽度为0的地方
  3. QtQuick中若父类定义了一个信号,子类也定义了这个信号,两个信号同名,若在子类的这个信号的槽内再发送同名的父类的信号,则会崩溃
  4. qml的repeater在重新设置model后会将其delegate全部析构再重新构造,构造顺序由index为0到n,析构顺序为由n到0
  5. qml中undefined类型只有一个值,即undefined,当声明的变量未初始化时,该变量的默认值为undefined
  6. 当为qml的某个可写属性执行赋值操作的时候,若值为undefined不会执行赋值操作,仅为有效值时才会执行赋值操作
  7. 若A是undefined赋值给某个可写属性,在最外层component.onCompleted槽函数之后,A还是未赋值的状态,则不会报错,在最内层的component.onCompleted槽函数之后,A还是未赋值的状态,则会报错,若在最内层的component.onCompleted槽函数或者在这之前,对A赋值一个有效值,则不会报错
Window {
    visible: true
    width: 800
    height: 480
    title: qsTr("Hello World")

    property var m_config: Object

    QYPSDItem {
        id: fit_C
        width: parent.width; height: parent.height
        Component.onCompleted: updateImage()
        m_info: {
            var temp = m_config.test1111
            if ( temp === undefined ) {
                return undefined;
            }
            return temp
        }

        Rectangle {
            width: 100; height: 100

            Rectangle {
                width: 100; height: 100

                Rectangle {
                    width: 100; height: 100

                    Rectangle {
                        width: 100; height: 100

                        Rectangle {
                            width: 100; height: 100

                            Rectangle {
                                width: 100; height: 100
                                Component.onCompleted: {
                                    var obj= {}
                                    obj.test1111 = "123455555"
                                    m_config = obj
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
  1. qml定义对话框的类型
    1. 存在对话框时,可以拖动至界面外,下一层的软件均不会收到点击事件
    2. 存在对话框时,可以拖动至界面外,与主界面的操作互不干扰
Window {
    flags: Qt.FramelessWindowHint 
        
    // 若modelity设置为WindowModal,则点击主窗口之后这个dialog消失
    modality: Qt.ApplicationModal 
}
Window {
    width: 100; height: 100
    flags: Qt.FramelessWindowHint 
    visible: true
}
  1. C++的继承关系:
    1. private 属性不能够被继承
    2. 使用protected继承,父类的protected和public属性在子类中变为protected
    3. 使用public继承, 父类的protected和public属性不发生改变
  2. C++单例模式:
    1. 构造函数私有化:可防止被继承,并且只能通过类内的static成员函数重新声明一个实例返回
    2. 析构函数私有化:在类外函数体内不能在栈中创建对象,只能通过堆(new)创建对象
    3. 赋值运算符,拷贝构造函数设置为私有或加入后缀 = delete
    4. 返回引用类型,若返回指针有可能被用户delete
class Singleton
{
public:
	~Singleton() {
		std::cout << "destructor called!" << std::endl; 
	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;

	static Singleton& get_instance() {
		static Singleton instance;
		return instance;
	}

	// 证明在static成员函数之下可以执行构造函数
	static Singleton* get_instance222() {
		Singleton* instance = new Singleton;
		return instance;
	}

private:
	Singleton() {
		std::cout << "constructor called!" << std::endl;
	}
	// 将析构函数定义为私有就不允许外部调用delete进行析构,只能在类域内的public定义一个函数执行delete this
	// 将析构函数定义为私有不允许像func()这样的形式声明变量,只能new
	func()
	{
		A a;
	}
	/*~Singleton() { }*/
};

void testSingleton()
{
	Singleton& instance_1 = Singleton::get_instance();
	Singleton* test = Singleton::get_instance222();
	delete test;
}
  1. C++简单工厂模式
#include <iostream>
#include <string>

class Operation
{
public:
	Operation(){number1 = 0; number2 = 0;}

	void setNumber1(double value){number1 = value;}

	void setNumber2(double value){number2 = value;}

	virtual std::string getResult() = 0;

	double number1;
	double number2;
};

class OperationAdd : public Operation
{
public:
	OperationAdd() = default;
	std::string getResult()
	{
		return std::to_string(number1 + number2);
	}
};

class OperationSub : public Operation
{
public:
	OperationSub() = default;
	std::string getResult()
	{
		return std::to_string(number1 - number2);
	}
};

class OperationMul : public Operation
{
public:
	OperationMul() = default;
	std::string getResult()
	{
		return std::to_string(number1 * number2);
	}
};

class OperationDiv : public Operation
{
public:
	OperationDiv() = default;
	std::string getResult()
	{
		if (number2 == 0) 
		{
			return "Dividend cannot be 0";
		}
		return std::to_string(number1 / number2);
	}
};

Operation* getInstance(char operate)
{
	Operation* instance = nullptr;
	switch (operate)
	{
	case '+': instance = new OperationAdd; break;
	case '-': instance = new OperationSub; break;
	case '*': instance = new OperationMul; break;
	case '/': instance = new OperationDiv; break;
	default: break;	
	}
	return instance;
}

// 测试运行
Operation* instance = getInstance(operate);
instance->setNumber1(number1);
instance->setNumber2(number2);
std::cout << instance->getResult() << std::endl;;
delete instance;
  1. maloc、free与new、delete的区别:
    1. 共同点:都是从堆上申请空间,并且需要用户手动释放。
    2. 不同点:
      1. malloc和free是函数,new和delete是操作符
      2. malloc申请的空间不会初始化,new可以初始化
      3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可。
      4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。
      5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常。
      6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。
  2. 有了malloc/free 为什么还要new/delete:

对象在创建的同时要自动执行构造函数, 对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算delete。注意new/delete 不是库函数。

  1. C++ 是如何做到函数重载的:

C++代码在编译时会根据参数列表对函数进行重命名,例如void Swap(int a, int b)会被重命名为_Swap_int_int,void Swap(float x, float y)
会被重命名为_Swap_float_float。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,
编译器就会报错,这叫做重载决议(Overload Resolution)。

  1. sqlite基础语法:
/* sqlite3pp */

// 1. 查找语句:
const char *select_table = "select ip,netmask,gateway,dhcp,eth from hmi_network order by id;";
#ifndef _WIN32
	sqlite3pp::database db(RPC_SQLITE_PATH.c_str());
#else
	sqlite3pp::database db(RPC_SQLITE_PATH_WIN32.c_str());
#endif // _WIN32
	sqlite3pp::query qry(db, select_table);
	for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) {
		hmi_network_config config;
		config.ip = (*i).get<char const*>(0);
		config.netmask = (*i).get<char const*>(1);
		config.gateway = (*i).get<char const*>(2);
		config.dhcp = (*i).get<int>(3) == 1?true:false;
		config.eth= (*i).get<char const*>(4);
		ret.push_back(config);
	}

// 2. 更新值
#ifndef _WIN32
	sqlite3pp::database db(RPC_SQLITE_PATH.c_str());
#else
	sqlite3pp::database db(RPC_SQLITE_PATH_WIN32.c_str());
#endif // _WIN32
	const char *sql = "update hmi_network set ip=\"?\",netmask=\"?\",gateway=\"?\",dhcp=\"?\" where eth=\"?\" ;";
	sqlite3pp::command cmd(db, sql);
	cmd.bind(1, config.ip, sqlite3pp::nocopy);
	cmd.bind(2, config.netmask, sqlite3pp::nocopy);
	cmd.bind(3, config.gateway, sqlite3pp::nocopy);
	cmd.bind(4, config.dhcp ? 1 : 0);
	cmd.bind(5, config.eth, sqlite3pp::nocopy);
	return cmd.execute();

// 3. 插入值
#ifndef _WIN32
	sqlite3pp::database db(RPC_SQLITE_PATH.c_str());
#else
	sqlite3pp::database db(RPC_SQLITE_PATH_WIN32.c_str());
#endif // _WIN32

	const char *sql = "insert into  hmi_route (ip,netmask,gateway,eth) values (?,?,?,?);";
	sqlite3pp::command cmd(db, sql);
	cmd.bind(1, config.ip, sqlite3pp::nocopy);
	cmd.bind(2, config.netmask, sqlite3pp::nocopy);
	cmd.bind(3, config.gateway, sqlite3pp::nocopy);
	cmd.bind(4, config.eth, sqlite3pp::nocopy);
	return cmd.execute();
	
// 4. 删除值
#ifndef _WIN32
	sqlite3pp::database db(RPC_SQLITE_PATH.c_str());
#else
	sqlite3pp::database db(RPC_SQLITE_PATH_WIN32.c_str());
#endif // _WIN32

	const char *sql = "delete from hmi_route where ip=? and netmask=? and gateway=? and eth=?;";
	sqlite3pp::command cmd(db, sql);
	cmd.bind(1, config.ip, sqlite3pp::nocopy);
	cmd.bind(2, config.netmask, sqlite3pp::nocopy);
	cmd.bind(3, config.gateway, sqlite3pp::nocopy);
	cmd.bind(4, config.eth, sqlite3pp::nocopy);
	return cmd.execute();

// 5. 事务机制
inline int hmi_sqlite_menu_config_insert(std::vector<struct_hmi_menu_config>& ret, int userid) {
#ifndef _WIN32
	sqlite3pp::database db(RPC_SQLITE_PATH.c_str());
#else
	sqlite3pp::database db(RPC_SQLITE_PATH_WIN32.c_str());
#endif // _WIN32
	sqlite3pp::transaction xct(db);
	{
		const char* deletesql = "delete from hmi_menu_config_list where userid=?;";
		sqlite3pp::command deletecmd(db, deletesql);
		deletecmd.bind(1, userid);
		deletecmd.execute();
		/*if (deletecmd.execute() != SQLITE_OK) {
			return -1;
		}*///是否需要加上这里的逻辑判定
		for (auto config : ret) {
			const char* sql = "insert into hmi_menu_config_list (userid,appname,isselect,appid,iconurl) values (?,?,?,?,?);";
			sqlite3pp::command cmd(db, sql);
			cmd.bind(1, userid);
			cmd.bind(2, config.appname, sqlite3pp::nocopy);
			cmd.bind(3, config.isselect, sqlite3pp::nocopy);
			cmd.bind(4, config.appid, sqlite3pp::nocopy);
			cmd.bind(5, config.iconurl, sqlite3pp::nocopy);
			cmd.execute();
		}
	}
	return xct.commit();
}

/* sqlite3.c */

// 1. 创建数据库, userid必须唯一
"create table if not exists d_patient_multi_cal(userid      INTEGER     PRIMARY KEY AUTOINCREMENT NOT NULL, \
                              name text, number text, serialNumber int, calData text, calState int)";

// 2. 给数据库添加字段
"ALTER TABLE 'd_patient_multi_cal' ADD 'test888' text";
// 增加字段并设置默认值
"ALTER TABLE 'd_patient_multi_cal' ADD 'test888' text DEFAULT 'ppp'"; 

// 3. 插入值
"INSERT INTO 'd_patient_multi_cal' (`userid`,`name`, `number`, serialNumber, calData, calState) VALUES (5,'p','m',5,5,6);";

// 4. 查询某个字段是否存在
const char* sql = "select * from d_patient_multi_cal where name = test888;";
rc = sqlite3_exec(db, sql, NULL, NULL, &err_msg);
if (rc == SQLITE_OK)
    return exist;
else 
    return not exist;
  1. Qt中数值转换可能存在坑,如下所示:当double类型转为int类型不会正常截断,而是直接为0:
QJsonObject config;
config.insert("trayColSpacing", 30);
config.insert("trayRowSpacing", 100154.9999);
qreal p0 = config["trayRowSpacing"].toDouble(); // 100154.9999
int p1 = config["trayRowSpacing"].toInt(); // 0
qreal p2 = config["trayColSpacing"].toDouble(); // 30
int p3 = config["trayColSpacing"].toInt(); // 30

特别注意JsonValue为double类型,但是转为int直接为0,所以再Qt中对数值的转换要小心

  1. Qml声明属性方式:
property ThreadTest test123: ThreadTest{}
property ThreadTest test123: null
ThreadTest { id: thread }
Component.onCompleted: test123 = thread
  1. Qt中使用多线程的简易方式
// 1. 包含头文件
#include <QFuture>
#include <QMutex>
#include <QWaitCondition>
#include <QtConcurrent/QtConcurrentRun>

// 2. 类内中使用形式为:
int test123(int p) { return p; }
QFuture<int> p = QtConcurrent::run(this, &ThreadTest::test123, 30); //这种形式会阻塞等待获取结果
QtConcurrent::run(this, &ThreadTest::test123, 30);					// 不会阻塞,最好使用信号的形式将结果用信号发出

// 3. lamada表达式:
QFuture < void > future =  QtConcurrent::run([=](){
	qDebug() << __FUNCTION__  << QThread::currentThreadId() << QThread::currentThread();
});

// 4. 非类内中使用线程
QFuture<void> fut1 = run(func, QString("Thread 1"));
  1. C语言宏定义内##作用:
#define	__SOCKADDR_COMMON(sa_prefix) \
  int sa_prefix##family

struct sockaddr_in
{
	__SOCKADDR_COMMON(sin_);  // 转换为int sin_family; 用于改变变量名
};
  1. std::function:
int add(int i, int j)
{
	return i + j;
}

// 绑定类内函数
function<int(int, int)> f = bind(&Math::Minus, &m, placeholders::_1, placeholders::_2);
// 绑定类外或者static函数
function<int(int, int)> f = add;
if (f) {
    cout << 1 << endl;  // 若f未赋值可以使用if判断,结果为false
}
else {
    cout << 2 << endl;
}
  1. C++多线程使用总结:
    1. sleep与wait的区别:最大的区别在于是否释放锁
      1. 当一个线程执行wait()方法时,它就进入到一个和该对象相关的等待池中,同时释放对象锁,并让出CPU资源,待指定时间结束后返还得到对象锁
      2. 当一个线程执行sleep()方法时,当线程处于上锁,sleep()方法不会释放对象锁,即睡眠时也持有对象锁。只会让出CPU执行时间片,并不会释放同步资源锁
    2. 主线程和子线程分别调用lock()方法:若出现排队优先给主线程,这有些类似于读写锁:
      1. 若子线程一直占用着锁,则主线程请求锁也阻塞等待
      2. 若子线程先占用锁,再释放锁,如此往复,则主线程请求锁时若子线程成占有锁,等待释放锁后会立即给到主线程,主线程释放锁后又给到子线程锁
    3. 当在子线程执行耗时的操作且在子线程的最开始处绑定一个信号,连接方式为队列连接,在这个槽函数里面延时十秒,则是主线程阻塞十秒,而不是子线程
  2. 各种延时函数区别:
// 1. while延时:非常消耗CPU资源,可也仅仅是让界面假活而已(比如可以处理hover等状态,但是不可以处理click等具体的事件)
QTime timer;
timer.start();

while ( timer.elapsed() < msecond )
{
    QCoreApplication::processEvents();  // 非阻塞延时 - 不停地处理事件,让程序保持响应
}

// 2. 当前线程延时,如果有锁会占用着锁
std::this_thread::sleep_for(std::chrono::milliseconds(m_interval));

// 3. 非主线程的延时函数,用在子线程中可以,在主线程中无效
QThread::msleep(m_interval);

// 4. 通过事件循环进行延时,也可以做成条件等待的函数,但是在多线程情况下可能不稳定
m_loop = new QEventLoop;
QTimer::singleShot(m_waitInterval, m_loop, SLOT(quit()));
m_loop->exec();

// 5. 通过条件变量进行延时
m_mutex.lock();
m_condition.wait(&m_mutex, m_waitInterval);
m_mutex.unlock();

  1. 在主线程内执行try catch,子线程抛出异常会导致崩溃
try {
    std::thread t([&](){
        QThread::msleep(1000);
        throw "child thread has exit !";
    });
    
} catch (const char *msg) {
    qDebug()<<"the error message:"<<msg;
}
  1. Qt事件传播
    1. 若安装了事件过滤器:则所有安装了过滤器的控件事件都先经过过滤器,若已经不需要将事件转发,则返回true,否则返回false
    2. 若未安装事件过滤器:则事件都先经过子控件本身,若已经处理,不再分发,若未处理,需要再次分发
  2. Qt中QLayout和QWidget析构:
    1. 若是在线程里面删除QObject,不能直接使用delete,需要使用QWidget::deleteLater()
    2. 将一个QWidget加入QLayout(addwidget),在delete QLayout后,这个wgt不会被析构,但是若delete layout的父类QWidget,则加入的QWidget会被析构掉,避免的方法是QWidget::setParent(nullptr)(因为调用addWidget方法后就会将layout对应的widget设置为所添加widget的父类)
    3. 在适当的时候将QWidget设置不会删除的父类,则不会有问题
  3. 槽函数中获取的sender()是最后触发槽函数的信号发送者
  4. Object::connect的坑:
// The connection will automatically disconnect if the sender or the context is destroyed
//. However, you should take care that any objects used within the functor are still alive when the signal is emitted.
1. QObject::connect(button, &QPushButton::clicked, this, someFunction, Qt::QueuedConnection);

// The connection will automatically disconnect if the sender is destroyed
2. QObject::connect(button, &QPushButton::clicked, someFunction);

这里遇到过一个坑,要是连接toolbar等单例的信号,那么就算对应的槽函数被销毁了,连接也不会消失,这样
极有可能导致调用一个不存在的函数,导致崩溃
所以在连接一些单例的时候,一定不要少些了this,不要使用这样的形式:
connect(button, &QPushButton::clicked, someFunction);
  1. QString中文与std::string互转
std::string cstr;
QString qstring = QStringLiteral("你哈成为");
//从std::string 到QString
//从QString 到 std::string
cstr = std::string((const char *)qstring.toLocal8Bit());
qstring = QString(QString::fromLocal8Bit(cstr.c_str()));

QString 转toLatin1 toUtf8 toLocal8Bit
toUtf8:utf8字符集以2个或以上的字节表示一个汉字。实际上具体的数值和unicode有很大的相关
toLatin1:ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF
toLocal8Bit:gb18030字符集兼容了gbk字符集,以两个字节表示一个文字。windows系统可能使用的就是这两种的一种
  1. VS2017配置dll,lib相关问题
    1. 属性->调试->环境:
    2. 设置多个环境:
      1. 绝对路径:path=%path%;D:\LearnVTK\ThirdLibrary\VTK-debug\bin;D:\LearnVTK\ThirdLibrary\ITK-debug\bin\
      2. 相对路径:path=%3b;…/…/ThirdLibrary/VTK-debug/bin;…/…/ThirdLibrary/ITK-debug/bin
      3. 设置单个环境:PATH=…/…/ThirdLibrary/VTK-release/bin
    3. 包含目录,库目录,附加依赖项随便一查就知道
  2. 继承自QWidget并带有Q_OBJECT宏的dll导出
#include <QWidget>
#include <QtGlobal>

// 在预处理中添加BUILD_OCTCAMERA_LIB的宏,生成dll其他项目调用就可以成功,但是为什么一定要定义一堆宏呢,
// 直接使用Q_DECL_EXPORT替换OCTCAMERA_EXPORT不是更简单吗

// 这样定义链接也会出现错误,一定要使用项目中的预处理器
//#define MCEWCEWCEW 1
//#  if defined(MCEWCEWCEW) 

#  if defined(BUILD_OCTCAMERA_LIB) 
#    define OCTCAMERA_EXPORT Q_DECL_EXPORT
#else
#    define OCTCAMERA_EXPORT Q_DECL_IMPORT
#  endif

// 这里
//class OCTCAMERA_EXPORT Mytest : public QWidget
class Q_DECL_EXPORT Mytest : public QWidget
{
    Q_OBJECT

public:
    Mytest(QWidget *parent = nullptr);
    ~Mytest();
};


  1. QJsonObject和QByteArray的相互转换:
/**
* @brief 字节数组转Json
* @param byteArray
* @return
*/
QJsonObject getJsonFromByteArray(const QByteArray &byteArray)
{
    QJsonParseError jsonParseError;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(byteArray, &jsonParseError);

    QJsonObject jsonObj = jsonDoc.object();
    return jsonObj;
}

/**
* @brief json转字节数组
* @param jsonObj
* @return
*/
QByteArray getByteArrayFromJsonObject(const QJsonObject &jsonObj)
{
    return QJsonDocument(jsonObj).toJson();
}

  1. 在VS里面配置好Qt项目信息后如何输出控制台信息:
    1. 点击项目属性
    2. 链接器->系统->子系统->选择控制台
  2. 正确析构QThread
class test: public QThread
{
public:
    test()
	{}
    ~test()
    {
        qDebug()<<"~test() start";
        wait(); // 这里的wait目的是让线程内的内容一直执行,直到结束
        qDebug()<<"~test() end";
    }
    virtual void run() override
    {
        for (int i = 0;i<10;++i)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            qDebug()<<"run times:"<<QString::number(i + 1);
        }
    }
};
  1. 读写JSON
// write
QFile file("write.json");
if(!file.open(QIODevice::ReadWrite))
{
    return 0;
}

// 写入jsonObj是无序的, 写入jsonArray是有序的,可以组合多个jsonObjct变成有序的
QJsonArray jsonArray;
QJsonObject jsonObject;
for(int i = 0; i < 3; i++) {
    QJsonObject jsonObject;
    QJsonValue value;
    jsonObject.insert("name", QString::number(i+1));
    jsonObject.insert("age", i+18);
    jsonObject.insert("time", QDateTime::currentDateTime().toString());
    jsonArray.append(jsonObject);
}

jsonObject.insert("number", jsonArray.size());
jsonArray.append(jsonObject);

QJsonDocument jsonDoc;
jsonDoc.setArray(jsonArray);

file.write(jsonDoc.toJson());
file.close();

// read
QFile file("read.json");
if(!file.open(QIODevice::ReadOnly))
{
    return 0;
}
QByteArray array = file.readAll(); //将数据读到array中
qDebug()<<array;

QJsonParseError json_error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(array,&json_error);

if(json_error.error == QJsonParseError::NoError)
{
    QJsonObject rootObj = jsonDoc.object();

    QJsonObject::iterator it = rootObj.begin();
    for(; it != rootObj.end(); ++it)
    {
        qDebug()<<it.key()<<it.value();
    }
}
  1. Qt乱码的问题
    1. 代码文件选择用utf8编码带bom。
    2. 在有中文汉字的代码文件顶部加一行(一般是cpp文件) #pragma execution_character_set(“utf-8”) 可以考虑放在head.h中,然后需要的地方就引入head头文件就行,而不是这行代码写的到处都是;这行代码是为了告诉msvc编译器当前代码文件用utf8去编译。
    3. main函数中加入设置编码的代码,以便兼容Qt4,如果没有Qt4的场景可以不用,从Qt5开始默认就是utf8编码。
    4. QStringLiteral(“强强”)等效于u8"强强"
// 兼容Qt4版本
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
#if _MSC_VER
    QTextCodec *codec = QTextCodec::codecForName("gbk");
#else
    QTextCodec *codec = QTextCodec::codecForName("utf-8");
#endif
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    QTextCodec::setCodecForTr(codec);
#else
    QTextCodec *codec = QTextCodec::codecForName("utf-8");
    QTextCodec::setCodecForLocale(codec);
#endif
  1. QRect的坑
QPoint center(720, 540);
QRectF crossRect(720 - (100 / 2), 540 - (100 / 2), 100, 100);
QRectF enlargeRect(720 - (920 / 2), 540 - (520 / 2), 920, 520);

crossRect.setWidth(150);
crossRect.setHeight(160);
enlargeRect.setWidth(500);
enlargeRect.setHeight(260);


crossRect.setX(center.x() - (crossRect.width() / 2));
crossRect.setY(center.y() - (crossRect.height() / 2));

enlargeRect.setX(center.x() - (enlargeRect.width() / 2));
enlargeRect.setY(center.y() - (enlargeRect.height() / 2));

// enlargeRect最后的尺寸居然变化了
  1. 在圆中角度的求法:

image.png

QPointF center = this->rect().center();
double dx = event->x() - center.x();
double dy = -(event->y() - center.y());
double arctan = std::atan(dy / dx);

if (dx < 0 && dy >= 0) {
    arctan += M_PI;
}
else if (dx < 0 && dy < 0) {
    arctan -= M_PI;
}
int angle = arctan * 180 / M_PI;
angle = angle >= 0 ? angle : 360 + angle;
  1. 在子线程崩溃后主线程是否会崩溃:是
  2. 在异常处理的函数里能执行主线程或子线程的函数吗:是
//注册异常处理函数  
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)ApplicationAbnormalExitHandler);

LONG ApplicationAbnormalExitHandler(EXCEPTION_POINTERS* pException)
{
    // to notify unload device
    LOG_ERROR << "ApplicationAbnormalExitHandler";

    // 能够正常执行
    QImage monitor = ej::rcf::pub::fetch<QImage, const std::string&>("ui.scleral.get.monitor.img");
    
    ApplicationCrashHandler(pException);

    ej::rcf::pub::publish<const std::string&>("event.app.exit.abnormal");
    return EXCEPTION_EXECUTE_HANDLER;
}
  1. QImage QImage::scaled有坑:
// 如果image为单通道图像,经过scale后temp为三通道图像
QImage temp = image.scaled(image.width(), 
image.width() / 9.96799 * 7.34282, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

// 这种方式不会改变通道数
QImage temp = image.scaled(image.width(), image.width() / 9.96799 * 7.34282);
  1. QScopedPointer,std::unique_ptr一定要定义析构函数,否则会出错

严重性 代码 说明 项目 文件 行 禁止显示状态
错误 C2027 使用了未定义类型“ej::ui::common::WgtPlotPrivate” ui_common D:\Qt5.14.2\5.14.2\msvc2017_64\include\QtCore\qscopedpointer.h 57

严重性 代码 说明 项目 文件 行 禁止显示状态
错误 C2118 负下标 ui_common D:\Qt5.14.2\5.14.2\msvc2017_64\include\QtCore\qscopedpointer.h 57

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值