Qt开发经验小技巧226-230

119 阅读6分钟
  1. qtc开发工具内置了不少的函数,可以很方便的进行一些判断和处理。
//最小版本要求
!minQtVersion(5, 15, 2) {
    message("Cannot build Qt Installer Framework with Qt version $${QT_VERSION}.")
    error("Use at least Qt 5.15.2.")
}
  1. 有时候文本框中的内容过长,而文本框默认光标在尾部,所以要主动设置下将光标移到最前面
//三种方法都可以
ui->lineEdit->setSelection(0, 0);
ui->lineEdit->setCursorPosition(0);
//样式表方式
"QLineEdit{qproperty-cursorPosition:0;}
  1. 关于Qt浏览器模块的几点说明。
  • Qt5.6以前用的是webkit,Qt5.6版本以后分两种情况,一种是mingw编译器(windows系统)对应的Qt库不再提供浏览器模块。
  • Qt5.6以后的版本在linux系统和mac等系统,都不存在没有浏览器控件的情况,都使用的是webengine。
  • 仅仅是windows上的mingw编译器的Qt版本没有,其他系统其实都有的。很多人在这个地方都有疑问,都以为只有msvc编译器有浏览器控件,其实确切的说是在windows上msvc的Qt库带浏览器控件。
  • 安装Qt的时候webengine模块默认不勾选,需要主动勾选才会安装。
  • 也不是所有的msvc的Qt版本都有webengine浏览器模块,哪怕你勾选了也没用,有些版本官方并没有编译,需要自行编译。需要到对应的Qt安装目录查看是否有 Qt5WebEngine.dll 文件。
  • 如果仅仅是为了弥补mingw版本缺失浏览器模块的遗憾,推荐用miniblink。
  • 如果为了统一兼容各种版本和系统,推荐用cef。
  • 如果没有历史包袱,推荐用webengine,与Qt的集成度高。
  • webkit和miniblink默认都不支持gpu,webengine默认走gpu。
  • qwebengine默认不支持MP4,需要自己重新编译。
  1. 关于编译数据库插件的几个经验总结。
  • 安装对应的数据库,安装后会有include头文件和lib链接库文件,这是基本的前提,编译数据库插件必须要有这两个东西。务必注意,32位的Qt必须安装32位的数据库才能正常编译成功,位数要一致。
  • 准备好数据库插件源码,比如qt-everywhere-src-5.14.2\qtbase\src\plugins\sqldrivers\mysql,可以在安装Qt的时候勾选src,或者后期直接官网重新下载源码解压出来。
  • 打开你要编译的数据库插件源码,比如mysql就打开mysql.pro,oracle就打开oci.pro。
  • 在pro中注释掉一行 #QMAKE_USE += mysql,如果是oci项目则是#QMAKE_USE += oci。
  • qsqldriverbase.pri文件中注释掉 #include(..shadowed(..PWD)/qtsqldrivers-config.pri)。
  • mysql.pro文件内容下面加上如下代码。
path = C:/Qt/mysql-5.7.30-winx64
INCLUDEPATH += $$path/include
win32:LIBS += -L$$path/lib -llibmysql
  • oci.pro文件内容下面加上如下代码。
path = C:/app/Administrator/product/11.2.0/client_1
INCLUDEPATH += $$path/oci/include
win32:LIBS += -L$$path/oci/lib/msvc -loci
  • psql.pro文件内容下面加上如下代码。
path = "C:/Program Files/PostgreSQL/13"
INCLUDEPATH += $$path/include
win32:LIBS += -L$$path/lib -llibpq
  • 以上写法同时支持mingw和msvc,其他系统编译过程也是类似。编译完成后默认会在你当前源码所在盘符的根目录下,会出现plugins目录,里面sqldrivers目录下就是对应编译生成好的插件动态库。
  • 默认oracle的插件驱动代码是按照oracle12的函数写的,如果链接的是oracle11,则需要改动两行代码才能编译成功。打开qsql_oci.cpp文件大概在1559行代码左右,有个OCIBindByPos2函数改成OCIBindByPos,下面还有一行bindColumn.lengths改成(ub2*)bindColumn.lengths。
  1. 关于Qt数据库开发的一些冷知识。
  • Qt即支持库的形式直接和数据库通信,也支持ODBC数据源的形式和各种数据库通信,这样就涵盖了所有的情况。
  • Qt数据库程序打包发布,所有前提:注意区分32/64位,你的程序是32位的就必须带上32位的库,64位的必须带上64位的库,这点Qt的库也是这个要求。mysql发布最简单,带上一个mysql的动态库文件就行(windows上的是libmysql.dll),非常简单。sqlserver不用带,因为是微软的亲儿子,一般操作系统自带。postgres需要带上libpq.dll、libintl-8.dll、libiconv-2.dll、libeay32.dll、ssleay32.dll这几个文件就行。oracle需要带上oci.dll、oraociei11.dll(这个文件很大有130MB+),如果不行建议直接安装个oracle client客户端软件,然后对应bin目录设置到环境变量就好。
  • 打包发布后测试下来,发现32位的程序也可以正常连接64位的mysql,64位的程序也可以正常连接32位的mysql,因此判断只要和程序的库的位数一致就行(编译的时候也是这个规则,32位的Qt程序编译数据库插件也要用32位的数据库链接库。),不需要和具体的数据库的位数一致,测试过mysql、sqlserver、postgresql数据库都是类似规则。
  • 大量测试对比下来,通过odbc数据源的方式和直连数据库的方式批量插入大量数据记录,直连方式速度更快,约5%左右,所以建议尽量采用此方式,是在没有此方式的环境才采用odbc数据源的方式,Qt默认自带odbc数据库插件。
  • 不同数据库在执行sql脚本的时候,会自动将表名或者字段名转成大写或小写,mysql会将表名转成小写、postgresql会将表名和字段名转成小写、oracle会将表名和字段名转成大写。这就导致使用QSqlTableModel调用setTable设置数据库表名的时候,一定要和数据库中的表名一致,区分大小写,所以就是在对postgresql和oracle数据库的时候一定要注意,本人就是在这里卡了很久,差点要把这巨大的屎盆扣在Qt的BUG上。
