Qt Creator插件制作小插曲:有关QT_NO_CAST_FROM_ASCII的注意事项



  • 这两天制作了两个Qt Creator增强套装的两个插件,其实也是非常简单的,但是其实花了我超过四天的时间,为什么呢?因为我之前很长一段时间都是在Linux下开发的,一切安好,没有任何问题,但是到了Windows下,各种问题就暴露出来了。首先呢,就是——

    1、Qt Creator源码中,默认是打开QT_NO_CAST_FROM_ASCII这个宏的,看文档,这个宏就是禁用一切来自双引号或者单引号的字符串字面量传入QString的函数。但是我们裸写字符串不是非常平常的一件事情么?这里是Qt Creator断了我们一条道路,解决的方法还是有的,我这里提供两个方法,欢迎提出新的方法:

    include($$QTCREATOR_SOURCES/src/qtcreatorplugin.pri)
    

    这一句后面添加

    DEFINES -= QT_NO_CAST_FROM_ASCII
    

    如果还有必要的话,那么还需要添加

    DEFINES -= QT_NO_CAST_TO_ASCII
    

    这些都是解除设定宏的好方法

    1. 使用QStringLiteral宏来包裹字面量
      我还设计了两个宏,分别对应识别单个字符和字符串。他们分别是:
    #define CHAR( c ) QChar( ushort( c ) )
    #define TEXT QStringLiteral
    

    这些都是非常方便的宏,可以很方便地替换掉裸写的字面量。

    2、由于Windows下编译器有MinGW以及MSVC,我们要注意我们编写的插件是C++插件,是编译器相关的,比如说,MinGW编写的插件无法在MSVC编译器编译的Qt Creator上使用。遗憾的是,Qt Creator官方编译的都是MSVC的,而且有一个现象,不同的MSVC版本编写的Qt Creator依赖的Qt库,有一些函数的地址还不能通用。比如说和上面一个例子非常相似的例子,那就是我使用MSVC2015编译的Qt Creator插件,其中用到了QString::utf8()函数,但就是这个函数,无法在官方MSVC2013编译的Qt Creator中成功载入,提示“无法找到模块”。为什么呢?看看QString::utf8()函数源码吧:

    #if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP)
        QByteArray toLatin1() const & Q_REQUIRED_RESULT
        { return toLatin1_helper(*this); }
        QByteArray toLatin1() && Q_REQUIRED_RESULT
        { return toLatin1_helper_inplace(*this); }
        QByteArray toUtf8() const & Q_REQUIRED_RESULT
        { return toUtf8_helper(*this); }
        QByteArray toUtf8() && Q_REQUIRED_RESULT
        { return toUtf8_helper(*this); }
        QByteArray toLocal8Bit() const & Q_REQUIRED_RESULT
        { return toLocal8Bit_helper(constData(), size()); }
        QByteArray toLocal8Bit() && Q_REQUIRED_RESULT
        { return toLocal8Bit_helper(constData(), size()); }
    #else
        QByteArray toLatin1() const Q_REQUIRED_RESULT;
        QByteArray toUtf8() const Q_REQUIRED_RESULT;
        QByteArray toLocal8Bit() const Q_REQUIRED_RESULT;
    #endif
    

    看出来什么吗?不同的预编译宏会导致不同版本的toUtf8()函数被编译,难怪Qt Creator发现QString::utf8()函数和自己Qt库中的这个函数地址不一致会报“无法找到模块”错误呢。

    那,有没有解决的方法呢?由于Json格式化,一定要这样的函数,因此我打算看看它的源码,是否能够找到替代的实现方法。没错,就是

    toUtf8_helper(*this);
    

    中,我找到了

    QUtf8::convertFromUnicode()
    

    方法
    终于找到了这个方法,又偶然发现QTextCodec::fromUnicode()函数中隐含调用了上述函数,于是我这么操作:

        QTextCodec* utf8Codec = QTextCodec::codecForName( "UTF-8" );
    
        QString plainText = ui->jsonEdit->toPlainText( );
        QByteArray converted = QJsonDocument::fromJson( utf8Codec->fromUnicode( plainText ) ).
                toJson( QJsonDocument::Indented );
        plainText = utf8Codec->toUnicode( converted );
    

    顺利实现效果!

    开发Qt Creator的过程中,每一步都是一个坑。但万变不离其宗,只要你有耐心,找到规律,找到核心问题,相信你一定和我一样,最终会找到问题的答案的。



  • @jiangcaiyang123Qt Creator插件制作小插曲:有关QT_NO_CAST_FROM_ASCII的注意事项 中说:

    plainText

    1. plainText 是一个编码 不是字符呢????
      怎么合适的方法转成 QByteArray,再 toUnicode( converted );
    2. 一定要DEFINES -= QT_NO_CAST_FROM_ASCII


  • @jiangcaiyang123Qt Creator插件制作小插曲:有关QT_NO_CAST_FROM_ASCII的注意事项 中说:
    //编码=0xcb9d,编号
    QTextCodec 能把GB2312任意编码转换成 Unicode编码吗?
    还是只能 由Unicode编码找到GB2312编码?
    也就是说只能 一个汉字 QString s="字";Qt存储为Unicode,QTextCodec::codecForName( "GB2312" ); fromUnicode,Unicode编码 装成了 GB2312编码,

    这样的话 由GB2312编码 转换成 Unicode编码 只能通过查表了吗????

    也就是说
    Unicode 提取区位码 可以找到 GB2312 编码;
    而由 GB2312 编码 不能反查 区 位码, 找到Unicode编码???

    也就是说 x+y=z 求z 只有一个解
    但是 z= x+y 求 x,y 可以有多种组合????



  • 那么 QTextCodec::toUnicode 做了些什么事情呢???

    QJsonDocument是5.0以上的 4.7 还用不了?
    Header:
    #include <QJsonDocument>
    qmake:
    QT += core
    Since:
    Qt 5.0



  • @MOMO QTextCodec是可以将一种编码转换为unicode,然后再转换为另外一种编码格式。



  • @MOMO 现在4.7不用了,尽量迁移到5吧。因为最新的是5.7了。


