- 面向过程的static:
- 函数中的静态变量 :当变量声明为static时,空间将在程序的生命周期内分配,其被存放在在全局数据区。即使多次调用该函数,静态变量的空间也只分配一次
- 静态区(全局区):静态变量和全局变量的存储区域是一起的,一旦静态区的内存被分配, 静态区的内存直到程序全部结束之后才会被释放。
#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
- 面向对象的static:
- 类中的静态变量 声明为static的变量只被初始化一次,因为它们在单独的静态存储中分配了空间,因此类中的静态变量由对象共享,可以减小对象的大小。对于不同的对象,不能有相同静态变量的多个副本。也是因为这个原因,静态变量不能使用构造函数初始化
- 类中的静态成员变量必须在类内声明,在类外定义(被const修饰的除外)
- 静态类 :和变量一样,静态类的生命周期直到程序的结束。在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
};
};
- 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;
}
- 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;
};
- 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信号时,其子组件已经完成可写属性的赋值操作了
- QObject中enable属性会进行值的传递,若父类的enable为true,则子类也为true,若父类为false,子类也为false
- 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是组合事件
- 在使用tabview的qml组件时,想获得它的rows和columns,有两个方法:
- 使用C++定义的rowCount和columnCount,但是在刷新界面的时候老是会出现刷新一个item时宽高等信息不对称,暂时也不知道原理。隐藏着较多bug
- 用tableview内的rows和columns,就不会出现刷新某一个item时宽高等信息不对应的问题
- C++ new一个任意指针,delete不会有问题,但是如果p++之后,再次delete就会出错。若初始时将某个指针设置为nullptr,则可以无限delete而不会出错
- C 语言strcat函数:
char *strcat(char *dest, const char *src),两个指针必须使用\0作为结束符才可以正常拼接,包括平时使用char指针或者数组的时候都需要注意以\0作为结束符,否则在调用一些函数时无法得到正常的处理。
- C语言spintf函数:
int sprintf(char *str, char * format [, argument, …]);
如sprintf(rpn + strlen(rpn), " %s", number.c_str()),将number拼接至rpn+strlen(rpn)的位置,以空格作为间隔
- QDialog 与主页面无关 -> 先new类,再this->show();与主页面相关,-> 先new,this->exec(),delete this
- QTableWidget设置表头不可点击,ui->regionTable->horizontalHeader()->setSectionsClickable(false)
- QTableWidget currentcellchanged信号对于翻动QtableWidget很有用
- cv::Mat==操作符:
// 不相等为0,相等为255(nan与nan不相等)0显示为黑色,255显示为白色,
// temp == temp是指每一个单元格的值进行相等比较,相等为true,否则为false
cv::Mat tmpMask = (temp == temp);
- cv::Mat::setTo
//若tmpMask某个位置与temp的这个位置值相等,则设置为240,否则设置不成功,
// 把矩阵mask中元素不为0的点全部变为value值;
tmpMask.setTo(240,tmpMask==temp);
- 重写鼠标移动事件
- QGraphicsView::mouseMoveEvent:获取的坐标为物理坐标
- QGraphicsScene::mouseMoveEvent:获取的坐标为场景坐标
- git拉取失败解决方案:
- 方案一:
- git stash
- git pull
- git stash pop
- 方案一:
- Qt 打包软件:
- 打包QWidget软件:cd /d D:\release windeployqt “xxx.exe”
- 打包QWidget的dll:cd /d D:\release windeployqt xxx.dll
- 打包含有QtQuick模块的软件:cd /d D:\release windeployqt xxx.exe --qmldir D:\Qt5.14.2\5.14.2\msvc2017_64\qml
- QWidget中或QtQuick中函数内调用对话框:
qml和C++内都可以使用Q_INVOLKABLE static void showMessageBox(int type, QString info);
显示QMessageBox时会阻塞,不会执行接下来的函数,直到关闭
- 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"
}
}
- 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;
}
- 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函数的重载
- AbstractSeries createSeries(enumeration type, string name, AbstractAxis axisX, AbstractAxis axisY):
此函数的返回值在帮助文档里是AbstractSeries类型,但返回的其实是一个指针(有可能确实是类,是var赋值时直接获取了它的指针)
- C++与QtQuick之间传递qquickItem:
在C++中将QQuickItem指针使用信号和槽的方式发送给qml,而qml中某个变量的定义为property var temp: null,当传递过来直接对temp赋值即可,若这个指针在C++中已经delete,则可以用if (temp===null)来判断指针是否有效,若有效,则可以调用Q_INVOKABLE标识的函数
- 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
- C++ const指针:
const char * ss= "xxxxxx"; // 这个表示的是指针指向的内容不可修改
char * const ss = "xxxxxx"; // 这个表示这是一个指针常量,再不能指向其他地址,比如ss= ...是不允许的
const char * const ss ="" ; // 这个就是指针不可修改,指向的内容也不可以修改
- 测试函数运行时间:
// 在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"
- C++枚举与枚举类区别:
enum class Color { black, white, red },仅可在使用Color 命名空间的值,如Color c = Color::white是正确的,
但是使用Color c = white是错误的。而定义enum Color { black, white, red }的话,两种表达方式都是正确的,
目的在于降低命名空间污染。enum class这种枚举变量被称为限定作用域的枚举
- 在C++调用某一个函数时,假设有两个类,先声明类a,再声明类b,函数执行完毕后先构造的函数后析构,后构造的先析构。
- Qml如果自定义QQuickPaintedItem,注意若qml外部设置x和y,或者使用anchors,则在内部的press时,0的起点是从x为0的地方,不是宽度为0的地方
- QtQuick中若父类定义了一个信号,子类也定义了这个信号,两个信号同名,若在子类的这个信号的槽内再发送同名的父类的信号,则会崩溃
- qml的repeater在重新设置model后会将其delegate全部析构再重新构造,构造顺序由index为0到n,析构顺序为由n到0
- qml中undefined类型只有一个值,即undefined,当声明的变量未初始化时,该变量的默认值为undefined
- 当为qml的某个可写属性执行赋值操作的时候,若值为undefined不会执行赋值操作,仅为有效值时才会执行赋值操作
- 若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
}
}
}
}
}
}
}
}
}
- qml定义对话框的类型
- 存在对话框时,可以拖动至界面外,下一层的软件均不会收到点击事件
- 存在对话框时,可以拖动至界面外,与主界面的操作互不干扰
Window {
flags: Qt.FramelessWindowHint
// 若modelity设置为WindowModal,则点击主窗口之后这个dialog消失
modality: Qt.ApplicationModal
}
Window {
width: 100; height: 100
flags: Qt.FramelessWindowHint
visible: true
}
- C++的继承关系:
- private 属性不能够被继承
- 使用protected继承,父类的protected和public属性在子类中变为protected
- 使用public继承, 父类的protected和public属性不发生改变
- C++单例模式:
- 构造函数私有化:可防止被继承,并且只能通过类内的static成员函数重新声明一个实例返回
- 析构函数私有化:在类外函数体内不能在栈中创建对象,只能通过堆(new)创建对象
- 赋值运算符,拷贝构造函数设置为私有或加入后缀 = delete
- 返回引用类型,若返回指针有可能被用户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;
}
- 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;
- maloc、free与new、delete的区别:
- 共同点:都是从堆上申请空间,并且需要用户手动释放。
- 不同点:
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可。
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常。
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。
- 有了malloc/free 为什么还要new/delete:
对象在创建的同时要自动执行构造函数, 对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算delete。注意new/delete 不是库函数。
- C++ 是如何做到函数重载的:
C++代码在编译时会根据参数列表对函数进行重命名,例如void Swap(int a, int b)会被重命名为_Swap_int_int,void Swap(float x, float y)
会被重命名为_Swap_float_float。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,
编译器就会报错,这叫做重载决议(Overload Resolution)。
- 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;
- 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中对数值的转换要小心
- Qml声明属性方式:
property ThreadTest test123: ThreadTest{}
property ThreadTest test123: null
ThreadTest { id: thread }
Component.onCompleted: test123 = thread
- 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"));
- C语言宏定义内##作用:
#define __SOCKADDR_COMMON(sa_prefix) \
int sa_prefix##family
struct sockaddr_in
{
__SOCKADDR_COMMON(sin_); // 转换为int sin_family; 用于改变变量名
};
- 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;
}
- C++多线程使用总结:
- sleep与wait的区别:最大的区别在于是否释放锁
- 当一个线程执行wait()方法时,它就进入到一个和该对象相关的等待池中,同时释放对象锁,并让出CPU资源,待指定时间结束后返还得到对象锁
- 当一个线程执行sleep()方法时,当线程处于上锁,sleep()方法不会释放对象锁,即睡眠时也持有对象锁。只会让出CPU执行时间片,并不会释放同步资源锁
- 主线程和子线程分别调用lock()方法:若出现排队优先给主线程,这有些类似于读写锁:
- 若子线程一直占用着锁,则主线程请求锁也阻塞等待
- 若子线程先占用锁,再释放锁,如此往复,则主线程请求锁时若子线程成占有锁,等待释放锁后会立即给到主线程,主线程释放锁后又给到子线程锁
- 当在子线程执行耗时的操作且在子线程的最开始处绑定一个信号,连接方式为队列连接,在这个槽函数里面延时十秒,则是主线程阻塞十秒,而不是子线程
- sleep与wait的区别:最大的区别在于是否释放锁
- 各种延时函数区别:
// 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();
- 在主线程内执行try catch,子线程抛出异常会导致崩溃
try {
std::thread t([&](){
QThread::msleep(1000);
throw "child thread has exit !";
});
} catch (const char *msg) {
qDebug()<<"the error message:"<<msg;
}
- Qt事件传播
- 若安装了事件过滤器:则所有安装了过滤器的控件事件都先经过过滤器,若已经不需要将事件转发,则返回true,否则返回false
- 若未安装事件过滤器:则事件都先经过子控件本身,若已经处理,不再分发,若未处理,需要再次分发
- Qt中QLayout和QWidget析构:
- 若是在线程里面删除QObject,不能直接使用delete,需要使用QWidget::deleteLater()
- 将一个QWidget加入QLayout(addwidget),在delete QLayout后,这个wgt不会被析构,但是若delete layout的父类QWidget,则加入的QWidget会被析构掉,避免的方法是QWidget::setParent(nullptr)(因为调用addWidget方法后就会将layout对应的widget设置为所添加widget的父类)
- 在适当的时候将QWidget设置不会删除的父类,则不会有问题
- 槽函数中获取的sender()是最后触发槽函数的信号发送者
- 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);
- 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系统可能使用的就是这两种的一种
- VS2017配置dll,lib相关问题
- 属性->调试->环境:
- 设置多个环境:
- 绝对路径:path=%path%;D:\LearnVTK\ThirdLibrary\VTK-debug\bin;D:\LearnVTK\ThirdLibrary\ITK-debug\bin\
- 相对路径:path=%3b;…/…/ThirdLibrary/VTK-debug/bin;…/…/ThirdLibrary/ITK-debug/bin
- 设置单个环境:PATH=…/…/ThirdLibrary/VTK-release/bin
- 包含目录,库目录,附加依赖项随便一查就知道
- 继承自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();
};
- 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();
}
- 在VS里面配置好Qt项目信息后如何输出控制台信息:
- 点击项目属性
- 链接器->系统->子系统->选择控制台
- 正确析构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);
}
}
};
- 读写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();
}
}
- Qt乱码的问题
- 代码文件选择用utf8编码带bom。
- 在有中文汉字的代码文件顶部加一行(一般是cpp文件) #pragma execution_character_set(“utf-8”) 可以考虑放在head.h中,然后需要的地方就引入head头文件就行,而不是这行代码写的到处都是;这行代码是为了告诉msvc编译器当前代码文件用utf8去编译。
- main函数中加入设置编码的代码,以便兼容Qt4,如果没有Qt4的场景可以不用,从Qt5开始默认就是utf8编码。
- 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
- 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最后的尺寸居然变化了
- 在圆中角度的求法:
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;
- 在子线程崩溃后主线程是否会崩溃:是
- 在异常处理的函数里能执行主线程或子线程的函数吗:是
//注册异常处理函数
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;
}
- 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);
- 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