void DbHelper::bindTable(const QString &dbType, QSqlTableModel *model, const QString &table)
{
    //postgresql全部小写,oracle全部大写,这两个数据库严格区分表名字段名的大小写卧槽
    QString flag = dbType.toUpper();
    if (flag == "POSTGRESQL") {
        model->setTable(table.toLower());
    } else if (flag == "ORACLE") {
        model->setTable(table.toUpper());
    } else {
        model->setTable(table);
    }
}
  • Qt支持不指定数据库名打开数据库,因为有时候是要在连接数据库服务器后,执行sql语句创建数据库。数据库都还没存在怎么连接呢,测试发现sqlite、mysql、sqlserver、postgresql都支持这个特性。在删除和创建数据库的前提是该数据库没有被其他程序占用,比如其他程序已经打开了该数据库则会执行失败。这里我就折磨过很多次,为什么执行失败呢?后面发现第三方数据库工具已经打开了该数据库,把工具关掉就ok了。
QSqlDatabase database = QSqlDatabase::addDatabase("QMYSQL");
//database.setDatabaseName("dbtool");
database.setHostName("127.0.0.1");
database.setPort(3306);
database.setUserName("root");
database.setPassword("root");

if (database.open()) {
    QSqlQuery query(database);
    qDebug() << "删除数据库" << query.exec("drop database dbtool");
    qDebug() << "创建数据库" << query.exec("create database dbtool");
    if (query.exec("select * from userinfo")) {
        while (query.next()) {
            qDebug() << "查询数据库" << query.value(0);
        }
    }
} else {
     qDebug() << "打开数据库" << database.lastError().text();
}
  • 用QSqlQueryModel+QTableView显示数据,int类型的数据,如果超过100万,会变成科学计数显示,这就很恼火了,肯定不是自己想要的结果。找遍网络搜索,终于找到一个同样问题的哥们,需要对这一列加个空的委托就行。后面发现空委托也不行,超过1000万条又屌样了,需要终极大法重载数据模型显示。
ui->tableView->setItemDelegateForColumn(0, new QItemDelegate);

//下面是终极大法
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
{
    QVariant value = QSqlQueryModel::data(index, role);
    //超过100万的数值会被科学计数显示需要这里转成字符串显示
    if (role == Qt::DisplayRole) {
        int result = value.toInt();
        if (result >= 1000000) {
            value = QString::number(result);
        }
    }
    return value
}
  • mysql数据库有多种数据库引擎,其中MyIsam不支持数据库事务,默认一般是这个引擎,所以当你使用Qt中的transaction方法后commit提交时候,会发现不成功,其实事实上又是成功的,去数据库里面查看对应的结果又是正确的。有两个办法,第一就是将数据库引擎改成InnoDB,第二就是在提交后做个错误判断 if (database.commit() || !database.lastError().isValid()) ,错误不可用也说明是成功的。
  • 如果采用odbc数据源通信,则只需设置数据库名称setDatabaseName、设置用户名称setUserName、设置用户密码setPassword这三个参数即可,因为数据源配置的时候就已经设置好对应的主机地址和端口以及关联的数据库名称,所以在用odbc数据源通信的时候只需要再次验证用户信息即可。这里特别要注意的是setDatabaseName设置数据库名称要填写数据源配置的名称。
  • 经过大量的对比测试,包括插入、删除、批量、查询、分页等操作,千万量级数据,在Qt数据库部分响应速度这块,友好度排名依次是 sqlite > postgresql > oracle > mysql > odbc 。千万量级以上是 postgresql > oracle > mysql > sqlite > odbc 。亿级别以上是 oracle > postgresql > 其他。以上测试均建立在初学者水平基础上,至于分库分表、联合查询、缓存、内存数据库等各种高级知识点没用上。
  • mysql主要有两个版本,mysql5.7和mysql8,官方说是8比5要快很多,个人测试下来,5.7比8要快很多,无论是查询,还是批量插入数据,不知道为何,网上搜索的也是这个结果(www.coder4.com/archives/75…),大家都说8慢了很多。
  • mysql有个分支叫mariadb,比mysql更纯正,据说各方面都吊打mysql(blog.csdn.net/x275920/art…),个人对比测试下来也是确实批量插入和查询性能要好不少,并且完全兼容mysql,甚至库文件直接重命名也可以直接使用,比如将libmariadb.dll改成libmysql.dll可以直接使用,而且体积还小了八倍,这个好,发布的时候又少了好几兆。
  • 如果是Qt+mysql程序,发布的时候带的库版本要和插件对应数据库版本一致,否则可能没有数据库事务特性,database.driver()->hasFeature(QSqlDriver::Transactions)为假。
  • QSqlTableModel封装的非常好,并不会一次性加载所有数据,而是随着滚动条的拉动加载需要的数据,测试一亿条的表,速度非常快,和几千条的表速度一样。
  • 在连接网络数据库的时候,如果你本地网络设置了代理,比如使用了代理上github等网站,就会发现Qt的数据库程序连不上,你需要设置下不使用本地代理设置 QNetworkProxyFactory::setUseSystemConfiguration(false) 。这个地方如果不仔细会找问题找到你怀疑人生。