Log in to reply
 

走马观花

最近的回复

  • C

    Qt for MCU需要商业授权的

    read more
  • Qt for MCUs

    搭建Qt for MCUs PC端开发环境。qt for mcus提供了一个完整的图形框架和工具包,包含了在MCUs上设计、开发和部署gui所需的一切。它允许您在裸机或实时操作系统上运行应用程序。

    先决条件

    开发主机环境支持仅限于Windows 10

    MSVC compiler v19.16 (Visual Studio 2017 15.9.9 or newer) x64

    CMake v3.13 or newer (you can install it using the Qt Online installer) x64

    使用Qt联机安装程序安装Qt for MCUs,该安装程序可通过Qt帐户下载

    安装Qt 5.14和Qt Creator 4.11 or higher

    安装链接

    › Qt: https://account.qt.io/downloads
    › CMake: https://cmake.org/download/
    › Python 2.7 32-bit: https://www.python.org/downloads/release/python-2716/
    › Arm GCC: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnutoolchain/gnu-rm/downloads
    › J-Link Software Pack: https://www.segger.com/downloads/jlink/JLink_Windows.exe
    › J-Link OpenSDA Firmware: https://www.segger.com/downloads/jlink/OpenSDA_MIMXRT1050-EVKHyperflash
    › STM32CubeProgrammer: https://www.st.com/en/development-tools/stm32cubeprog.html
    › STM32 ST-LINK Utility: https://www.st.com/en/development-tools/stsw-link004.html​​​​​​​

    Qt Creator设置 启用Qt Creator插件 选择“帮助>关于插件”,然后从列表中选择“MCU支持(实验性)”插件,重新启动Qt Creator以应用更改
    替代文字 为MCU创建Qt工具包

    选择工具>选项>设备>MCU

    选择Qt for MCUs-Desktop 32bpp作为目标

    如果尚未设置,请提供Qt for MCUs安装目录的路径。

    单击Apply应用。

    替代文字

    替代文字
    替代文字

    注意:

    编译器要选X64,Qt版本要选64bit,CMake Tool选x64

    打开恒温器项目demo

    选择文件>打开文件或项目。。。

    打开CMakefiles.txt文件来自thermo文件夹的文件。

    选择Qt作为MCU-桌面32bpp套件。

    单击“配置项目”以完成。

    替代文字

    问题

    开发主机环境支持仅限于Windows 10

    C++编译失败,文本大字体.pixelSize.

    文本类型无法正确呈现需要复杂文本布局的unicode序列。对复杂文本使用StaticText

    read more
  • H

    hi 有问题请教你,方便加个联系方式吗

    read more
  • boost.asio是一个很棒的网络库,这回儿我也开始系统地学习起来了。想想当年接触boost,也有八年多了。这次开始接触boost,觉得既熟悉又陌生。熟悉的是小写字母+下划线的命名方式、晦涩的模板、很慢的编译速度以及较大的程序体积,陌生的是asio的各种概念:io服务、接收器、套接字等等:我之前对网络编程不是非常了解。

    于是根据我的理解,参考《Boost.Asio C++网络编程》实现了这样一个简单的客户端和服务端通信的例子,例子非常简单,还不完善,但是幸运的是,可以在本机上互通了。
    下面是客户端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void writeHandler( const boost::system::error_code& ec, size_t bytesTransferred ) { if ( ec ) { cout << "Write data error, code: " << ec.value( ) << "transferred: " << bytesTransferred << endl; } else { cout << "OK! " << bytesTransferred << "bytes written. " << endl; } } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::socket sock( service ); ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; sock.connect( ep, ec ); if ( ec ) { cout << "Connect error, code: " << ec.value( ) << ", We will exit." << endl; return ec.value( ); } else { char buf[1024] = "Hello world!"; sock.async_write_some( buffer( buf ), writeHandler ); sock.close( ); } return service.run( ); }

    下面是服务端的代码:

    #include <iostream> #include <boost/asio.hpp> #include <boost/proto/detail/ignore_unused.hpp> using namespace std; using namespace boost::asio; using namespace boost::system; using namespace boost::proto::detail;// 提供ignore_unused方法 void acceptHandle( const boost::system::error_code& code ) { cout << "Accepted." << endl; } int main(int argc, char *argv[]) { ignore_unused( argc ); ignore_unused( argv ); io_service service; ip::tcp::endpoint ep( ip::address::from_string( "127.0.0.1" ), 6545 ); boost::system::error_code ec; ip::tcp::socket sock( service ); ip::tcp::acceptor acceptor( service, ep ); acceptor.async_accept( sock, acceptHandle ); if ( ec ) { cout << "There is an error in server. code: " << ec.value( ) << endl; } return service.run( );// 阻塞运行 }

    运行结果是这样的:
    78448d7b-b3ae-42fc-9e2e-4dd2fbdac2c2-image.png

    我对boost.asio中几个概念的理解:

    io_service,这就是一个类似事件循环的东西,它为io设备提供服务,故名。不管是套接字、文件还是串口设备,都要使用它的服务。它的run()函数相当于启动了一个事件循环。一旦有消息了,即进行响应。这也是实现异步编程的重要基础。 socket,这个类则是套接字,可以处理TCP或者是UDP请求。有同步以及异步的处理方式,也有带异常以及不带异常的处理方式。 acceptor,接收器,仅仅是服务端使用。相当于其余框架中的listener,作接收用的。

    比较浅显,如果有不当之处,敬请指正。

    read more

关注我们

微博
QQ群