您现在的位置是:网站首页> C/C++

QT学习总结

  • C/C++
  • 2022-03-29
  • 1436人已阅读
摘要
QT账户

123Wasd

目录:

QT pro平台

QT读ini

Qt http post 表单访问数据

QT典型登录进入主界面

Qt中QDialog窗口的返回值设置

特别重要的JAVA交互类QAndroidJniObject

Qt调用Android相册获取图片

Qt中获取Android手机的IMEI

Qt c++ java android 相互调用

QML中文件的加载(三种方法)

QT 动态加载资源 .qrc文件

QT线程

QT线程同步

QT等待且不卡界面的方法

QT加C头文件和加库

QT的容器类

QT打印功能

qt for android + opencv的配置及使用

Qt在IOS平台编译配置以及打包发布

Qt on Mac 开发之 Qt程序加入Objective-C代码(包含如何查找Objective-C的库)

QT IOS开发相关博客

QT运行Objective-C代码

QListView 自定义delegate和model, 添加checkbox、按钮、文本

QListView vs QListWidget

Qml中ListView GridView Repeater中Model使用

QListWidgetItem自定义添加控件

Qt listWidget通过setItemWidget添加widget后无法捕获鼠标的解决方法

Qt中添加java文件

Qt显示窗口

Qt在pro里添加资源文件如qml文件

Qt使用COM组件

使用QT5创建动态库以及如何在程序运行时加载动态库

IOS开发系列—Objective-C之基础概览 

IOS开发系列--Objective-C之类和对象 

IOS开发系列--Objective-C之协议、代码块、分类

Qt小技巧14.Qt5.12.x编译Mysql插件驱动 

自定义的QListWidget

QListWidget自定义item的两种方式


iOS播放视频

若只是简单的在一个页面加载播放视频,没啥复杂的功能,就可用qt自带的 MediaPlayer实现,若视频地址是不安全的http,记得在info.plist配置哦~


iOS显示网页或者富文本

iOS app若是要显示网页或者展示富文本,建议用Webview。若是超简单的富文本,可选用Text,稍复杂的还是建议用Webview。


iOS微信登录(加载其他第三方库或框架)

用Qt开发iOS的真的是非常不多见,可以说是罕见了,所以一般第三方是不会专门针对Qt开发而出一套东西的,我们一般找到的都是针对swift或者objective-c而出的代码。那么我们真的就没办法了吗?不是的,你想既然Qt可以跨平台到iOS,他就一定有办法实现。

其实,这个用到了Objective-c特有的一个东西——分类(Category)。这个特性对于整体的实现起到了关键性 的作用。当然,在微信的加载部分,我还遇到了各种细节的坑,实在不易。


调用iOS系统函数(objective-c混编)

混编这部分,我是参考的github的一个代码GitHub - jpnurmi/qtstatusbar: StatusBar for Qt allows setting the status bar color and theme on Android and iOS.


它写了一个关于状态栏颜色的设置,虽然我开发时并不需要设置状态栏,但是里面涉及了一些知识点:比如注册qml类型,对于多个平台(安卓或ios),代码的封装及层次的设置等,这个代码算是对我的一点启蒙吧,比之前我看的各种混编啥的实用的多,也直观的多,若是用qt开发ios,建议稍微看看。


pro各平台

Qt开发中如何正确的编写.pro文件及详细说明

使用过QT框架进行项目开发的小伙伴,也许都知道.pro文件, 但文件里面的具体配置方式可能比较模糊。本文将详细说明文件里各配置项的含义,并对其编写方法进行归纳总结,以便各位读者可以在未来的项目开发中快速而准确地编写.pro文件。


1.配置项详细说明

注释:注释是从一行的#开始,到这一行的结束。

QT += :这个是添加QT项目需要的模块的,若项目中要排除某个模块,也可用QT -=配置项。

TEMPLATE = : 这个配置项确定qmake为这个应用程序生成哪种makefile 。有下面五种形式可供选择:

app:建立一个应用程序的makefile,这个是默认值,若模块项未指定,将默认使用此项;

lib:建立一个库的makefile;

vcapp:建立一个应用程序的VisualStudio项目文件;

vclib:建立一个库的VisualStudio项目文件;

subdirs:这是一个特殊的模板,可以创建一个可进入特定目录并为一个项目文件生成makefile,此makfile可以调用make;

4. TARGET = :这个配置项用来指定最后生成的目标应用程序的名称。


5. CONFIG += : 用来告诉qmake关于应用程序的配置信息,使用+=表示在现有的配置上添加,这样会更安全。比如,CONFIG += qt warn_on release 其具体的意义为:


qt :告诉qmake此程序是使用qt来连编的。即qmake在连接、为编译添加所需包含路径时会考虑qt的库;

warn_on :告诉qmake要将编译器设置为输出警告信息形式;

release :告诉qmake应用程序必须被连编为一个可发布的应用程序。开发过程中,也可以使用debug;

6. UIC_DIR += :用来指定uic命令,将.ui文件转化为ui_*.h文件存放的目录。


7. RCC_DIR += :用来指定rcc命令,将.qrc文件转换成qrc_*.h文件存放的目录。


8. MOC_DIR += :用来指定moc命令,将含有Q_OBJECT的头文件转换成标准.h文件存放的目录。


9. OBJECTS_DIR += :用来指定目标文件obj的存放目录。


10. DEPENDPATH += : 用来指定工程的依赖路径。


11. INCLUDEPATH += : 用来指定工程所需要的头文件。


12. CODECFORSRC += : 用来指定源文件的编码格式。


13. FORMS += :用来指定工程中的ui文件。


14. HEADERS += : 用来指定工程中所包含的头文件。


15. SOURCES += :用来指定工程中包含的源文件。


16. RESOURCES += :用来指定工程中所包含的资源文件。


17. LIBS += :用来指定引入的lib文件的路径,一般会在前面加下参数-L,根据不同的版本可以分为两种形式:


Release: LIBS += -L folder Path //release版本引入的lib文件

Debug: LIBS += -L folder Path //debug版本引入的lib文件

18. DEFINES += : 用来定义编译选项。


19. DESTDIR += :用来指定目标的生成路径。


20. 跨平台处理信息也要写在.pro文件中。 其示例如下:


win32{



unix{



android

{


}


ios

{

}

2. .pro文件配置实例

 //添加QT依赖的库

QT += gui

QT += core xml network multimedia serialport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

 

//添加c11配置支持

CONFIG += c++11

//输出文件的名称

TARGET = YouAppName

//配置控制台输出

CONFIG += console

//输出类型application

TEMPLATE = app

 

//源文件

SOURCES += main.cpp \

    appconfig.cpp \

    opendoorthread.cpp \

    TestProject/testform.cpp \

    TestProject/common.pb.cpp \

    TestProject/goods_req.pb.cpp \

    TestProject/goods_resp.pb.cpp

 

//头文件

HEADERS += \

    appconfig.h \

    opendoorthread.h \

    TestProject/testform.h \

    TestProject/common.pb.h \

    TestProject/goods_req.pb.h \

    TestProject/goods_resp.pb.h

 

//配置debug和release

CONFIG +=debug_and_release

CONFIG(debug,debug|release){

DESTDIR += $$PWD/debug

LIBS += -L$$PWD/debug/ -lThorModel

LIBS += -L$$PWD/debug/ -lThorUtil

LIBS += -L$$PWD/debug/ -lThorBLL

LIBS += -L$$PWD/debug/ -lThorHardwareUtil

LIBS += -L$$PWD/debug/ -lprotobufd

LIBS += -L$$PWD/debug/ -lprotobuf-lited

LIBS += -L$$PWD/debug/ -lopencv_core2410d

LIBS += -L$$PWD/debug/ -lopencv_highgui2410d

LIBS += -L$$PWD/debug/ -lopencv_imgproc2410d

LIBS += -L$$PWD/debug/ -lQtActionDetectd

}else{

}

//需要的头文件

INCLUDEPATH += $$PWD/AllDLL/include

INCLUDEPATH += $$PWD/debug/3rdparty/opencv-2.4.10/include \

            $$PWD/debug/3rdparty/opencv-2.4.10/include/opencv \

            $$PWD/debug/3rdparty/opencv-2.4.10/include/opencv2

//ui

FORMS += \

    TestProject/testform.ui

qmake非常方便、快捷,是一个轻量级的makefile生成工具,在使用该指令前要正确地编写.pro文件。

$$PWD的意思为当前目录

如pro文件中:INCLUDEPATH += $$PWD/lib/x64-win64

$$PWD表示的意思就是pro文件所在的目录


QT读ini文件

QMap<QString, QString> MyData::readConfigFile()

{

    QMap<QString,QString> dataMap;

    //qDebug()<<qApp->applicationDirPath();

 

    //QStandardPaths::AppDataLocation;

    m_strPath=QStandardPaths::writableLocation(QStandardPaths::CacheLocation);

    //qDebug()<<m_strPath<<QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);

    QString fileName="config.ini";

    QFile tempFile;

    QDir tempDir;

    QString currentDir = tempDir.currentPath();

    qDebug()<<m_strPath;

    tempDir.setCurrent(m_strPath);

    //检查m_strPath路径下是否存在文件fileName,如果停止操作。

    if(!tempFile.exists(fileName))

    {

        tempFile.setFileName(fileName);

        if(tempFile.open(QIODevice::WriteOnly|QIODevice::Text))

        {

            QFile resFile(":/res/config.ini");

            resFile.open(QIODevice::ReadOnly);

            QByteArray data=resFile.readAll();

            tempFile.write(data);

            resFile.close();

        }

    }

    tempFile.close();

    tempDir.setCurrent(currentDir);

    QSettings* config=new QSettings(m_strPath+"/config.ini",QSettings::IniFormat);

 

    QString domain=config->value("/net/domain").toString();;

    dataMap.insert("domain",domain);

 

 

 

    delete config;

    return dataMap;

}



Qt http post 表单访问数据——Qt

QString post_data="type1=1&type2=2";

 

    QNetworkRequest requst;

    //QString header="application/json";

    QString header="application/x-www-form-urlencoded";

    requst.setHeader(QNetworkRequest::ContentTypeHeader,header);

    requst.setHeader(QNetworkRequest::ContentLengthHeader,post_data.length());

 

    requst.setUrl(QUrl(url));

    QNetworkReply* reply =QNetworkAccessManager::post(requst,post_data);



void NetCpp::syncPostOfFormData(QString url, QMap<QString,QString> mapData, int num)

{

    QByteArray post_data;

    QString strAnd="&";

    QMap<QString,QString>::iterator it;

    for(it=mapData.begin(); it!=mapData.end();it++)

    {

        QString key=it.key();

        QString val=it.value();

 

        QString str=key+"="+val+strAnd;

        post_data.append(str);

    }

 

    QNetworkRequest requst;

    //QString header="application/json";

    QString header="application/x-www-form-urlencoded";

    requst.setHeader(QNetworkRequest::ContentTypeHeader,header);

    requst.setHeader(QNetworkRequest::ContentLengthHeader,post_data.length());

 

    requst.setUrl(QUrl(url));

 

    requst.setAttribute(QNetworkRequest::User,num);

 

    QNetworkReply* reply =QNetworkAccessManager::post(requst,post_data);

    QEventLoop loop;

    connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));

    loop.exec();

}


QT典型登录进入主界面

 int main(int argc,char *argv[])

{

  QApplication a(argc,argv);

QDlgLogin *dlgLogin=new QDlgLogin;

if(dlgLogion->exec()==QDialog::Accepted)

{

 QWMainWindow w;

 w.show();

return a.exec();

}

else

{

  return 0;

}

}


Qt中QDialog窗口的返回值设置

当我们在使用QDialog的窗口时,需要点击确定或者取消按钮,关闭该窗口且通知使用者

1.png

用法很简单,点击确定按钮,代码如下


done(Accepted); //确定

点击取消按钮时,代码如下


done(Rejected); //取消

外部调用窗口的使用


QTestDialog dlg;

int nResult = dlg.exec();

if(nResult == QDialog::Accepted) //点击了确定按钮

{

    //代码实际处理

}

else

{

    //代码实际处理

}

 


特别重要的JAVA交互类QAndroidJniObject,具体看查阅QAndroidJniObject资料

QAndroidJniEnvironment env;
QAndroidJniObject action = QAndroidJniObject::fromString("com.demo.testactivity.MainActivity");
 QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>());
QtAndroid::startActivity(intent, 0);
CHECK_EXCEPTION()//这里可有可无,但是人家大佬都这么用了


QAndroidJniObject stringNumber = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass",

                                                                             "fromNumber"

                                                                             "(I)Ljava/lang/String;",

                                                                             10);

QAndroidJniObject string1 = QAndroidJniObject::fromString("String1");

QAndroidJniObject string2 = QAndroidJniObject::fromString("String2");

QAndroidJniObject stringArray = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass",

                                                                            "stringArray"

                                                                            "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"

                                                                             string1.object<jstring>(),

                                                                             string2.object<jstring>()); 


Java implementation:


  class FooJavaClass

  {

      public static void foo(int x)

      {

          if (x < 100)

              callNativeOne(x);

          else

              callNativeTwo(x);

      }


  private static native void callNativeOne(int x);

  private static native void callNativeTwo(int x);


  }


C++ Implementation:


  static void fromJavaOne(JNIEnv *env, jobject thiz, jint x)

  {

      Q_UNUSED(env)

      Q_UNUSED(thiz)

      qDebug() << x << "< 100";

  }


  static void fromJavaTwo(JNIEnv *env, jobject thiz, jint x)

  {

      Q_UNUSED(env)

      Q_UNUSED(thiz)

      qDebug() << x << ">= 100";

  }


  void registerNativeMethods() {

      JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)},

                                 {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}};


      QAndroidJniObject javaClass("my/java/project/FooJavaClass");

      QAndroidJniEnvironment env;

      jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());

      env->RegisterNatives(objectClass,

                           methods,

                           sizeof(methods) / sizeof(methods[0]));

      env->DeleteLocalRef(objectClass);

  }


  void foo()

  {

      QAndroidJniObject::callStaticMethod<void>("my/java/project/FooJavaClass", "foo", "(I)V", 10);  // Output: 10 < 100

      QAndroidJniObject::callStaticMethod<void>("my/java/project/FooJavaClass", "foo", "(I)V", 100); // Output: 100 >= 100

  }



C++中可以通过建立虚拟机使用java,Qt中当然也可以用同样的方法;

为了简单Qt提供了QAndroidJniObject 简化了对java的应用;后面举例简单示例:

//MyJavaClass.java文件

package org;

import java.io.OutputStream;

import java.util.Iterator;

import java.util.List;

import android.app.Activity;

import android.content.ContentValues;

import android.content.res.Configuration;

import android.hardware.Camera;

import android.net.Uri;

import android.os.Bundle;

import android.provider.MediaStore;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.View;

import android.widget.LinearLayout;

public class MyJavaClass

{

    public static int fibonacci(int n)             //静态函数;

    {

       return 1234567890;

    }

    public int getNum()                            //没有参数;

    {

        return 1357902468;

    }

    public int setNum( int n )                     //没有参数;

    {

        return n;

    }

    public String getStr()                        //返回字符串

    {

        String teststr = new String("this is a test java string; 测试文本");

        return teststr;

    }


}


C++中的应用方法:


// C++ code

{

     //类中的静态函数:

    {

        return QAndroidJniObject::callStaticMethod<jint>

                            ("org/MyJavaClass" // class name

                            , "fibonacci" // method name

                            , "(I)I" // signature

                            , n);

    }

     

    //类中的无参数,有返回值函数:

    {

        QAndroidJniObject QAndroidJniObject_MyJavaClass_obj("org/MyJavaClass");

        jint n = QAndroidJniObject_MyJavaClass_obj.callMethod<jint>("getNum");

        return n;

    }

    

    //类中有参数又返回值函数;

    {

        QAndroidJniObject QAndroidJniObject_MyJavaClass_obj("org/MyJavaClass");

        jint n = QAndroidJniObject_MyJavaClass_obj.callMethod<jint>("setNum", "(I)I", 12345 );

        return n;

    }

    {

      //从这个示例看出,jstring是不可以直接返回的: 

      // 1:返回的是 QAndroidJniObject ,可以通过QAndroidJniObject 的toString()获取QString;

      //  2:   const char *pstr= env->GetStringUTFChars(jstringtemp;

            //3:网上还有一些jstring转char*的函数,这里就不说明了;

 

        QAndroidJniObject VideoCaptureAndroid_JavaObj("org/MyJavaClass");

        QAndroidJniObject stringJinObj = VideoCaptureAndroid_JavaObj.callObjectMethod<jstring>("getStr");

        QAndroidJniEnvironment env;

        jstring jstringtemp = stringJinObj.object<jstring>();

       // const char *pstr= env->GetStringUTFChars(jstringtemp, 0); //获取jstring to char* 方法1;

        QString str(pstr);

        QMessageBox mbox;

        mbox.setText( stringJinObj.toString() ); //jstring to QString;

        mbox.exec();

    }


}



1、震动效果

  震动效果主要调用了android的vibrate服务,对应android.os.Vibrator类。切记添加android.permission.VIBRATE权限


void Widget::onVibrate()

{

#ifdef Q_OS_ANDROID

    QAndroidJniObject activity = androidActivity();

    QAndroidJniObject name = QAndroidJniObject::getStaticObjectField(

                "android/content/Context",

                "VIBRATOR_SERVICE",

                "Ljava/lang/String;"

                );

 

    QAndroidJniObject vibrateService = activity.callObjectMethod(

                "getSystemService",

                "(Ljava/lang/String;)Ljava/lang/Object;",

                name.object<jstring>());

 

    jlong duration = 200;

    vibrateService.callMethod<void>("vibrate", "(J)V", duration);

#endif

}

2、屏幕旋转

  屏幕旋转无需添加任何权限,下面展示的是手动控制自旋,对应android.app.Activity 类,设备感应旋转的后续添加


void Widget::onScreenOrientation()

{

#ifdef Q_OS_ANDROID

    QAndroidJniObject activity = androidActivity();

    jint orient = activity.callMethod<jint>("getRequestedOrientation");

 

    orient = (1 == orient)? 0:1;

    activity.callMethod<void>( "setRequestedOrientation", "(I)V", orient);

#endif

}

3、铃声模式

  来电铃声有三种基本模式,分别是响铃、震动和静音,对应着android.media.AudioManager 类


void Widget::onSetRingMode(int iMode)

{

#ifdef Q_OS_ANDROID

    QAndroidJniObject activity = androidActivity();

    QAndroidJniObject name = QAndroidJniObject::getStaticObjectField(

                                "android/content/Context",

                                "AUDIO_SERVICE", "Ljava/lang/String;");

 

    QAndroidJniObject audioService = activity.callObjectMethod(

                                "getSystemService",

                                "(Ljava/lang/String;)Ljava/lang/Object;",

                                name.object<jstring>());

 

    audioService.callMethod<void>( "setRingerMode", "(I)V", iMode);

 

    //获取当前铃声模式

    //iMode = audioService.callMethod<jint>("getRingerMode", "()I");

#endif

4、屏幕常亮

  这个功能主要用于一些类似播放视频不息屏或者音乐播放歌词等场景,主要是用android的电源管理类ndroid.os.PowerManager,这个需要添加权限WAKE_LOCK和DEVICE_POWER


void Widget::onKeepScreenLight(int iStatus)

{

#ifdef Q_OS_ANDROID

    if(Qt::Unchecked == iStatus)

    {

        if(m_wakeLock.isValid())

        {

            m_wakeLock.callMethod<void>("release");

            CHECK_EXCEPTION();

        }

        return;

    }

 

    QAndroidJniObject name = QAndroidJniObject::getStaticObjectField(

                "android/content/Context",

                "POWER_SERVICE",

                "Ljava/lang/String;"

                );

 

    QAndroidJniObject activity = androidActivity();

    QAndroidJniObject powerService = activity.callObjectMethod(

                "getSystemService",

                "(Ljava/lang/String;)Ljava/lang/Object;",

                name.object<jstring>());

   

    QAndroidJniObject tag = QAndroidJniObject::fromString("QtJniWakeLock");

    m_wakeLock = powerService.callObjectMethod(

                "newWakeLock",

                "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;",

                10,

                tag.object<jstring>()

                );

  

 

    if(m_wakeLock.isValid())

    {

        m_wakeLock.callMethod<void>("acquire");

    }

#endif

}


Qt调用Android相册获取图片

原文

Qt程序要访问android默认相册,必须要通过调用android API来完成,这就涉及到c++调用java的问题。

java给我们提供了一套叫做JNI(Java Native Interface)的接口,帮助其他语言(主要是c和c++)与java进行交互。我们不会用到很多JNI的知识,所以就直接跳过啦,JNI的详细内容可以查看JNI的官方文档。

下面重点介绍Qt给我们提供的和android API进行交互的工具。


QAndroidJniObject

QAndroidJniObject类属于androidextras模块,所以include之前要记得在.pro文件中添加QT += androidextras。

关于该类的介绍,Qt官方文档和下面这篇文章写的很清楚,不再赘述。

QtAndroid详解(1):QAndroidJniObject

PS:博主关于Qt的文章真心不错,细致而全面,推荐收藏。


调用相册

先上代码:



    QAndroidJniObject action = QAndroidJniObject::fromString("android.intent.action.GET_CONTENT");

    QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>());

    QAndroidJniObject image = QAndroidJniObject::fromString("image/*");

    intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", image.object<jstring>());

    receiver = new ResultReceiver(1);

    connect(receiver, &ResultReceiver::sendData, this, &MainWindow::handleData);

    QtAndroid::startActivity(intent, 1, receiver);

    QAndroidJniEnvironment env;

    if(env->ExceptionCheck())

    {

        qDebug() << "exception occured";

        env->ExceptionDescribe();

        env->ExceptionClear();

    }



这段代码中最主要的就是上面提到的QAndroidJniObject 类,我们逐行分析。

第一行,使用fromString方法构造了一个java string实例,内容是"android.intent.action.GET_CONTENT",这是intent的一个action,也就是告诉android我们想要做的事情,这里我们需要获取内容(图片,视频,音频)。不了解intent的同学可以读一下这篇文章,更多action的种类可以看这篇文章可以大概了解以下就可以啦,暂时不必深究。

第二行构造了一个java类(android/content/Intent),并且以刚刚构造的字符串为参数。

第七行,启动这个intent。为什么直接到第七行了?如果是不需要设置和返回值的action,也就是说,我只需要android系统帮我用默认的方式执行这个action,执行完了也不用通知我,比如打开系统设置界面,第二行结束后就可以直接启动了。关于QtAndroid::startActivity方法,这篇文章讲的很详细。

我们要调用相册获取图片,第一需要设置,不然系统不知道我们要的到底是什么类型的文件(是图片、音频,还是视频?),第二需要返回,获取图片的路径才是我们的最终目的,没有返回是不行的。

所以,第三行,我们构造了一个字符串"image/*"表示我们需要iamge类型(为什么是这样一个字符串,大家可以查以下mime标准)。

第四行,调用Intent类的setType函数,这个函数接收一个表示类型的字符串(我们传入的就是第三行构造的"image/*"),返回一个Intent实例,所以需要用callObjectMethod方法调用。

第五、六行,在堆上创建了一个ResultReceiver类,并将它的信号和MainWindow的槽连起来。

这个ResultReceiver类是我们自己实现的,它实现了QAndroidActivityResultReceiver接口,Intent执行完毕后(这里就是用户选完图片后)会调用handleActivityResult并将结果通过data参数返回。我们在handleActivityResult中提取出文件的本地路径,并通过信号槽发送出去。

如下:



//.h

class ResultReceiver: public QObject, public QAndroidActivityResultReceiver

{

    Q_OBJECT

signals:

    void sendData(QString);

public:

    int requestId;

    ResultReceiver(int id, QObject *parent = nullptr) : QObject(parent), requestId(id) {}

    void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data);

};


//.cpp

void ResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)

{

    if(receiverRequestCode == requestId)

    {

        if(resultCode == RESULT_OK)

        {

            QAndroidJniObject url = data.callObjectMethod("getDataString", "()Ljava/lang/String;");

            qDebug() << url.isClassAvailable("java/lang/String");

            qDebug() << "string: "<< url.toString();

            //提取本地文件路径

            QAndroidJniObject uri = data.callObjectMethod("getData", "()Landroid/net/Uri;");

            QAndroidJniObject dadosAndroid = QAndroidJniObject::getStaticObjectField("android/provider/MediaStore$MediaColumns", "DATA", "Ljava/lang/String;");

            QAndroidJniEnvironment env;

            jobjectArray projecao = (jobjectArray)env->NewObjectArray(1, env->FindClass("java/lang/String"), NULL);

            jobject projacaoDadosAndroid = env->NewStringUTF(dadosAndroid.toString().toStdString().c_str());

            env->SetObjectArrayElement(projecao, 0, projacaoDadosAndroid);

            QAndroidJniObject contentResolver = QtAndroid::androidActivity().callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");

            QAndroidJniObject cursor = contentResolver.callObjectMethod("query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", uri.object<jobject>(), projecao, NULL, NULL, NULL);

            jint columnIndex = cursor.callMethod<jint>("getColumnIndex", "(Ljava/lang/String;)I", dadosAndroid.object<jstring>());

            cursor.callMethod<jboolean>("moveToFirst", "()Z");

            QAndroidJniObject resultado = cursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);

            emit sendData(resultado.toString());

        }

        else

        {

            //some code here

        }

    }

}



提取本地路径部分对应如下java 代码:



Uri uri = data.getData();

String[] proj = { MediaStore.Images.Media.DATA };

Cursor actualimagecursor = managedQuery(uri,proj,null,null,null);

int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

actualimagecursor.moveToFirst();

String img_path = actualimagecursor.getString(actual_image_column_index);

File file = new File(img_path);

看起来有点复杂,其实就是调用了几个androidAPI的方法而已。只不过JNI的混编对于像我这种不熟悉的人来说形式上比较陌生,习惯以后就能看到陌生代码下的熟悉“本体”了。

混编的代码调试起来比较麻烦,如果能先写好android的activity,多封装代码,减少混编代码量是最好的。


Qt中获取Android手机的IMEI。

#include <QString>

#include <QAndroidJniEnvironment>

#include <QandroidJniObject>

QString getDeviceImei(){

QAndroidJniEnvironment env; 

jclass contextClass = env->FindClass("android/content/Context");

jfieldID fieldId = env->GetStaticFieldID(contextClass, "TELEPHONY_SERVICE", "Ljava/lang/String;"); 

jstring telephonyManagerType = (jstring) env->GetStaticObjectField(contextClass, fieldId);

jclass telephonyManagerClass = env->FindClass("android/telephony/TelephonyManager");

jmethodID methodId = env->GetMethodID(contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); 

QAndroidJniObject qtActivityObj =QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");   jobject telephonyManager = env->CallObjectMethod(qtActivityObj.object<jobject>(), methodId, telephonyManagerType);  

methodId = env->GetMethodID(telephonyManagerClass, "getDeviceId", "()Ljava/lang/String;");  

jstring jstr = (jstring) env->CallObjectMethod(telephonyManager, methodId);  

jsize len = env->GetStringUTFLength(jstr);  

char* buf_devid = new char[32];  

env->GetStringUTFRegion(jstr, 0, len, buf_devid);  

QString imei(buf_devid);  

delete buf_devid;  

return imei;


}


数据类型签名对照表:


jobject

Ljava/lang/Object;


jclass

Ljava/lang/Class;


jstring

Ljava/lang/String;


jthrowable

Ljava/lang/Throwable;


jobjectArray

[Ljava/lang/Object;


jarray

[<type>


jbooleanArray

[Z


jbyteArray

[B


jcharArray

[C

jshortArray

[S


jintArray

[I


jlongArray

[J


jfloatArray

[F


jdoubleArray

[D


Primitive Types



jboolean

Z


jbyte

B


jchar

C


jshort

S


jint

I

jlong

J


jfloat

F


jdouble

D



void

V

Custom type

L<fully-qualified-name>;


函数描述如:

(输入参数1;输入参数2;)返回类型;是传的要加;

(I)V

(Ljava/lang/String;)Ljava/lang/Object;

(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;


JAVA中的String

String是一个类,在JAVA中非常常用,和C++交互也很常用.QT封装了他,只需要调用:

QAndroidJniObject::fromString("字符串");



返回一个String对象,使用的时候JNI识别jstring,需要强制转换:

对象.object<jstring>());  转换成jstring


QtAndroid::androidActivity()  

QtAndroid::androidActivity() 返回当前安卓层的Activity对象。


调用动态对象的动态方法:

对象.callMethod<返回值类型>("方法名","参数类型签名",参数)

调用安卓窗口中封装的Toask函数。

//JAVA层

public class QtActivity extends Activity

{

....

...

..

void  QMessage(String s)

    {

        Toast  toast =  Toast.makeText(this, s, 500);

        toast.show();


    }


}

//C++调用

 void MyJni::QMessage(QString s)

 {


     QtAndroid::runOnAndroidThread([=](){

      //在UI线程调用


         QAndroidJniEnvironment   EV;

          QAndroidJniObject activity =QtAndroid::androidActivity();

         QAndroidJniObject str =QAndroidJniObject::fromString(s);

          activity.callMethod<void>("QMessage","(Ljava/lang/String;)V",str.object<jstring>());

          qDebug()<<"WAIT*****END";


     });


 }

或使用静态方法获取java对象,然后调用之:

c++:

 void MyJni::QMessage(QString s)

 {

     QtAndroid::runOnAndroidThread([=](){ 

         QAndroidJniEnvironment   EV;

         QAndroidJniObject str =QAndroidJniObject::fromString(s);

          QAndroidJniObject selfobj = QAndroidJniObject::callStaticObjectMethod<jobject>("org/qtproject/qt5/android/bindings/QtActivity", "getobj");

          selfobj.callMethod<void>("QMessage","(Ljava/lang/String;)V",str.object<jstring>());


     });


 }

JAVA:

 public static QtActivity selfObj =null;

    public QtActivity()

    {

        selfObj = this;

        m_loader = new QtActivityLoader(this);

        if (Build.VERSION.SDK_INT >= 21) {

            QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"};

            QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light";

        } else {

            QT_ANDROID_THEMES = new String[] {"Theme_DeviceDefault_Light"};

            QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_Light";

        }

    }

public  static  Object getobj(){

        return selfObj;

    }


JNI字段描述符“([Ljava/lang/String;)V” "(II)V" 表示 void Func(int, int);

“([Ljava/lang/String;)V” 它是一种对函数返回值和参数的编码。这种编码叫做JNI字段描述符(JavaNative Interface FieldDescriptors)。一个数组int[],就需要表示为这样"[I"。如果多个数组double[][][]就需要表示为这样 "[[[D"。也就是说每一个方括号开始,就表示一个数组维数。多个方框后面,就是数组 的类型。


如果以一个L开头的描述符,就是类描述符,它后紧跟着类的字符串,然后分号“;”结束。


比如"Ljava/lang/String;"就是表示类型String;


"[I"就是表示int[];


"[Ljava/lang/Object;"就是表示Object[]。


JNI方法描述符,主要就是在括号里放置参数,在括号后面放置返回类型,如下:


(参数描述符)返回类型


当一个函数不需要返回参数类型时,就使用”V”来表示。


比如"()Ljava/lang/String;"就是表示String f();


"(ILjava/lang/Class;)J"就是表示long f(int i, Class c);


"([B)V"就是表示void String(byte[] bytes);



Java 类型


符号


Boolean


Z


Byte


B


Char


C


Short


S


Int


I


Long


J


Float


F


Double


D


Void


V


objects对象


以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。比如:Ljava/lang/String;如果是嵌套类,则用$来表示嵌套。例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"


另外数组类型的简写,则用"["加上如表A所示的对应类型的简写形式进行表示就可以了,


比如:[I 表示 int [];[L/java/lang/objects;表示Objects[],另外。引用类型(除基本类型的数组外)的标示最后都有个";"


例如:


"()V" 就表示void Func();


"(II)V" 表示 void Func(int, int);


"(Ljava/lang/String;Ljava/lang/String;)I".表示 int Func(String,String)



Qt c++ java android 相互调用

在这纠正 foruok 这个SB 的教程


java调用QT-----------------------------------------------------------------------------------------------------------------------------


** Java QtActivity 代码**


    public static native int sendVideoData(long unused, byte[] data, int len, long timestamp);

    public static native int sendCh340xData(byte[] data, int len);


c++ 导出函数


qt 会生成 so 文件 直接导出 只要导出函数签名一致 java 就能 调用 不需要调用 System.loadLibrary("")

#if defined(Q_OS_ANDROID)

#include <jni.h>


#ifdef __cplusplus

extern "C" {

#endif

JNIEXPORT jint JNICALL

Java_org_qtproject_qt5_android_bindings_QtActivity_sendCh340xData(

    JNIEnv *env, jclass type,

        jbyteArray data_, jint len)

{

    jbyte *data = env->GetByteArrayElements(data_, NULL);


    QByteArray buff;

    buff.resize(len);

    memcpy(buff.data(), (uint8_t *)data, len);

    QMetaObject::invokeMethod(g_cH34xWidget, "onData", Qt::QueuedConnection,    Q_ARG(QByteArray, buff));


    env->ReleaseByteArrayElements(data_, data, 0);


    return 0;

}

#ifdef __cplusplus

}

#endif


#if defined(Q_OS_ANDROID)

#include <jni.h>


#ifdef __cplusplus

extern "C" {

#endif

JNIEXPORT jint JNICALL

Java_org_qtproject_qt5_android_bindings_QtActivity_sendVideoData(

    JNIEnv *env, jclass type, jlong cptr,

    jbyteArray data_, jint len, jlong timestamp)

{

    jbyte *data = env->GetByteArrayElements(data_, NULL);


    uint8_t *buf = (uint8_t *)data;

    TcpClient::instance()->sendVideo((uint8_t *)buf, len);


    env->ReleaseByteArrayElements(data_, data, 0);


    return 0;

}

#ifdef __cplusplus

}

#endif


#endif


qt调用java-----------------------------------------------------------------------------------------------------------------------------


java代码



    public void SetAvcEncoderOptions(int width, int height, int bitrate, int fps) {

        Log.d("JAVA: SetAvcEncoderOptions", String.format("(%dx%d, %dkbps, %dfps)", width, height, bitrate / 1000, fps));

    }

    

    public void AvcEncodeData(byte[] data, int width, int height) {

        Log.d("JAVA: AvcEncodeData()", String.format("buffer == %d", data.length));

    }

c++


    #if defined(Q_OS_ANDROID)

    QtAndroid::androidActivity().callMethod<void>("SetAvcEncoderOptions", "(IIII)V", 640, 480, 600000, 16);

    #endif


#if defined(Q_OS_ANDROID)

#include <QtAndroid>

JavaVM *g_javaVM = NULL; // 保存虚拟机

JNIEXPORT jint JNI_OnLoad(JavaVM *vm,void * reserved)

{

    //qDebug() << Q_FUNC_INFO;

    //av_jni_set_java_vm(vm, reserved);

    g_javaVM = vm;

    return JNI_VERSION_1_6;

}

#endif


#if defined(Q_OS_ANDROID)

    JNIEnv* env = NULL;

    if(g_javaVM->AttachCurrentThread(&env,NULL) < 0)

    {

        qDebug("Fail to AttachCurrentThread");

        return;

    }


    QElapsedTimer t;

    t.restart();

    

    const int mapped = width*height*3/2;

    const int width = 1920;

    const int height = 1080;

    

    jbyteArray pixmap = env->NewByteArray(mapped);

    env->SetByteArrayRegion(pixmap, 0, mapped, (const jbyte *)  传递数组指针 );

    

    QtAndroid::androidActivity().callMethod<void>("AvcEncodeData", "([BII)V", pixmap, width, height); //调用 java 函数 传递数组

    

    g_javaVM->DetachCurrentThread();


    qDebug() << "Send buffer to java elapsed:" << t.elapsed();

#endif




根据以上代码调用第三方的JAR so可以封装成Java类后由QT调用

获得当前Activity

public static Activity getCurrentActivity () {

    try {

        Class activityThreadClass = Class.forName("android.app.ActivityThread");

        Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(

                null);

        Field activitiesField = activityThreadClass.getDeclaredField("mActivities");

        activitiesField.setAccessible(true);

        Map activities = (Map) activitiesField.get(activityThread);

        for (Object activityRecord : activities.values()) {

            Class activityRecordClass = activityRecord.getClass();

            Field pausedField = activityRecordClass.getDeclaredField("paused");

            pausedField.setAccessible(true);

            if (!pausedField.getBoolean(activityRecord)) {

                Field activityField = activityRecordClass.getDeclaredField("activity");

                activityField.setAccessible(true);

                Activity activity = (Activity) activityField.get(activityRecord);

                return activity;

            }

        }

    } catch (ClassNotFoundException e) {

        e.printStackTrace();

    } catch (InvocationTargetException e) {

        e.printStackTrace();

    } catch (NoSuchMethodException e) {

        e.printStackTrace();

    } catch (NoSuchFieldException e) {

        e.printStackTrace();

    } catch (IllegalAccessException e) {

        e.printStackTrace();

    }

    return null;

}



QML中文件的加载(三种方法)

在这里小小总结一下QML文件中如何加载QML文件与JavaScript文件。


1、QML文件中加载JavaScript文件


语法:


import <ModuleIdentifier> <Version.Number> [as <Qualiflier>]


ModuleIdentifier为URL;


Version.Number为版本号;


Qualifier为自定义命名;


示例代码如下:


Qml文件:

1.png


图一

Js文件


1.png

图二

注:将js文件引入后可直接调用里面的函数,自定义命名首字母必须大写,不然后报如下错误:


Invalid import qualifier ID


2、QML文件中加载QML文件


语法:


import <moduleIdentifier> <Version.Number> [as <Qualifier>]


ModuleIdentifier为URL


Version.Number为版本号


Qualifier为自定义命名


示例代码如下:

1.png


Import“qml/”中,qml为文件夹,里面有Monitor.qml和Compass.qml两个文件qml/为文件夹的相对路径


3、js文件中加载js文件:


方法一:


.import "common.js" as Common


用法如同QML文件中加载JavaScript文件


方法二


Qt.include("common.js")


用法如同QML文件中加载QML文件,加载后可直接调用被加载文件中的函数



代码生成组件

property var netCom;

netCom=Qt.createComponent("ImageDigger.qml");

netCom.destory();

netCom=undefined;



使用Qt.createComponent 动态加载组件

import QtQuick 2.0


Item {

id: container

width: 300; height: 300


function loadButton() {

// 定义一个对象 object createComponent(url, mode, parent)

var component = Qt.createComponent("HMButton.qml");

console.log(component.status)

if (component.status == Component.Ready) {

// 指定父对象

var button = component.createObject(container);

button.color = "red";

}

}

// 当加载好组件时 调用动态加载函数

Component.onCompleted: loadButton()

}


QT 动态加载资源 .qrc文件

1、传统资源一般是在.pro文件中加载 RESOURCES += xxx.qrc文件,然后在项目中调用

这种方式的资源是嵌入方式,即编译进了.exe文件中。

2、利用rcc文件方式可以把资源剥离出来,方便替换而不用编译代码

实现方式也简单,只需要把xxx.qrc文件编译成xxx.rcc文件,然后在程序启动时加载.rcc文件,后面的调用方式便和第一种方式一样了。

rcc --binary myapp.qrc -o resource.rcc,如下图(windows下doc命令)



image.png


加载rcc:


int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    bool ret = QResource::registerResource(qApp->applicationDirPath() + "/resource.rcc");

    if(ret == true){

        qDebug()<<"it's ok";

    }

    else

    {

        qDebug()<<"it's not ok";

    }

    MainWindow w;

    w.show();

    return a.exec();

}


QT生成静态库或动态库

QT生成动态库

QT       -= gui


TARGET = AutoDll

TEMPLATE = lib



QT生成静态库

QT       -= gui


TARGET = AutoLib

TEMPLATE = lib

CONFIG += staticlib




QT线程

1、QThread线程基础

void run()函数是线程体函数,用于定义线程的功能。

void start()函数是启动函数,用于将线程入口地址设置为run函数。

void terminate()函数用于强制结束线程,不保证数据完整性和资源释放。

2、线程的优先级

线程的优先级

    QThread线程总共有8个优先级


    QThread::IdlePriority   0 scheduled only when no other threads are running.


    QThread::LowestPriority  1 scheduled less often than LowPriority.


    QThread::LowPriority   2 scheduled less often than NormalPriority.


    QThread::NormalPriority  3 the default priority of the operating system.


    QThread::HighPriority   4 scheduled more often than NormalPriority.


    QThread::HighestPriority  5 scheduled more often than HighPriority.


    QThread::TimeCriticalPriority 6 scheduled as often as possible.


    QThread::InheritPriority   7 use the same priority as the creating thread. This is the default.


    void setPriority(Priority priority) 

3、线程的创建

   void start ( Priority priority = InheritPriority )


    启动线程执行,启动后会发出started ()信号


4、线程的执行

int exec() [protected] 

    进入事件循环并等待直到调用exit(),返回值是通过调用exit()来获得,如果调用成功则返回0。


void run() [virtual protected] 

    线程的起点,在调用start()之后,新创建的线程就会调用run函数,默认实现调用exec(),大多数需要重新实现run函数,便于管理自己的线程。run函数返回时,线程的执行将结束。


5、线程的退出

void quit();


通知线程事件循环退出,返回0表示成功,相当于调用了QThread::exit(0)。


void exit ( int returnCode = 0 );


调用exit后,thread将退出event loop,并从exec返回,exec的返回值就是returnCode。通常returnCode=0表示成功,其他值表示失败。


void terminate ();


    结束线程,线程是否立即终止取决于操作系统。


    线程被终止时,所有等待该线程Finished的线程都将被唤醒。


    terminate是否调用取决于setTerminationEnabled ( bool enabled = true )开关。

6、线程的等待

bool wait ( unsigned long time = ULONG_MAX )


    线程将会被阻塞,等待time毫秒,如果线程退出,则wait会返回。Wait函数解决多线程在执行时序上的依赖。


void msleep ( unsigned long msecs )


void sleep ( unsigned long secs )


void usleep ( unsigned long usecs )


    sleep()、msleep()、usleep()允许秒,毫秒和微秒来区分,但在Qt5.0中被设为public。


    一般情况下,wait()和sleep()函数应该不需要,因为Qt是一个事件驱动型框架。考虑监听finished()信号来取代wait(),使用QTimer来取代sleep()。


7、线程的状态

bool isFinished () const  线程是否已经退出


bool isRunning () const   线程是否处于运行状态


8、线程的属性

Priority priority () const


void setPriority ( Priority priority )


uint stackSize () const


void setStackSize ( uint stackSize )


void setTerminationEnabled ( bool enabled = true )


设置是否响应terminate()函数


9、线程与事件循环

    QThread中run()的默认实现调用了exec(),从而创建一个QEventLoop对象,由QEventLoop对象处理线程中事件队列(每一个线程都有一个属于自己的事件队列)中的事件。exec()在其内部不断做着循环遍历事件队列的工作,调用QThread的quit()或exit()方法使退出线程,尽量不要使用terminate()退出线程,terminate()退出线程过于粗暴,造成资源不能释放,甚至互斥锁还处于加锁状态。


我们从QThread派生出一个类,并重新实现run方法。

 class HelloThread : public QThread

 {

     Q_OBJECT

 private:

     void run();

 };

QThread::start() 将在另一个线程中被调用。

 int main(int argc, char *argv[])

 {

     QCoreApplication app(argc, argv);

     HelloThread thread;

     thread.start();

     qDebug() << "hello from GUI thread " << app.thread()->currentThreadId();

     thread.wait();  // do not exit before the thread is completed!

     return 0;

 }


线程同步

QMutex、QMutexLocker、QReadWriteLocker

Qt提供了QMutexLocker类何以简化互斥量的处理,它在构造函数中接受一个QMutex对象作为参数并将其锁定,在析构函数中解锁这个互斥量。

bool Thread::stop()  

         {  

             QMutexLocker locker(&mutex);  

             m_stop = true;  

             return m_stop;  

        } 


MyData data;  

 QReadWriteLock lock; 

void ReaderThread::run()  

{  


        ...  


        lock.lockForRead();  

        access_data_without_modifying_it(&data);  

         lock.unlock();  

       ...  


  }  


void WriterThread::run()  

{  

...

  lock.lockForWrite();  

   modify_data(&data);  

   lock.unlock();  

  ...  

 } 


QSemphore

acquire(n)函数用于获取n个资源,当没有足够的资源时调用者将被阻塞直到有足够的可用资源。release(n)函数用于释放n个资源。

QSemaphore类还提供了一个tryAcquire(n)函数,在没有足够的资源是该函数会立即返回。


QSemaphore sem(5);      // sem.available() == 5


sem.acquire(3);         // sem.available() == 2

sem.acquire(2);         // sem.available() == 0

sem.release(5);         // sem.available() == 5

sem.release(5);         // sem.available() == 10


sem.tryAcquire(1);      // sem.available() == 9, returns true

sem.tryAcquire(250);    // sem.available() == 9, returns false





QSemaphore sem(5);      // a semaphore that guards 5 resources  //初始5个资源

sem.acquire(5);         // acquire all 5 resources 请求5个资源

sem.release(5);         // release the 5 resources 添加5个资源

sem.release(10);        // "create" 10 new resources 添加10个资源



 QSemaphore ss(5); //初始五个资源

 ss.acquire(4);//获得4个资源剩余1个资源

 QMessageBox::information(NULL, tr("提示"), tr("1"),QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);

 ss.release(1);//添加1个资源变成2个资源

 ss.acquire(2);//请求2个资源变成0个资源

 QMessageBox::information(NULL, tr("提示"), tr("中华人名共和国"),QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);


QWaitCondition

对生产者和消费者问题的另一个解决方法是使用QWaitCondition,它允许线程在一定条件下唤醒其他线程。其中wakeOne()函数在条件满足时随机唤醒一个等待线程,而wakeAll()函数则在条件满足时唤醒所有等待线程。

下面重写生产者和消费者实例,以QMutex为等待条件,QWaitCondition允许一个线程在一定条件下唤醒其他线程。 


QWaitCondition.wait 内部会首先解锁mutex,使其他线程能使用mutex ,进入等待状态,当收到wakeAll  wakeOne 唤醒线程时候,wait会再次锁定mutex 然后退出阻塞状态,执行后面的语句

QMutex mutex;

QWaitCondition newdataAvliable;

QByteArray array;

典型生产者消费者

mutex.lock();

array.Add(..);

....

mutex.unlock();

newdataAvliable.wakeAll();


消费者


mutex.lock();

newdataAvliable.wait(&mutext);

array[0];

mutex.unlock();



QT等待且不卡界面的方法

void waitMiliSec(int ms)

{

QTime t;

t.start();

while(t.elapsed()<ms)

QCoreApplication::processEvents();

}


QT加C头文件和加库
extern "C"{
#include
<libavcodec/avcodec.h>
}
#pragma comment(lib,""avcodec.lib")
pro常用
$$PWD当前路径
INCLUDEPATH+=$$PWD/../../
//库路径是大写L  具体库是小写l
LIBS+=-L$$PWD/../../lib
//具体库

LIBS+=L$$PWD/../../lib/lmylib.so



QT的容器类

一.顺序容器

QList<QString> alist;

QVector<QString>avect;

QStack<int>stack;

stack.push(1);

...

stack.pop();

队列

QQueue<work *>m_WorkList;

m_WorkList->enqueue(pwork1);

m_WorkList->enqueue(pwork2);

m_WorkList->enqueue(pwork3);

work *pwork=m_WorkList->dequeue();


二.关联容器类

QMap<string,int>map;

map["a"]=1;

map["b"]=2;

map.insert("c",1);

map.remove("c");

int A=map["a"];

map.value("a");


QMultiMap map;//同一个key可以包含数组

QMultiMap <string,int>map1;

map1.insert("a",1);

map1.insert("a",2);

QList<int>values=map1.values("a");


QHash



QT打印功能

Qt中对打印的支持是有一个独立的printsupport模块来完成的,所以,要想在程序中使用Qt的打印功能,必须先在pro文件中添加下面这句代码:

QT += printsupport


图形视图通过它的展示函数:QGraphicsScene::render()和QGraphicsView::render()提供单线(single-line)打印。这些函数提供相同的API,通过将QPainter传递给展示函数,你可以打印场景、视图的全部或部分内容。例子显示了如何使用QPainter将场景的全部内容打印到整页纸上。


QGraphicsScene scene;

scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));


QPrinter printer;

if (QPrintDialog(&printer).exec() == QDialog::Accepted) {

  QPainter painter(&printer);

  painter.setRenderHint(QPainter::Antialiasing);

  scene.render(&painter);

}


场景和视图函数展示函数的差异是一个在场景坐标,另一个在视图坐标。QGraphicsScene::render()常用于打印无变换的场景的全部内容,如画几何数据文档等。QGraphicsView::render()适合于打印屏幕快照(screenshots),缺省情况下,它展示视图端口中的当前内容。


QGraphicsScene scene();

scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

//QPixmap pixmap; 官当文档这样是错误的,pixmap初始化为空,没空间可不行

QPixmap pixmap(scene->sceneRect().width(), scene->sceneRect().height());

QPainter painter(&pixmap);

painter.setRenderHint(QPainter::Antialiasing);

scene.render(&painter);

painter.end();

pixmap.save("scene.png");



 

当源区域和目标区域的大小不匹配时,源内容进行伸展以适合目标区域。通过传递Qt::AspectRatioMode给你正调用的展示函数,你可以在源内容伸缩时,保持或忽略纵横比。

void MainWindow::printReview()

{

    QPrinter printer(QPrinter::HighResolution);

    // 创建打印预览对话框

    QPrintPreviewDialog preview(&printer, this,

                                windowFlags() | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint);

    // 当要生成预览页面时,发射paintRequested()信号

    connect(&preview, SIGNAL(paintRequested(QPrinter *)),

            this, SLOT(printPreview(QPrinter *)));

    preview.exec();

}


void MainWindow::printPreview(QPrinter *printer)

{

    QPainter painter(printer);

    painter.setRenderHints(QPainter::Antialiasing |

                           QPainter::TextAntialiasing |

                           QPainter::SmoothPixmapTransform,

                           true);

    m_pScene->render(&painter);

}


void MainWindow::printToPrinter()

{

    QPrinter printer(QPrinter::HighResolution);

    if (QPrintDialog(&printer).exec() == QDialog::Accepted)

    {

        QPainter painter(&printer);

        painter.setRenderHint(QPainter::Antialiasing);

        m_pScene->render(&painter);

    }

}


void MainWindow::printToImage()

{

    // 记住,一定要初始化QPixmap的大小

    QPixmap pixmap(m_pScene->sceneRect().width(),

                   m_pScene->sceneRect().height());

    pixmap.fill(Qt::white);

    QPainter painter(&pixmap);

    painter.setRenderHint(QPainter::HighQualityAntialiasing);

    m_pView->render(&painter);

    painter.end();


    QString filePath = QFileDialog::getSaveFileName(this, "Save Image",

                       "", "BMP (*.bmp);;PNG (*.png);;JPEG (*.jpg *.jpeg);;All files (*.*)");

    if (filePath == "" && pixmap.isNull())

    {

        qDebug() << "Error!";

        return;

    }


    pixmap.save(filePath);

}


void MainWindow::printToPdf()

{

    QPrinter printer(QPrinter::HighResolution);

    printer.setPageSize(QPrinter::A4);

    printer.setOrientation(QPrinter::Portrait);

    printer.setOutputFormat(QPrinter::PdfFormat);

    // file will be created in your build directory (where debug/release directories are)

    printer.setOutputFileName("MyGV-OpenGL.pdf");


    QPainter painter;

    if(!painter.begin(&printer))

    {

        qDebug() << "Error!";

        return;

    }

    m_pScene->render(&painter);

    painter.end();

}


Qt实现保存、浏览、预览、打印功能的示例代码

1、保存PDF


(1)保存某个控件里的内容


一些输入类控件可以直接调用print()函数,一些显示类的控件可以直接调用render()函数,一些控件不具备这个功能。代码如下:

void MainWindow::on_btnSave_clicked()

{

 QString fileName = QFileDialog::getSaveFileName(this, tr("导出PDF文件"), QString(), "*.pdf");

 if (!fileName.isEmpty())

 {

 // 如果文件后缀为空,则默认使用.pdf

 if (QFileInfo(fileName).suffix().isEmpty())

 {

  fileName.append(".pdf");

 }

 QPrinter printer;

 // 指定输出格式为pdf

 printer.setOutputFormat(QPrinter::PdfFormat);

 printer.setOutputFileName(fileName);

 // ui->textEdit->print(&printer);

 ui->tableWidget->render(&printer);

 }

}

(2)保存某些控件里的内容


这里需要将要保存的所有内容放在一个容器里面,比如放在QWidget上,同样可以用上面的方式来保存,下面用的是以图片的方式来保存。代码如下:

void MainWindow::on_btnSave_clicked()

{

 QString fileName = QFileDialog::getSaveFileName(this, tr("保存PDF文件"), QString(), "*.pdf");

 if (!fileName.isEmpty())

 {

 // 如果文件后缀为空,则默认使用.pdf

 if (QFileInfo(fileName).suffix().isEmpty())

 {

  fileName.append(".pdf");

 }

 QPrinter printerPixmap(QPrinter::HighResolution);

 //自定义纸张大小,这里要打印的内容都在stackedWidget上

 printerPixmap.setPageSize(QPrinter::Custom);

 printerPixmap.setPaperSize(QSizeF(ui->stackedWidget->height(), ui->stackedWidget->width()), QPrinter::Point);

 //设置纸张大小为A4,这里注释掉了,建议自定义纸张 ,否则保存的就会有很多空白

 //printerPixmap.setPageSize(QPrinter::A4);

 //横向打印

 printerPixmap.setOrientation(QPrinter::Landscape);

 //设置输出格式为pdf

 printerPixmap.setOutputFormat(QPrinter::PdfFormat);

 //设置输出路径

 printerPixmap.setOutputFileName(fileName);

 //获取界面的图片

 QPixmap pixmap = QPixmap::grabWidget(ui->stackedWidget, ui->stackedWidget->rect());

 QPainter painterPixmap;

 painterPixmap.begin(&printerPixmap);

 QRect rect = painterPixmap.viewport();

 int x = rect.width() / pixmap.width();

 int y = rect.height() / pixmap.height();

 //将图像(所有要画的东西)在pdf上按比例尺缩放

 painterPixmap.scale(x, y);

 //画图

 painterPixmap.drawPixmap(0, 0, pixmap);

 painterPixmap.end();

 QMessageBox::information(this, tr("生成PDF"), tr("保存PDF文件成功"), QMessageBox::Ok);

 }

}


2、浏览


Qt没有提供浏览pdf的方式,可以通过使用第三方库Poppler来实现,这里是相关文件:官网,编译好的库文件,所有文件的文件包,实现pdf阅读器。可以通过官方的源码来编译库,不过可能会非常坎坷。


我试了这个方法,不过没有成功,(⊙﹏⊙)b!因为只是需要实现打开pdf文件的一个小功能而不是实现类似pdf阅读器,所以就换了一个方法,回头搞阅读器的时候还是得研究一番。

这里用的是进程的方法来使用电脑上的阅读器打开文件,优点是:简单,就两行代码;缺点是:①电脑上没下载阅读器就没办法了;②效率应该没有使用第三方库高。

代码如下:

QString fileName = QFileDialog::getOpenFileName(this, tr("选择文件"),QString(),

       tr("PDF 文档 (*.pdf);;所有文件 (*.*)"));

 QProcess * p = new QProcess;

 p->start("C:\\Program Files (x86)\\Foxit Software\\Foxit Reader Plus\\FoxitReaderPlus.exe",

  QStringList() << fileName);

3、预览


预览使用了预览对话框QPrintPreviewDialog,也是用的图片的方式来预览pdf,其实预览窗口已经自带了打印按钮,在这个界面已经可以打印了。代码如下:

void MainWindow::on_btnPreview_clicked()

{

 QPrinter printer(QPrinter::HighResolution);

 //自定义纸张大小

 printer.setPageSize(QPrinter::Custom);

 printer.setPaperSize(QSizeF(ui->stackedWidget->height(), ui->stackedWidget->width()),

    QPrinter::Point);

 QPrintPreviewDialog preview(&printer, this);

 preview.setMinimumSize(1000,600);

 connect(&preview, SIGNAL(paintRequested(QPrinter*)), SLOT(printPreviewSlot(QPrinter*)));

 preview.exec ();

}

void MainWindow::printPreviewSlot(QPrinter *printerPixmap)

{

 printerPixmap->setOrientation(QPrinter::Landscape);

 //获取界面的图片

 QPixmap pixmap = QPixmap::grabWidget(ui->stackedWidget, ui->stackedWidget->rect());

 QPainter painterPixmap(this);

 painterPixmap.begin(printerPixmap);

 QRect rect = painterPixmap.viewport();

 int x = rect.width() / pixmap.width();

 int y = rect.height() / pixmap.height();

 painterPixmap.scale(x, y);

 painterPixmap.drawPixmap(0, 0, pixmap);

 painterPixmap.end();

}

4、打印


打印使用了打印对话框QPrintDialog,如何打印文本框内容的话直接用print()函数就行,否则还是用打印图片的方式,和预览不同的其实就是使用的窗口类不同其余都差不多,代码如下:

void MainWindow::on_btnPrint_clicked()

{

 // 创建打印机对象

 QPrinter printer;

 // 创建打印对话框

 QString printerName = printer.printerName();

 if( printerName.size() == 0)

 return;

 QPrintDialog dlg(&printer, this);

 //如果编辑器中有选中区域,则打印选中区域

 if (ui->textEdit->textCursor().hasSelection())

 dlg.addEnabledOption(QAbstractPrintDialog::PrintSelection);

 // 如果在对话框中按下了打印按钮,则执行打印操作

 if (dlg.exec() == QDialog::Accepted)

 {

 ui->textEdit->print(&printer);

 }

}

void MainWindow::on_btnPrint_2_clicked()

{

 QPrinter printerPixmap;

 QPixmap pixmap = QPixmap::grabWidget(ui->stackedWidget, ui->stackedWidget->rect()); //获取界面的图片

 QPrintDialog print(&printerPixmap, this);

 if (print.exec())

 {

 QPainter painterPixmap;

 painterPixmap.begin(&printerPixmap);

 QRect rect = painterPixmap.viewport();

 int x = rect.width() / pixmap.width();

 int y = rect.height() / pixmap.height();

 painterPixmap.scale(x, y); 

 painterPixmap.drawPixmap(0, 0, pixmap); 

 painterPixmap.end();

 }

}



调用热敏打印机,打印测试字符

pro文件加载库文件

 

QT       += core gui

qtHaveModule(printsupport): QT += printsupport 

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 

TARGET = test_printer

TEMPLATE = app 

SOURCES += main.cpp\

        mainwindow.cpp 

HEADERS  += mainwindow.h 

FORMS    += mainwindow.ui


mainwindows.h


#ifndef MAINWINDOW_H

#define MAINWINDOW_H

 

#include <QMainWindow>

 

#include <QPrintDialog>

#include <QPrinter>

#include <QPrintPreviewDialog>

#include <QFileDialog>

#include <QPageSetupDialog>

 

namespace Ui {

class MainWindow;

}

 

class MainWindow : public QMainWindow

{

    Q_OBJECT

 

public:

    explicit MainWindow(QWidget *parent = 0);

    ~MainWindow();

 

private slots:

    void on_pushButton_clicked();

 

    void doPrint();

        void doPrintPreview();

        void printPreview(QPrinter *printer);

        void createPdf();

    void setUpPage();

 

 

private:

    Ui::MainWindow *ui;

};

 

#endif // MAINWINDOW_H


mainwindow.cpp


#include "mainwindow.h"

#include "ui_mainwindow.h"

 

#include <QDebug>

#include <QTextDocument>

 

MainWindow::MainWindow(QWidget *parent) :

    QMainWindow(parent),

    ui(new Ui::MainWindow)

{

    ui->setupUi(this);

}

 

MainWindow::~MainWindow()

{

    delete ui;

}

 

void MainWindow::on_pushButton_clicked()

{

    QPrinter printer;

    QTextDocument doc;

 

    QFont font("宋体",2);

    doc.setDefaultFont(font);

 

//    QFont font = doc.defaultFont();

//    font.setBold(true);

//    font.setPointSize(10);

//    doc.setDefaultFont(font);

 

    QSizeF s = QSizeF(printer.logicalDpiX() * (58 / 25.4),

                      printer.logicalDpiY() * (297 / 25.4));

    doc.setPageSize(s);

    printer.setPageSizeMM(s);

    printer.setOutputFormat(QPrinter::NativeFormat);

    doc.setPlainText("I am jdh!\n打印测试");

    doc.print(&printer);

}

 

void MainWindow::doPrint()

{

    // 创建打印机对象

    QPrinter printer;

    // 创建打印对话框

    QString printerName = printer.printerName();

    if( printerName.size() == 0)

        return;

    QPrintDialog dlg(&printer, this);

    //如果编辑器中有选中区域,则打印选中区域

    if (ui->textEdit->textCursor().hasSelection())

        dlg.addEnabledOption(QAbstractPrintDialog::PrintSelection);

    // 如果在对话框中按下了打印按钮,则执行打印操作

    if (dlg.exec() == QDialog::Accepted)

    {

       //ui->textEdit->print(&printer);

       // print the existing document by absoult path

        //printFile("D:/1.doc");

 

        QTextDocument doc;

        //doc.setHtml(htmlString);

        QFont font = doc.defaultFont();

        font.setBold(true);

        font.setPointSize(10);

        doc.setDefaultFont(font);

        QSizeF s = QSizeF(printer.logicalDpiX() * (58 / 25.4), printer.logicalDpiY() * (297 / 25.4));

        //doc.setPageSize(s);

        printer.setPageSizeMM(s);

        printer.setOutputFormat(QPrinter::NativeFormat);

 

        doc.setHtml("店联");

        doc.print(&printer);

    }

}

// 打印预览

void MainWindow::doPrintPreview()

{

    QPrinter printer;

 

    // 创建打印预览对话框

    QPrintPreviewDialog preview(&printer, this);

    // 当要生成预览页面时,发射paintRequested()信号

    connect(&preview, SIGNAL(paintRequested(QPrinter*)),

                  this,SLOT(printPreview(QPrinter*)));

    preview.exec();

}

void MainWindow::printPreview(QPrinter *printer)

{

    //ui->textEdit->print(printer);

    QTextDocument doc;

    //doc.setHtml(htmlString);

//    QFont font = doc.defaultFont();

//    font.setBold(true);

//    font.setPointSize(font.pointSize() + 1);

//    doc.setDefaultFont(font);

    QSizeF s = QSizeF(printer->logicalDpiX() * (58 / 25.4), printer->logicalDpiY() * (297 / 25.4));

//    doc.setPageSize(s);

    printer->setPageSizeMM(s);

    printer->setOutputFormat(QPrinter::NativeFormat);

 

    doc.setHtml("店联");

    doc.print(printer);

}

// 生成PDF文件

void MainWindow::createPdf()

{

    QString fileName = QFileDialog::getSaveFileName(this, tr("导出PDF文件"), QString(), "*.pdf");

    if (!fileName.isEmpty()) {

        // 如果文件后缀为空,则默认使用.pdf

        if (QFileInfo(fileName).suffix().isEmpty())

            fileName.append(".pdf");

        QPrinter printer;

        // 指定输出格式为pdf

        printer.setOutputFormat(QPrinter::PdfFormat);

        printer.setOutputFileName(fileName);

        ui->textEdit->print(&printer);

    }

}

// 页面设置

void MainWindow::setUpPage()

{

    QPrinter printer;

    QPageSetupDialog pageSetUpdlg(&printer, this);

    if (pageSetUpdlg.exec() == QDialog::Accepted)

    {

        printer.setOrientation(QPrinter::Landscape);

    }

    else

    {

        printer.setOrientation(QPrinter::Portrait);

    }

}


qt for android + opencv的配置及使用

1:下载opencv for android


网址:https://opencv.org/releases.html


下载android的包

1.png



 


 


2:下载后解压,放到一个英文的路径里,路径里不要带中文和空格。


 1.png




 


3:新建qt项目,在pro文件里添加库


unix {

 

ANDROID_OPENCV = E:/ku/opencv-4.0.1-android-sdk/OpenCV-android-sdk/sdk/native

 

 

INCLUDEPATH += $$ANDROID_OPENCV/jni/include/opencv2 \

               $$ANDROID_OPENCV/jni/include

 

LIBS += $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_calib3d.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_core.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_dnn.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_features2d.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_flann.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_highgui.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_imgcodecs.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_imgproc.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_ml.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_objdetect.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_photo.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_stitching.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_video.a \

        $$ANDROID_OPENCV/staticlibs/armeabi-v7a/libopencv_videoio.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libcpufeatures.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libIlmImf.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/liblibjasper.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/liblibjpeg-turbo.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/liblibpng.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/liblibprotobuf.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/liblibtiff.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/liblibwebp.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libquirc.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libtbb.a \

        $$ANDROID_OPENCV/3rdparty/libs/armeabi-v7a/libtegra_hal.a \

        $$ANDROID_OPENCV/libs/armeabi-v7a/libopencv_java4.so

 

 

 

 

}

   4:最后一个库libopencv_java4.so是动态库,要加入到安卓的apk里。按照顺序将动态库加入进去。


 


 


 


 


5:正常编写opencv的代码即可。


FFPEG下载地址:http://ffmpeg.org/download.html




Qt on Mac 开发之 Qt程序加入Objective-C代码(包含如何查找Objective-C的库)

首先我要说一下 Objective-C 的源文件,后缀是.m 或 .mm ,在 .mm 文件里,可以直接使用 C++ 代码。所以,我们要混合 Qt 代码与 Objective-C 代码,就需要在 Qt 项目里加入 mm 文件。

1、引入OC文件

在pro(或pri)中加入Objective-C的头文件和源文件


例如:


HEADERS += $$PWD/os/mac/readDeviceInfo.h

有可能是

OBJECTIVE_HEADERS+= $$PWD/os/mac/readDeviceInfo.h


OBJECTIVE_SOURCES += $$PWD/os/mac/readDeviceInfo.mm


2、引入OC库

在包含此方法的pro(或pri)中加入相关库


例如:


LIBS += -framework CoreServices

LIBS += -framework Foundation


3、引入Plist文件

在包含此方法的pro(或pri)中加入相关plist文件


例如:


QMAKE_INFO_PLIST += ./os/ios/AirLink.plist


4、编译程序

Qt会显示这里有问题,但是可以编译成功

1.png


如何查找Objective-C所对应的库呢?

这个要借助Xcode的帮助文档了


我们打开Mac上的X-code,依次点击:

2.png


先在Xcode上编译通过的程序,如果Qt的IDE编译报错,一般是少库的原因。

按错误原因查找,在X-code的帮助文档中找到对应的库,然后再Qt的pro或者pri中加入(LIBS += -framework+库名)即可


例如:

下图中的NSBundle是需要Foundation库,于是我们加入LIBS += -framework Foundation语句。

3.png

把Object-C编写的文件加入了Qt项目中,


Qt是可以编译的,但是编辑器检查时,依旧是不识别Object-C代码的,这一点要记住哈。


X-code会帮我们检查错误,所以一般先在X-code上编译通过了,再用Qt去编译。



QT运行Objective-C代码

qtrunoc


MainWindow.h:

#ifdef Q_OS_MAC

    #include <Carbon/Carbon.h>

    #include <ctype.h>

    #include <stdlib.h>

    #include <stdio.h>


    #include <mach/mach_port.h>

    #include <mach/mach_interface.h>

    #include <mach/mach_init.h>


    #include <IOKit/pwr_mgt/IOPMLib.h>

    #include <IOKit/IOMessage.h>

#endif


MainWindow.cpp:

MainWindow::MainWindow(QWidget *parent) :

    QMainWindow(parent),

    ui(new Ui::MainWindow)

{

    ui->setupUi(this);



    #ifdef Q_OS_MAC

    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self

            selector: @selector(receiveSleepNote:)

            name: NSWorkspaceWillSleepNotification object: NULL];

    #endif


}


#ifdef Q_OS_MAC

- (void) receiveSleepNote: (NSNotification*) note

{

    NSLog(@"receiveSleepNote: %@", [note name]);

}

#endif


但是出现错误,似乎QT无法理解代码结构

推荐答案

为了使用C ++编译Objective-c,您需要在.m或.mm文件中包含Objective-c代码. 


然后,附带的标头可以包含可以从C ++调用的函数,而这些函数的主体可以包含Objective-C代码.


例如,假设我们要调用一个函数来弹出OSX通知.从标题开始:-


#ifndef __MyNotification_h_

#define __MyNotification_h_


#include <QString>


class MyNotification

{

public:

    static void Display(const QString& title, const QString& text);    

};    


#endif

如您所见,这是标头中的常规函数,可以从C ++调用.这是实现:-


#include "mynotification.h"

#import <Foundation/NSUserNotification.h>

#import <Foundation/NSString.h>


void MyNotification::Display(const QString& title, const QString& text)

{

    NSString*  titleStr = [[NSString alloc] initWithUTF8String:title.toUtf8().data()];

    NSString*  textStr = [[NSString alloc] initWithUTF8String:text.toUtf8().data()];


    NSUserNotification* userNotification = [[[NSUserNotification alloc] init] autorelease];

    userNotification.title = titleStr;

    userNotification.informativeText = textStr;


    [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];

}

该实现包含Objective-C,并且由于其.mm文件扩展名,编译器将能够正确处理此问题.


请注意,在问题中提供的示例中,您需要考虑代码的作用.尤其是在使用' self '时,因为我希望这需要引用一个Objective-C类,而不是C ++类.


这篇关于QT运行Objective-C代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!


混合代码

//getAllVisibleWndPos.h

#ifndef GETALLVISIBLEWNDPOS_H

#define GETALLVISIBLEWNDPOS_H


#include <QRect>

#include <QList>

#include <vector>


#import <Foundation/Foundation.h>

#import <Cocoa/Cocoa.h>

#import <CoreGraphics/CGWindow.h>

#import <CoreFoundation/CFArray.h>


struct WND_INFO{

int layer;//Z序

int index;//序号

QRect pos;

};


bool CompareWNDINFO(const WND_INFO& A , const WND_INFO& B);

QList<WND_INFO> GetAllVisibleWndPos();


#endif // GETALLVISIBLEWNDPOS_H

//getAllVisibleWndPos.mm

#import <getallvisiblewndpos.h>


bool CompareWNDINFO(const WND_INFO& A , const WND_INFO& B)

{

if(A.layer == B.layer)

{

return A.index < B.index;

}

if(A.layer > B.layer && B.layer > 0)

{

return true;

}

if(A.layer < B.layer && A.layer > 0)

{

return false;

}

return A.layer == 0;

}


QList<WND_INFO> GetAllVisibleWndPos()

{

std::vector<WND_INFO> vec;

CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements , kCGNullWindowID);

CFIndex cnt = CFArrayGetCount(windowList);

for (CFIndex i = 0; i < cnt ; i++)

{

NSDictionary *dict = (NSDictionary* )CFArrayGetValueAtIndex(windowList , i);

int layer = 0;

CFNumberRef numberRef = (__bridge CFNumberRef) dict[@"kCGWindowLayer"];

CFNumberGetValue(numberRef , kCFNumberSInt32Type , &layer);

if(layer < 0)

continue;

CGRect windowRect;

CGRectMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)(dict[@"kCGWindowBounds"]) , &windowRect);

QRectF pos;

pos.setRect(windowRect.origin.x , windowRect.origin.y , windowRect.size.width , windowRect.size,height);

WND_INFO info;

info.layer = layer;

info.pos.setRect(pos.x() , pos.y() , pos.width() , pos.height());

info.index = i;

vec.push_back(info);

}

std::sort(vec.begin() , vec.end() , CompareWNDINFO);

QList<WND_INFO> list;

for(int i = 0 ; i < vec.size() ; i++)

{

list.push_back(vec[i]);

}

return list;

}




最近有好几个使用Qt的朋友问起 Qt for iOS 的事情,因为我在这方面的经验特别少,写不出系统的文章来,非常抱歉,不能给出令人满意的答复,推荐大家去看 Jason’s Home ,在我博客左侧边栏的友情链接里也有,他提供了 Qt for iOS 的一些非常有意义的文章,而且是基于实践的,他的 App 已经在 App Store 中上线。


至于我呢,在这篇文章里,简单介绍一些如何混合 Qt 与 OC 编程。


我要说的内容呢,大部分在 Qt 帮助里都有,请大家到索引模式下,键入”Qt for iOS”,找到 Qt for iOS 这篇文章来看。它介绍了搭建开发环境、编译应用、混合OC编程这三个方面,已经非常详细了。


如果你不想啃英文,那可以接着我的文章往下看。


项目设置

既然要聊 Qt 混合 OC 编程,首先要简单介绍一下 Objective C 。我只有一句话:Go,问搜索引擎去。因为我所知实在有限,怕误导了您。当然如果您不怕,往下看吧。


OC源文件介绍

首先我要说一下 Objective C 的源文件,后缀是.m 或 .mm ,在 .mm 文件里,可以直接使用 C++ 代码。所以,我们要混合 Qt 代码与 OC 代码,就需要在 Qt 项目里加入 mm 文件。


pro 文件配置

Qt SDK for Mac ,安装之后, Qt Creator 会使用 XCode 提供的编译工具链来编译代码,能够正确编译 mm 文件,也可以链接 iOS 的库文件。


而要混合 OC 代码,需要更改一下 pro 文件。一个是添加 mm 文件,一个是连接针对 iOS 的库文件。


添加源文件,使用 OBJECTIVE_SOURCES 这个变量,比如酱紫:


OBJECTIVE_SOURCES += ocview.mm

链接库 XCode 提供的库,则需要使用 QMAKE_LFLAGS ,类似酱紫:


ios {

    QMAKE_LFLAGS    += -framework OpenGLES

    QMAKE_LFLAGS    += -framework GLKit

    QMAKE_LFLAGS    += -framework QuartzCore

    QMAKE_LFLAGS    += -framework CoreVideo

    QMAKE_LFLAGS    += -framework CoreAudio

    QMAKE_LFLAGS    += -framework CoreImage

    QMAKE_LFLAGS    += -framework CoreMedia

    QMAKE_LFLAGS    += -framework AVFoundation

    QMAKE_LFLAGS    += -framework AudioToolbox

    QMAKE_LFLAGS    += -framework CoreGraphics

    QMAKE_LFLAGS    += -framework UIKit

}

上面是我使用 Qt 针对 iOS 编程的配置。我使用了很多针对 iOS 的库,所以添加了很多 framework 。


“ -framework UIKit ”这种参数,是经由 Makefile 传递给 Clang 的参数,-framework 是用来指示要链接某个框架(或者说库)的关键字,它后面跟的是框架(库)名。


需要注意的是,我们使用针对 iOS 的库,不是通过“ LIBS += ”这种方式来引入哦。当然,你自己通过 Qt 实现的 .a 库,依然需要使用“ LIBS += ”这种方式。


指定plist文件

有时你需要为你的项目指定 plist 文件, plist 文件全名是 Property List ,后缀是 .plist 。它用来定义 iOS 应用的属性,比如 Bundle(iOS上的一个应用被称为一个 Bundle ) 的显示名字、可执行文件名字、签名、证书等等,当然也可以保存一些配置数据。具体的介绍参考 iOS 开发的文档吧。


要在 pro 文件里添加 plist ,要使用 QMAKE_INFO_PLIST 关键字。如下面酱紫:


QMAKE_INFO_PLIST += MultiWindow.plist

好啦,关于 pro 文件中与混合使用 OC 相关的配置项,大体就这些了。接下来我们看如何写 Objective C 代码啦。


混合使用Objective C 代码

乖乖,很惶恐啊,这是我的弱项,没写过多少 OC 代码。所以,请不要问我 OC 有关的问题,我真不知道……


背景

我的示例,是在 QML 的界面上叠加iOS原生的界面,即 UIView、UIWindow之类的。因为 OC 是 C 的近亲,和 C++ 有着天然的血缘,混合起来特别方便哈,比 Android 上使用 JNI 编程好用多了。


不过有一点, OC 都适用 [] 这种语法来调用函数,使用 XCode 的话,语法提示和自动完成功能非常强大,基本不用思考的就能找到你要用的函数。而 Qt Creator 么,嘿嘿,就没这么好相与了,纯粹要手写哦。我当时都是开着 XCode 看 API 文档找的,比较痛苦。


QQuickView 是什么

我测试时的示例,用的是 Qt Quick App 项目模板,使用 QQuickView 来加载 QML 文档。这里也以此为例来说明。


首先要说 QQuickView 到底是什么。


QQuickView 呢,其实是一个 UIView 。UIView 则是 iOS 开发框架里很多界面元素的根儿,比如 UIWindow 就是 UIView 的子类。


Qt 的 QQuickView 是一个 UIView ,创建了 QQuickView 实例后,就有了一个 UIView ,然后 Qt 玩了一些魔法,拿到了 UIView 的 OpenGL Context ,跑起了 Qt 的事件循环,在这个 OpenGL Context 上从零开始绘制了自己的场景和 UI 系统。


就这么简单,你可以查阅 Qt 源码来进一步了解。


需要注意的是, QML 界面元素的渲染,与 UIView 这种原生界面的渲染,不在一个线程中。而且 iOS 对 OpenGL ES 的支持很好,你可以同时使用多个 OpenGL Context 。更好的是,你可以窗口模式来创建一个 OpenGL Context 。不像 Android 版本哦, Qt 使用 OpenGL 的时候都是全屏模式,局部更新不支持,所以我们在 Android 上使用 QML 里的 Camera 和 VideoOutput 来开发拍照应用时, VideoOutput 必须是全屏模式(必须fill_parent)。而在 iOS 上,则没有这个限制了。看来 iOS 还是很美好的啦。


我靠,扯得有点儿远,最近写技术文章少了,越来越罗嗦了。言归正传吧。


因为 QQuickView 实际上就是一个 UIView ,所以可以强制转换为 UIView ,然后使用 OC 的方法来创建新的 UIView 或者 UIWindow ,这样就有了原生的 UI 组件了,你可以在这个原生的 UI 组件上使用 OpenGL 绘制自己的东西或者添加其它原生的控件,非常美好。


不过要说明的是,通过这种方法创建出来的 iOS 原生界面元素,会始终在 QML 界面之上,把 QML 场景里的界面元素给盖住。


混合代码

要使用 OC 的类库,需要在 mm 文件内包含相关的头文件,又有几部分工作要做。一个是在 pro 文件里加入 SDK 路径,使用 INCLUDEPATH 变量即可,不多说了。另外一点是在 mm 文件内包含 OC 的头文件,与 C++ 头文件一个理儿,不过要使用 #import 哦。类似酱紫:


#import <UIKit/UIKit.h>

#import <GLKit/GLKit.h>

包含了头文件,就可以使用 OC 类库了。比如我要在 QQuickView 上面创建一个新的 iOS 原生的 UIView ,.mm 文件里可以这样:


void addOCView(QQuickWindow *w)

{

    UIView *view = reinterpret_cast<UIView *>(w->winId());

    CGRect  viewRect = CGRectMake(10, 10, 100, 100);

    UIView* myView = [[UIView alloc] initWithFrame:viewRect];

    [myView setBackgroundColor:[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0]];

    [view addSubview: myView];

}

如你所见,我写了一个 addOCView 方法,它的参数是 QQuickView 。在 addOCView 方法里,我把 QQuickView 强制转换为 UIView 来使用。


我创建了一个新的 UIView ,设置了它的背景颜色,然后把它添加为 QQuickView 的子窗口。诺,就这么简单了。


说下 main.cpp ,看它如何使用 addOCView() 方法。代码如下:


int main(int argc, char *argv[])

{

    QGuiApplication app(argc, argv);

    QQuickView viewer;

    viewer.setResizeMode(QQuickView::SizeRootObjectToView);

    viewer.setSource(QUrl("qrc:/main.qml"));

    viewer.show();

    addOCView(&viewer);

    return app.exec();

}

一点儿都不惊喜是吧,就是直接调用了 addOCView 哦。哈哈,确实如此了。


iOS 原生界面与 QML 元素的位置映射

混合使用 iOS 原生界面时,也可以达到原生界面与 QML 界面的无缝集成。关键就在于计算 QML 界面元素的位置,然后根据 QML 界面元素的位置来设置原生界面的位置。


QML元素位置换算

QML 提供了换算元素位置的方法,Item 有个方法,叫作 mapToItem() ,它可以把一个相对于 Qt Quick Item 的区域映射到另一个 Item 上,得到坐标了。比如你的 qml 文件是下面的样子:


Rectangle {

    ...

    Rectangle {

        id: videoLayer;

        anchors.margins: 8;

        anchors.left: parent.left;

        anchors.right: parent.right;

        anchors.top: parent.top;

        anchors.bottom: actionBar.top;

        color: "green";

    }

    ...

}

你想把原生的 UIView 定位到 id 为 videoLayer 的元素内部,可以类似下面换算一个坐标并使用它:


var coordinate = videoLayer.mapToItem(null, 8, 8, videoLayer.width - 16, videoLayer.height - 16);

winUtil.addUIView(coordinate.x, coordinate.y, coordinate.width, coordinate.height);

换算出的coordinate有 x 、 y 、 width 、 height 属性。当 mapToItem 的第一个参数为 null 时,换算的结果就是相对于 QQuickView 的。那我们在添加 UIView 时,就可以用这个换算结果来构造一个 CGRect 对象,用这个 CGRect 来初始化 UIView 的位置。


设置 UIView 的位置

前面示例代码中的 winUtil 是我在 C++ 内实现的一个辅助类,导出到了 QML 环境中。它的 addUIView 方法根据传入的坐标来设置原生 UIView 的位置。参考代码如下:


    UIView *v = reinterpret_cast<UIView*>(view->winId());

    uiw = [[UIWindow alloc] initWithFrame:CGRectMake(x, y, width, height)];

    [v addSubview: uiw];

上面代码中,view 是 QQuickView 。这次我们创建 UIView 时使用了传入的 x 、 y 、 width 、 height,这样新建的 UIView 就和 QML 元素整合在一起了,看起来好像是一体的。


OK,这就是全部了。





QListView 自定义delegate和model, 添加checkbox、按钮、文本

实现原理:QCheckbox在页面显示,就是根据model数据中的按钮的选中状态来显示对应图片, 然后画出对应的图片;同样道理,我们可以添加各种自定义button,自定义button的各种状态图片。


效果如图:

1.png



头文件:


#ifndef MAINWINDOW_H

#define MAINWINDOW_H


#include <QMainWindow>

#include <QAbstractListModel>

#include <QStyledItemDelegate>

#include <QListView>


struct CustomData

{

    bool m_isSelect;

    QString m_txt;

};


class CustomListModel : public QAbstractListModel

{

    Q_OBJECT


public:

    CustomListModel();

    ~CustomListModel();


    void insertData(CustomData data);


    //必须实现的函数

    int rowCount(const QModelIndex &parent) const;

    QVariant data(const QModelIndex &index, int role) const;

    bool setData(const QModelIndex &index, const QVariant &value, int role);


private:

    QList<CustomData> m_listData;

};



class CustomDelegate : public QStyledItemDelegate

{

    Q_OBJECT


public:

    CustomDelegate();

    ~CustomDelegate();


    //描绘画面显示

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;


    //处理鼠标事件

    bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);

};


class MainWindow : public QMainWindow

{

    Q_OBJECT


public:

    MainWindow(QWidget *parent = 0);

    ~MainWindow();


private:

    QListView *m_list;

    CustomListModel *m_model;

    CustomDelegate * m_delegate;

};


#endif // MAINWINDOW_H

cpp文件:


#include <QPainter>

#include <QDebug>

#include <QMouseEvent>

#include "mainwindow.h"


MainWindow::MainWindow(QWidget *parent)

    : QMainWindow(parent)

{

    this->setGeometry(200,200,800,480);



    m_list = new QListView(this);

    m_list->setGeometry(0,0,300,200);


    m_model = new CustomListModel();

    //添加测试数据库

    for(int i = 0; i < 5; i++)

    {

        CustomData data;

        data.m_isSelect = (i%2==0);

        data.m_txt = QString("This is %1").arg(QString::number(i+1));

        m_model->insertData(data);

    }


    m_delegate = new CustomDelegate();


    m_list->setModel(m_model);

    m_list->setItemDelegate(m_delegate);

    m_list->setMouseTracking(true);


}


MainWindow::~MainWindow()

{


}


CustomListModel::CustomListModel()

{


}


CustomListModel::~CustomListModel()

{


}


void CustomListModel::insertData(CustomData data)

{

    m_listData.push_back(data);

}


int CustomListModel::rowCount(const QModelIndex &parent) const

{

    return m_listData.size();

}


QVariant CustomListModel::data(const QModelIndex &index, int role) const

{

    QVariant ret;

    int row = index.row();


    if(row>=m_listData.size()||(!index.isValid()))

    {

        return QVariant();

    }


    CustomData tmpData = m_listData.at(row);


    // 下面的role要和setData中的role一一对应;

    switch(role) {

    case Qt::UserRole+1:

        ret = tmpData.m_isSelect;

        break;

    case Qt::UserRole+2:

        ret = tmpData.m_txt;

        break;

    default :

        break;


    }

    return ret;

}


bool CustomListModel::setData(const QModelIndex &index, const QVariant &value, int role)

{

    bool ret = false;

    int row = index.row();


    if(row>=m_listData.size()||(!index.isValid()))

    {

        return false;

    }

    CustomData tmpData = m_listData.at(row);


    switch(role) {

    case Qt::UserRole+1:

        tmpData.m_isSelect = value.toBool();

        ret = true;

        break;

    case Qt::UserRole+2:

        tmpData.m_txt = value.toString();

        ret = true;

        break;

    default :

        break;

    }

    m_listData.replace(row, tmpData);

    return ret;

}


CustomDelegate::CustomDelegate()

{


}


CustomDelegate::~CustomDelegate()

{


}


void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const

{

    QRect retc = option.rect;


    //这里取的数据 对应model里面的role

    bool isSelect = index.data(Qt::UserRole+1).toBool();

    QString txt = index.data(Qt::UserRole+2).toString();


    //qDebug() << "paint data isSelect" << isSelect << "txt" << txt;


    QStyleOptionViewItem viewoption(option);

    initStyleOption(&viewoption, index);

    if(option.state.testFlag(QStyle::State_HasFocus))

    {

        viewoption.state = viewoption.state^QStyle::State_HasFocus;

    }

    QStyledItemDelegate::paint(painter, viewoption, index);



    //画按钮

    {

        QRect checboxRec(retc.left() + 10, retc.top() + (retc.height()-20)/2, 20, 20); //左边距10,竖直方向居中


        if(isSelect)

        {

            QPixmap pix("G:\\QT_Project\\listViewModelDelegate\\fxk_not.png");

            painter->drawPixmap(checboxRec, pix);

        }

        else

        {

            QPixmap pix("G:\\QT_Project\\listViewModelDelegate\\fxk_ok.png");

            painter->drawPixmap(checboxRec, pix);

        }

    }



    //画txt

    {

        painter->save();


        //设置字体,颜色

        QFont font;

        font.setFamily("Microsoft YaHei");

        font.setPixelSize(10);

        painter->setFont(font);


        QRect txtRec(retc.left() + 50, retc.top(), retc.width()-100, retc.height());

        painter->drawText(txtRec, Qt::AlignCenter, txt);


        painter->restore();

    }

}


bool CustomDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)

{

    QRect retc = option.rect;


    //对应上面画的checkbox的retc

    QRect checboxRec(retc.left() + 10, retc.top() + (retc.height()-20)/2, 20, 20);


    //按钮点击事件;

    QMouseEvent *mevent = static_cast<QMouseEvent*>(event);

    if(checboxRec.contains(mevent->pos()) && event->type() == QEvent::MouseButtonPress)

    {

        bool value = model->data(index, Qt::UserRole+1).toBool();

        model->setData(index, !value, Qt::UserRole+1);

        model->dataChanged(index, index);


        //此处可以添加自定义信号,即使checbox点击信号;

    }


    return QStyledItemDelegate::editorEvent(event, model, option, index);

}

总结:


按钮的显示,归根到底就是根据鼠标事件选择显示对应的图片;主要函数就是delegate中的paint函数和editorEvent函数;paint负责渲染,editorEvent负责鼠标事件;根据此框架,可以满足绝大部分的定制需求;


代码


QListView和QListWidget

QListView是基于Model,而QListWidget是基于Item。这是它们的本质区别。


往QListView中添加条目需借助QAbstractListModel:


如:


    MainWindow::MainWindow(QWidget *parent) :

    QMainWindow(parent),

    ui(new Ui::MainWindow)

{

    ui->setupUi(this);    

    QStandardItemModel *model = new QStandardItemModel(this);

    QStandardItem *item = new QStandardItem("item1");

    model->appendRow(item);

    item = new QStandardItem("item2");

    model->appendRow(item);

    ui.listView_stage->setModel(model);


}


添加数据

QStandardItem *item = new QStandardItem("item3");

QStandardItemModel *model = dynamic_cast<QStandardItemModel*>(ui.listView_stage->model());

model->appendRow(item);


删除数据

QStandardItemModel *model = dynamic_cast<QStandardItemModel*>(ui.listView_stage->model());

model->removeRow(ui.listView_stage->currentIndex().row());


而在QListWidget中添加条目可以直接additem


如:


  QListWidget list;

  list.setViewMode(QListWidget::IconMode );

  list.setResizeMode(QListWidget::Adjust);

  list.setMovement(QListWidget::Static);

  for(int i =0; i < 100 ; i ++){

  QListWidgetItem *item = new QListWidgetItem(&list);

  QStyle::StandardPixmap sp = (QStyle::StandardPixmap)(i % 57);


  item->setData(Qt::DecorationRole, qApp->style()->standardPixmap(sp).scaled(QSize(16,16), Qt::KeepAspectRatio, Qt::SmoothTransformation) );

  item->setData(Qt::DisplayRole,QObject::tr("Item %1").arg(i));

  }

插入数据

// 创建一个列表项对象

    QListWidgetItem *item = new QListWidgetItem(listWidget);

    item->setText("静夜思");

    item->setFont(font);

    item->setTextAlignment(Qt::AlignCenter);

// 向列表中插入数据

    listWidget->insertItem(0, item);


双击当前选中的项,进行修改

// 双击列表时,可以对当前选中的项进行编辑

    connect(listWidget, &QListWidget::doubleClicked, [=](){

        // 获取当前选中的行

        qDebug() << listWidget->currentRow();

        

        // 获取当前选中的项

        QListWidgetItem *item = listWidget->currentItem();

        qDebug() << item->text().toUtf8().data();

        

        // 设置 item 允许编辑

        item->setFlags(item->flags() | Qt::ItemIsEditable);

    });

 

    // 当列表项发生改变时,触发的信号(参数为当前发生更改的项)

    connect(listWidget, &QListWidget::itemChanged, [=](QListWidgetItem * item){

        qDebug() << "修改后的值:" << item->text().toUtf8().data();

    });

删除


QList<QListWidgetItem*> list;

    list = ui->addrListWidget>findItems("nihao",Qt::MatchCaseSensitive);

    QListWidgetItem* sel = list[0];

    int r = ui->addrListWidget->row(sel);

    QListWidgetItem* item = ui->addrListWidget->takeItem(r);

    ui->addrListWidget->removeItemWidget(item);

    delete item;

 


ListView GridView Repeater中Model使用


QML数据模型(Model)

QML中的ListView GridView Repeater等元素需要数据模型来提供要显示的数据


ListModel{

id:myModel

ListElement{type:"Dog";ane:8}

ListElement{type:"Cat";age:5}

}


Compent{

id:myDelegate

Text{

 text:type+","+age

  }

}


}


ListView{

anchor.fill:parent

model:myModel

delegate:myDelegate


}

对于没有角色名称的数据模型(如QStringList)可以使用modelData角色来应用数据,只有一个角色的模型也可以使用modelData角色,这时modelData角色包含的数据与命名的角色相同


Qt添加Java文件



QT显示窗口


void GraceShowWidget(QWidget *widget)

{

    if (!widget) {

        return;

    }


    if (widget->isMinimized()) {

        widget->showNormal();

    } else {

        widget->show();

    }

    widget->raise();

    widget->activateWindow();

}

窗口的显示与模态窗口

动态构造对象,使用new关键字分配动态内存,使得构造函数执行完毕后,对象所在空间仍然存在(注意,如果设置了QDialog的父窗口,就不必手动销毁动态分配的空间了,在关闭父窗口的同时,子窗口的内存空间也会被自动销毁) (推荐)


#include "mywidget.h"

#include "ui_mywidget.h"

#include<QDialog>

#include<windows.h>

#include<QDebug>

MyWidget::MyWidget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::MyWidget)

{

    ui->setupUi(this);

    QDialog *dialog =  new QDialog(this);   //动态构造对象

    dialog->show();

}

 

MyWidget::~MyWidget()

{

    delete ui;

}

如何设置模态呢?


通过QWidget::setModal(bool) 函数来设置


参数为TRUE(1)代表设置为模态窗口,参数为FALSE(0)代表设置为非模态窗口(默认)

/*MyWdiget.cpp 模态的设置*/

#include "mywidget.h"

#include "ui_mywidget.h"

#include<QDialog>

#include<windows.h>

#include<QDebug>

MyWidget::MyWidget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::MyWidget)

{

    ui->setupUi(this);

    QDialog *dialog =  new QDialog(this);

 

    dialog->setModal(TRUE); //将窗口设置为模态窗口

    dialog->show();

}

 

MyWidget::~MyWidget()

{

    delete ui;

}

exec()方法来显示一个模态对话框

void showModalWindow()

{

    QWidget* pWindow = new QWidget();

 

    QDialog* childDialog = new QDialog(pWindow);

    int resutl = childDialog ->exec();

    if (resutl == QDialog::Accepted)

    {

        qDebug() << "You Choose Ok";

    }

    else

    {

        qDebug() << "You Choose Cancel";

    }

 

 

    // 在关闭对话框之后,下面的代码才可以执行;

    qDebug() << "这是一个模态窗口";

}



Qt中添加资源文件如qml icon,修改pro文件

RESOURCES += \

    gallery.qml \

    pages/BusyIndicatorPage.qml \

    pages/ButtonPage.qml \

    pages/CheckBoxPage.qml \

    pages/ComboBoxPage.qml \

    pages/DelayButtonPage.qml \

    pages/DelegatePage.qml \

    pages/DialogPage.qml \

    pages/DialPage.qml \

    pages/FramePage.qml \

    pages/GroupBoxPage.qml \

    pages/PageIndicatorPage.qml \

    pages/ProgressBarPage.qml \

    pages/RadioButtonPage.qml \

    pages/RangeSliderPage.qml \

    pages/ScrollablePage.qml \

    pages/ScrollBarPage.qml \

    pages/ScrollIndicatorPage.qml \

    pages/SliderPage.qml \

    pages/SpinBoxPage.qml \

    pages/StackViewPage.qml \

    pages/SwipeViewPage.qml \

    pages/SwitchPage.qml \

    pages/TabBarPage.qml \

    pages/TextAreaPage.qml \

    pages/TextFieldPage.qml \

    pages/ToolTipPage.qml \

    pages/TumblerPage.qml \

    qtquickcontrols2.conf \

    icons/gallery/index.theme \

    icons/gallery/20x20/back.png \

    icons/gallery/20x20/drawer.png \

    icons/gallery/20x20/menu.png \

    icons/gallery/20x20@2/back.png \

    icons/gallery/20x20@2/drawer.png \

    icons/gallery/20x20@2/menu.png \

    icons/gallery/20x20@3/back.png \

    icons/gallery/20x20@3/drawer.png \

    icons/gallery/20x20@3/menu.png \

    icons/gallery/20x20@4/back.png \

    icons/gallery/20x20@4/drawer.png \

    icons/gallery/20x20@4/menu.png \

    images/arrow.png \

    images/arrow@2x.png \

    images/arrow@3x.png \

    images/arrow@4x.png \

    images/arrows.png \

    images/arrows@2x.png \

    images/arrows@3x.png \

    images/arrows@4x.png \

    images/qt-logo.png \

    images/qt-logo@2x.png \

    images/qt-logo@3x.png \

    images/qt-logo@4x.png



C++(Qt) 和 Word、Excel、PDF 交互总结

日常开发软件可能会遇到这类小众需求,导出数据到 Word、Excel 以及 PDF文件,如果你使用 C++ 编程语言,那么可以选择的方案不是很多,恰好最近刚好有这部分需求,整理下这段时间踩过的坑,方便后人


读写 Word

日常开发的软件使用最多的应该是导出数据到 Word 文档中,目前可以用的方案有这几种

1.jpg

C++(Qt) 和 Word、Excel、PDF 交互总结

没有十全十美的方案,任何方案都存在优点和缺点,下面来详细看下这几种方案的优缺点以及适用场景


XML 模板替换


原理:事先编辑好一份 Word 模板,需要替换内容的 地方预留好位置,然后使用特殊字段进行标记,后面使用代码进行全量替换即可完成


优点

代码量相对较少、导出速度快

跨平台,支持多个系统,系统不安装 office 也能导出;

支持图片以及固定格式导出;

缺点

导出格式固定,可扩展性不强,如果需求变化导出格式变了,那么模板也要跟着改变;

一种格式对应一份模板,如果导出格式较多,需要准备的模板文件较多,这样比较繁琐;

需要 Word 2003 以上版本;

举个栗子

我们先编辑一份 Word 模板文档,内容大概如下所示:

1.png

C++(Qt) 和 Word、Excel、PDF 交互总结

将该文档另存为 Word XML 文档 XML-Template.xml

读取文档内容进行变量替换

    QFile file("XML-Template.xml");

    if (!file.open(QIODevice::ReadOnly))

    {

        qDebug() << "open xxml file fail. " << file.errorString();

        return 0;

    }

    QByteArray baContent = file.readAll();

    file.close();

    QString strAllContent = QString::fromLocal8Bit(baContent);


    strAllContent.replace("$VALUE0", "1");

    strAllContent.replace("$VALUE1", QString::fromLocal8Bit("法外狂徒张三"));

    strAllContent.replace("$VALUE2", QString::fromLocal8Bit("考试不合格"));

    strAllContent.replace("$VALUE3", "2");

    strAllContent.replace("$VALUE4", QString::fromLocal8Bit("李四"));

    strAllContent.replace("$VALUE5", QString::fromLocal8Bit("合格"));


    QFile newFile("export.doc");

    if (!newFile.open(QIODevice::WriteOnly))

    {

        qDebug() << "file open fail." << newFile.errorString();;

        return 0;

    }


    newFile.write(strAllContent.toLocal8Bit());

    newFile.close();

保存替换后的内容,写入文件

1.png

C++(Qt) 和 Word、Excel、PDF 交互总结

可以看出来这种方式比较繁琐,重点是编辑固定的模板格式,而且编辑好后保存成XML格式后还需要继续调整,这种 XML 格式标签很多,不小心就修改错了,导致导出的文档打不开


这种方式适合模板内容不太复杂,内容较少的情况下使用


COM 组件方式


原理:采用 Micro Soft公开的接口进行通讯,进行读写时会打开一个 `Word进程来交互


COM 技术概述


Qt 为我们提供了专门进行交互的类和接口,使用 Qt ActiveX框架就可以很好的完成交互工作


优点

实现简单,快速上手;

缺点

导出写入速度慢,因为相当于打开 word 文档操作;

仅 Windows平台可用,其它平台失效;

需要程序运行的电脑安装 office word,否则调用失败

举个栗子

使用时需要引入对应的模块,在 pro 文件引入模块


QT  *= axcontainer

打开文档写入内容


QAxObject *pWordWidget = new(std::nothrow) QAxObject;


bool bResult = pWordWidget->setControl("word.Application");


if (!bResult)

{

    return false;

}


// 设置是否显示

pWordWidget->setProperty("Visible", false);


QAxObject *pAllDocuments = pWordWidget->querySubObject("Documents");


if(nullptr == pAllDocuments)

{

    return false;

}


// 新建一个空白文档

pAllDocuments->dynamicCall("Add (void)");


// 获取激活的文档并使用

QAxObject *pActiveDocument = pAllDocuments->querySubObject("ActiveDocument");

if(nullptr == pActiveDocument)

{

    return false;

}


// 插入字符串

QAxObject *pSelectObj = pWordWidget->querySubObject("Selection");

if (nullptr != pSelectObj)

{

    pSelectObj->dynamicCall("TypeText(const QString&)", "公众号:devstone");

}


……

可以看出来使用起来不难,对于新手友好一点,很多写入操作方法比较繁琐,需要自己重新封装一套接口


这种方案比较适合那些排版比较复杂,图片、文字、表格混排的场景下,而且内容都是动态变化的,可以很好的实现定制化

当然了它的缺点也不少,也有一些坑,有时候莫名其妙会失败,还有就是比如你电脑安装的 Word 没有激活,那么每次启动会弹激活窗口

还有就是这种方式要求所有的路径必须是本地化的,比如 D:\\Soft\test.png

使用前最好读取注册表判断当前电脑是否安装了 Office Word,如果没有安装,直接读取操作肯定会崩溃

这种方式同样适用于写入 Excel 文件,后面再说


HTML 方式


原理:这种方式得益于 Word支持 HTML格式导出渲染显示,那么反向也可以支持,需要我们拼接 HTML格式内容,然后写入文件保存成 .doc格式


优点

跨平台,不仅限于 Windows平台,代码可扩展性比较好

导出速度快、代码可扩展;

缺点

字符串拼接 HTML 容易出错,缺失标签导出后无法显示;

插入的图片是本地图片文件的链接,导出的 word文档拷贝到其它电脑图片无法显示

举个栗子

QString HTML2Word::getHtmlContent()

{

    QString strHtml = "";

    strHtml += "<html>";

    strHtml += "<head>";

    strHtml += "<title>测试生成word文档</title>";

    strHtml += "<head>";

    strHtml += "<body style=\"bgcolor:yellow\">";

    strHtml += "<h1 style=\"background-color:red\">测试qt实现生成word文档</h1>";

    strHtml += "<hr>";

    strHtml += "<p>这里是插入图片<img src=\"D:\\title.jpg" alt=\"picture\" width=\"100\" height=\"100\"></p>";

    strHtml += "</hr>";

    strHtml += "</body>";

    strHtml += "</html>";


    return strHtml;

}


// 保存写入文件

QFile file("D:/htmp2Word.doc");

if (!file.open(QIODevice::WriteOnly))

{

    return false;

}


QTextStream out(&file);

out << getHtmlContent();

file.close();

1.png

C++(Qt) 和 Word、Excel、PDF 交互总结

这种方式难点在于 HTML格式拼接,任何缺失字段都会导致导出失败,适合小众需求下导出


图片问题其实可以手动进行转化,文档导出成功后手动拷贝内容到新的文档,这样图片就真正插入到文档中,文档发送给别人也不会丢失图片了


还有一个坑就是:如果你使用 WPS 打开导出的文档,默认显示的是 web视图,需要手动进行调整


某些电脑分辨率变化也会导致生成的文档中字体等产生变化


第三方开源库

可以使用的第三方库几乎没有,网络上找到的有这么几个


OpenOffice: 兼容性差,集成调用难度大

LibOffice: 太庞大,不容易集成

DuckX: 太小众,只能简单的使用

docx:小众库

DuckX库 docx库


在读写 Word这部分,C++ 基本没有可以使用的第三方库,不像其他语言Java、C#、Python有很多可以选择,这个痛苦也只有 C++ 程序员能够理解了吧


所以怎么选择还是看自己项目需求吧,没有十全十美的方案


上面说了这么多,都是导出生成 Wrod,那么下面来看看有那些方式可以读取显示 Word内容


这种需求应该不会很多,而且显示难度更大一些


使用 COM组件方式,即采用 QAxWidget框架显示 office 文档内容,本质上就是在我们编写的 Qt 界面上嵌入 office 的软件,这种方式其实和直接打开 Word查看没有啥区别,效果、性能上不如直接打开更好一些


目前一般都会采用折中方案,把 Word 转为 PDF 进行预览加载显示,我们知道 PDF 渲染库比较多,生态相对来说要好一些,在选择上就更广泛些,如何使用后面部分有专门介绍 PDF章节


读写 Excel

目前有一个支持比较好的第三方库可以使用,整体使用基本可以满足日常使用


QXlsx

1.png

C++(Qt) 和 Word、Excel、PDF 交互总结

这款开源库支持跨平台,Linux、Windows、Mac、IOS、Android,使用方式支持动态库调用和源码直接集成,非常方便


编译支持 qmake和cmake,可以根据你自己的项目直接集成编译,读写速度非常快


QXlsx::Document xlsx;


// 设置一些样式

QXlsx::Format titleFormat;

titleFormat.setBorderStyle(QXlsx::Format::BorderThin);  // 边框样式

titleFormat.setRowHeight(1,1,30);   // 设置行高

titleFormat.setHorizontalAlignment(QXlsx::Format::AlignHCenter);   // 设置对齐方式


// 插入文本

xlsx.write(1,1, "微信公众号:devstone", titleFormat);


// 合并单元格

xlsx.mergeCells(QXlsx::CellRange(2,1,4,4), titleFormat);


// 导出保存

xlsx.saveAs("D:/xlsx_export.xlsx");


// 添加工作表

xlsx.addSheet("devstone");

可以看到上手非常容易、各个函数命名也贴近 Qt Api,是一款非常良心的开源软件



PS:注意该软件使用 MIT 许可协议,这样对于很多个人或者公司来说非常良心,意味着你可以无偿使用、修改该项目,但是必须在你项目中也添加同样的 MIP许可


上面也提到了,还可以使用 COM 组件的方式读写 Excel,不过有了这款开源库基本就可以告别 COM组件方式了


读写 PDF

PDF相关开源库挺多的,给了 C++ 程序员莫大的帮助,目前可用的主要有这些

a1e65acca5fe4c3994f0fedc8692b5ad.png

C++(Qt) 和 Word、Excel、PDF 交互总结

其中 mupdf和 poppler 属于功能强大但是很难编译的那种,需要有扎实的三方库编译能力,否则面对 n 个依赖库会无从下手


不过可喜的是 Github 上有两个开源库可以供选择


qpdf 库

这个库其实封装了 pdf.js库,使用 WebEngine来执行 JavaScript进而加载文件

1.png

C++(Qt) 和 Word、Excel、PDF 交互总结

项目地址


直接从本地文件加载;

支持从内存数据直接加载渲染 PDF 内容;

这种方式对环境有特殊要求了,如果你的项目使用的 Qt 版本不支持 WebEngine,那么就无法使用


qtpdf 库

这个库是 Qt 官方亲自操刀对第三方库进行了封装,暴露的 API 和 Qt 类似,使用起来非常舒服


Qt 官方

1.png

C++(Qt) 和 Word、Excel、PDF 交互总结

代码结构以及使用 Demo

2.png

C++(Qt) 和 Word、Excel、PDF 交互总结

小试牛刀

关于如何使用,官方已经给了我们非常详细的步骤了,直接跟着下面几步就 OK 了


官方教程


git clone git://code.qt.io/qt-labs/qtpdf

cd qtpdf

git submodule update --init --recursive

qmake

make

cd examples/pdf/pdfviewer

qmake

make


./pdfviewer /path/to/my/file.pdf

可以看到使用了谷歌开源的 pdfium 三方库,编译时需要单独更新下载这个库,因为某些原因可能你无法下载,不过好在有人在 GitHub上同步了这个仓库的镜像,有条件还是建议直接下载最新稳定版的

1.png

C++(Qt) 和 Word、Excel、PDF 交互总结

可正常访问的仓库地址:https://github.com/PDFium/PDFium


相关类可以看这个文档:https://developers.foxit.com/resources/pdf-sdk/c_api_reference_pdfium/modules.html


使用QT5创建动态库以及如何在程序运行时加载动态库

本文介绍了如何使用QT5创建动态库,以及如何在程序运行时加载动态库。这种方式类似于Qt的应用程序插件机制,插件的方式可以直接获取动态库中类的实例,而此方式只能到函数级,从库中获取函数的地址并转换为对应的函数指针来执行。

下面以实例进行介绍:


创建动态库

新建库项目,pro文件如下:

QT += widgets

TEMPLATE = lib #输出为动态库

CONFIG += c++11

DEFINES += QT_DEPRECATED_WARNINGS

TARGET= mplugin

SOURCES += \

    mplugin.cpp

HEADERS += \

    mplugin.h


将cpp中需要导出的函数用extern “C”

#ifndef MPLUGIN_H

#define MPLUGIN_H


#include <QObject>


/* QT在windows下导出符号必须要用“__declspec(dllexport)”修饰,否则QLibrary会加载失败 */

#ifdef Q_OS_WIN

#define MY_EXPORT __declspec(dllexport)

#else

#define MY_EXPORT

#endif


class Mplugin

{

public:

    Mplugin();

    void show(QString s);

};


#endif // MPLUGIN_H


#include "mplugin.h"

#include <QMessageBox>

#include <QWidget>

Mplugin::Mplugin()

{

}

//调用此函数显示一个消息框

void Mplugin::show(QString s)

{

    QWidget w;

    QMessageBox::warning(&w,"warning!",s);

}

extern "C" MY_EXPORT void myShow(QString s)

{

    Mplugin plu;

    plu.show(s);

}

使用动态库

新建另一项目,源码如下:


#include <QApplication>

#include <QLibrary>


int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    /* 加载库文件,可以使用绝对路径或相对路径,mplugind.dll为上面动态库项目编译产生的二进制库文件 */

    QLibrary lib("D:/Documents/build-mmplugin-Desktop_x86_windows_msvc2019_pe_64bit-Debug/mplugin/debug/mplugind.dll");

    typedef void (*FUN)(QString); //此格式同导出符号的格式


/* 解析动态库中的符号,如果不存在会返回NULL */

    FUN fun = (FUN)lib.resolve("myShow");

    if (fun) {

        fun("test"); //运行到此处会显示消息框

    }

    return a.exec();

}

注意事项

编写为动态库的代码中,所有需要外部调用的函数必须为C函数,Cpp中需要使用extern "C"导出。

windwos下导出的符号需要使用extern “C” __declspec(dllexport)**来修饰。可以在头文件中加入如下的代码段,然后在源代码文件中所有需要导出的CPP函数加上extern “C” MY_EXPORT即可实现不修改代码就可以在不同的系统下使用。

  #ifdef Q_OS_WIN

  #define MY_EXPORT __declspec(dllexport)

  #else

  #define MY_EXPORT

  #endif


Qt小技巧14.Qt5.12.x编译Mysql插件驱动

1 遇到的问题

Qt后面的版本都没有直接带Mysql插件驱动了,应该是协议的原因,需要我们自己手动编译下,默认是这样子的:

1.png

打印下QSqlDatabase::drivers()结果如下:


("QSQLITE", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

很显然,默认是不包含Mysql的插件驱动。

如果使用QSqlDatabase::addDatabase("QMYSQL")去加载插件,就会报下面的错误:


QSqlDatabase: QMYSQL driver not loaded

QSqlDatabase: available drivers: QSQLITE QODBC QODBC3 QPSQL QPSQL7

这个错误是不是很经典,网上也有很多编译插件的教程,但是居然没有一篇文章是完全能够解决问题的,一定要综合多篇文章一起才行,所以还得要自己多动动手才行,下面我们来看看解决办法。


2 解决办法

2.1 下载源码

如果安装Qt时勾选了源码,这步就可忽略,但是大多数人安装时是没有勾选的,可以自己去下载一下:

2.png

附加地址https://download.qt.io/archive/qt/5.12/5.12.6/submodules/

这里根据自己的版本下载即可。


2.2 解压源码

如果安装Qt时勾选了源码,这步也可忽略,这里只提一点,解压时一定要整个目录一起解压,不然会影响后面的配置:

3.png

解压目录自己可以随便指定,一定要全部一起解压,我这里解压到了D:/QtPro目录下。


2.3 打开mysql.pro工程

首先打开工程文件:


打开后无非会遇到下面两种错误:


Project ERROR: Library 'mysql' is not defined.

Cannot read D:/qtsqldrivers-config.pri: No such file or directory

为了解决这个问题,接下来我们修改下配置。


2.4 修改工程文件

修改mysql.pro文件如下:

4.png

这里额外提示下,如果本地没有安装mysql数据库,可以下载对应的zip包,里面有对应的头文件和库文件,例如我这里服务器的版本是mysql-5.7.9,所以我下载的版本是mysql-5.7.9-winx64.zip,并解压到了D:/Program目录下:

5.png

然后,修改qsqldriverbase.pri文件如下:

6.png

好了,上面提到的两个错误到这里就解决了,下面开始编译。


2.5 编译一下

编译完成后,会在指定的build目录下生成插件驱动:


7.png

2.6 部署一下

首先将编译生成的mysql插件驱动复制到sqldrivers目录下:

8.png

然后再将Mysql驱动复制到Qt安装目录bin目录下:

9.png


3 验证一下

首先使用QSqlDatabase::drivers()打印下:


("QSQLITE", "QMYSQL", "QMYSQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7")

很显然,Mysql插件驱动已经安装成功了。

下面测试下能不能连接到数据库:


    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");

    db.setHostName("192.168.10.5");

    db.setUserName("root");

    db.setPassword("123456");

    db.setPort(3306);

    qDebug() << db.open() << db.lastError().text();

结果打印如下:


好啦,搞定!此方法应该对其他版本也适用,需要自行去测试。

最后提一下,我们编译的插件qsqlmysql.dll是依赖于Mysql驱动libmysql.dll的,两者缺一不可,插件就像是中间转换层一样,会对Mysql驱动进行适配,直接调用是不行的。


QListWidgetItem自定义添加控件


最近在做一个小的项目,其中想使用qlistwidget实现自定义数据的显示,自定义行要求有图片列,文字列,按钮列等,于是研究了一下qlistwidget的使用。


要想实现上述要求,必须按如下顺序实现


1、这里重点是ui->listWidget->addItem(item);的使用,必须先将QListWidgetItem对象加入qlistwidget。


QListWidgetItem *item=new QListWidgetItem;

 

    item->setSizeHint(QSize(10,100));

    ui->listWidget->addItem(item);

2、定义完如上,接下来就是下面这个函数的使用


void QListWidget::setItemWidget(QListWidgetItem * item, QWidget * widget)


这个函数实际上是将QWidget对象与QListWidgetItem对象进行绑定,具体操作如下:


 

    QWidget *w = new QWidget;

    QHBoxLayout *layout=new QHBoxLayout;

    QPushButton *pushButton=new QPushButton(w);

    QCheckBox *checkBox=new QCheckBox(w);

    layout->addWidget(checkBox);

    layout->addWidget(pushButton);

    w->setLayout(layout); 

ui->listWidget->setItemWidget(item,w);

基本效果如下,当然你也可以在ui界面设计QWidget对象,使其布局达到自己需求的样式。

1.png

Qt listWidget通过setItemWidget添加widget后无法捕获鼠标的解决方法

1、首先安装过滤器要安装到viewport上如下


    this->listWidget->viewport()->installEventFilter(this);

    this->listWidget->viewport()->setMouseTracking(true);


2、将添加到listwidget上的widget上的所有控件都设置setMouseTracking,包括widget:


QWidget *widget = new QWidget();

    widget->setObjectName("widget");


    QGridLayout *gridLayout = new QGridLayout;

    QHBoxLayout *hBoxLayout = new QHBoxLayout;


    QLabel *labelName = new QLabel(name);

    QLabel *labelDate = new QLabel(date);

    QLabel *labelLabel = new QLabel(label);


    labelName->setObjectName(LABEL_TASK_NAME);

    labelDate->setObjectName(LABEL_DATE_NAME);

    labelLabel->setObjectName(LABEL_LABEL_NAME);


    labelName->setFrameShape(QFrame::NoFrame);

    labelDate->setFrameShape(QFrame::NoFrame);

    labelLabel->setFrameShape(QFrame::NoFrame);


    labelName->setMouseTracking(true);

    labelDate->setMouseTracking(true);

    labelLabel->setMouseTracking(true);


    QFont font = labelName->font();

    font.setBold(true);

    labelName->setFont(font);



    hBoxLayout->addWidget(labelDate);

    hBoxLayout->addWidget(labelLabel);


    gridLayout->addWidget(labelName,0,0);

    gridLayout->addLayout(hBoxLayout,1,0);


    widget->setStyleSheet(ITEM_DEFAULT_BG);

    widget->setLayout(gridLayout);


    widget->setMouseTracking(true);

    this->listWidget->setItemWidget(this->listWidget->item(itemCount),widget);



自定义的QListWidget

第一:添加我们的自定义ItemWidget


QListWidgetItem* listItem = new QListWidgetItem(ui.listWidget);

CustomItemWidget* customWidget = new CustomItemWidget();

listItem->setSizeHint(QSize(330, 80));

ui.listWidget->addItem(listItem);

ui.listWidget->setItemWidget(listItem, customWidget);



第二:点击我们的itemWidget获取控件的一些内容 获取自定义ItemWidget里面的内容


connect(ui.listWidget, &QListWidget::itemClicked, this, &QCustomListWidget::SLOT_ListWidget_ItemClicked);


void QCustomListWidget::SLOT_ListWidget_ItemClicked(QListWidgetItem* item)

{

    CustomItemWidget* customItemWidget = static_cast<CustomItemWidget*>(ui.listWidget->itemWidget(item));

    if (customItemWidget == nullptr)

        return; 


    QList<QPushButton*> listBtns = customItemWidget->findChildren<QPushButton*>();


    for (auto & btn:listBtns)

    {

        qDebug() <<btn->objectName() << btn->text();

    }



第三步:我们需要响应我们ItemWidget发出来的控件的点击信号,获取其他控件的一些信号


CustomItemWidget* customWidget = new CustomItemWidget();


connect(customWidget, &CustomItemWidget::EmitPushButtonClicked, this, [=](){

        QMessageBox::information(nullptr, "456", "123");

        })


完整代码如下:

QCustomListWidget .ui

11.png

QCustomListWidget .h


#pragma once


#include <QtWidgets/QWidget>

#include "ui_QCustomListWidget.h"

#include <QListWidgetItem>

#include <QListWidget>

#include "CustomItemWidget.h"

#include <QMessageBox>


class QCustomListWidget : public QWidget

{

    Q_OBJECT


public:

    QCustomListWidget(QWidget *parent = Q_NULLPTR);


private slots:

    void SLOT_ListWidget_ItemClicked(QListWidgetItem *item);


private:

    Ui::QCustomListWidgetClass ui;

};



QCustomListWidget .cpp


#include "QCustomListWidget.h"


QCustomListWidget::QCustomListWidget(QWidget *parent)

    : QWidget(parent)

{

    ui.setupUi(this);


    //第一个ItemWidget

    QListWidgetItem* listItem = new QListWidgetItem(ui.listWidget);

    CustomItemWidget* customWidget = new CustomItemWidget();

    listItem->setSizeHint(QSize(330, 80));

    ui.listWidget->addItem(listItem);

    ui.listWidget->setItemWidget(listItem, customWidget);

   

    connect(customWidget, &CustomItemWidget::EmitPushButtonClicked, this, [=](){

        QMessageBox::information(nullptr, "456", "123");

        })


    connect(ui.listWidget, &QListWidget::itemClicked, this, &QCustomListWidget::SLOT_ListWidget_ItemClicked);

  

}


void QCustomListWidget::SLOT_ListWidget_ItemClicked(QListWidgetItem* item)

{

    CustomItemWidget* customItemWidget = static_cast<CustomItemWidget*>(ui.listWidget->itemWidget(item));

    if (customItemWidget == nullptr)

        return; 


    QList<QPushButton*> listBtns = customItemWidget->findChildren<QPushButton*>();


    for (auto & btn:listBtns)

    {

        qDebug() <<btn->objectName() << btn->text();

    }


}


CustomItemWidget .ui


1.png

CustomItemWidget .h


#pragma once


#include <QWidget>

#include "ui_CustomItemWidget.h"

#include "CustomItemSubWidget.h"

#include <QMessageBox>

#include <QDebug>


class CustomItemWidget : public QWidget

{

Q_OBJECT


public:

CustomItemWidget(QWidget* parent = Q_NULLPTR);

~CustomItemWidget();


signals:

void EmitPushButtonClicked(int);


public slots:

void on_pushButton_1_clicked();


private:

Ui::CustomItemWidget ui;


};


CustomItemWidget .cpp


#include "CustomItemWidget.h"


CustomItemWidget::CustomItemWidget(QWidget* parent)

: QWidget(parent)

{

ui.setupUi(this);

}


CustomItemWidget::~CustomItemWidget()

{


}


void CustomItemWidget::on_pushButton_1_clicked()

{

emit EmitPushButtonClicked(10);

}


效果图:

添加自定义ItemWidget的效果

2.png

点击ItemWidget的第一个按钮 的 效果

3.png

点击ItemWidget的效果图:

4.png



QListWidget自定义item的两种方式



QListWidget自定义item的两种方式(一)——使用Delegate

一. 效果

使用Delegate自定义QListWidget的Item,测试程序效果如下(下面内容包含测试程序源代码):



二. 步骤

使用Delegate自定义QListWidget的Item的步骤如下:


1. 派生QStyledIemDelegete得到一个子类


class CMyListWidgetDelegate : public QStyledItemDelegate

 


2. 为QListWidget指定一个delegate


ui->listWidget->setItemDelegate(new CMyListWidgetDelegate(ui->listWidget));

 


3. QStyledIemDelegete派生得到的子类,重写paint和sizehint两个函数


重写这两个函数,来自定义Item的显示。


//决定如何绘图

virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

 

//决定单元格的推荐大小

virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;

备注:如果没有给QListWidget设置delegate,QlistWidget也会给自己创建一个默认的delegate。


 


三. 测试程序及效果

MyListWidgetDelegate.h


#ifndef MYLISTWIDGETDELEGATE_H

#define MYLISTWIDGETDELEGATE_H

 

#include <QStyledItemDelegate>

 

class CMyListWidgetDelegate : public QStyledItemDelegate

{

    Q_OBJECT

 

public:

    CMyListWidgetDelegate(QObject* pParent);

    virtual ~CMyListWidgetDelegate();

 

private:

    //决定如何绘图

    virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

 

    //决定单元格的推荐大小

    virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;

 

};

 

#endif // MYLISTWIDGETDELEGATE_H

MyListWidgetDelegate.cpp


#include "MyListWidgetDelegate.h"

#include <QPainter>

 

CMyListWidgetDelegate::CMyListWidgetDelegate(QObject *pParent)

    :QStyledItemDelegate(pParent)

{

 

}

 

CMyListWidgetDelegate::~CMyListWidgetDelegate()

{

 

}

 

void CMyListWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const

{

    //QStyledItemDelegate::paint(painter, option, index);

 

    QRect rect = option.rect; // 目标矩形

    rect.adjust(2,2,-2,-2); // 缩小一圈,留出空白间隔

 

    // 取得该项对应的数据

    QString qstrFileName = index.data(Qt::DisplayRole).toString();

    QString qstrRecordPic = index.data(Qt::UserRole+1).toString();

    int iFileSize = index.data(Qt::UserRole+2).toInt();

 

    // 状态显示: 若该项被选中

    if(option.state & QStyle::State_Selected)

    {

        painter->setBrush(QColor(0xCC, 0xAA, 0xAA));

        painter->drawRoundedRect(rect, 2, 2);

        painter->setBrush(Qt::NoBrush);

    }

 

    // 图片显示

    {

        QRect dst = rect;

        dst.setRight(rect.left() + 40);

        QRect area(0,0,24,24);

        area.moveCenter(dst.center());

 

        QPixmap pixmapPic(qstrRecordPic);

        painter->drawPixmap(area, pixmapPic);

 

    }

 

    // 文件名显示

    {

        QRect dst = rect;

        dst.setLeft(rect.left() + 40);

        dst.setBottom(rect.top() + 20);

        painter->drawText(dst, Qt::AlignLeft | Qt::AlignVCenter, qstrFileName);

    }

 

    // 文件大小

    {

        QRect dst = rect;

        dst.setLeft(rect.left() + 40);

        dst.setTop(rect.top() + 20);

        painter->drawText(dst, Qt::AlignLeft | Qt::AlignVCenter, QString::number(iFileSize));

    }

 

}

 

QSize CMyListWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const

{

    //return QStyledItemDelegate::sizeHint(option, index);

 

    QSize size = QStyledItemDelegate::sizeHint(option, index);

    size.setHeight(40);

    return size;

}

MainWidget.h


#ifndef MAINWIDGET_H

#define MAINWIDGET_H

 

#include <QWidget>

 

namespace Ui {

class CMainWidget;

}

 

class CMainWidget : public QWidget

{

    Q_OBJECT

 

public:

    explicit CMainWidget(QWidget *parent = 0);

    ~CMainWidget();

 

private:

    Ui::CMainWidget *ui;

 

    void AddItem(const QString& qstrDownloadFileName, const QString& qstrRecordPic, int iFileSize);

};

 

#endif // MAINWIDGET_H

MainWidget.cpp


#include "MainWidget.h"

#include "ui_MainWidget.h"

#include "MyListWidgetDelegate.h"

 

CMainWidget::CMainWidget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::CMainWidget)

{

    ui->setupUi(this);

 

    //添加数据

    AddItem("Video1", ":/images/video_1.png", 1024);

    AddItem("Video2", ":/images/video_2.png", 2048);

    AddItem("Video3", ":/images/video_3.png", 3072);

    AddItem("Video4", ":/images/video_4.png", 4096);

    AddItem("Video5", ":/images/video_1.png", 5120);

 

    //使用自定义绘图

    ui->listWidget->setItemDelegate(new CMyListWidgetDelegate(ui->listWidget));

 

}

 

CMainWidget::~CMainWidget()

{

    delete ui;

}

 

void CMainWidget::AddItem(const QString& qstrDownloadFileName, const QString& qstrRecordPic, int iFileSize)

{

    QListWidgetItem* pItem = new QListWidgetItem();

    if(nullptr != pItem)

    {

        pItem->setData(Qt::DisplayRole, qstrDownloadFileName);

        pItem->setData(Qt::UserRole + 1, qstrRecordPic);

        pItem->setData(Qt::UserRole + 2, iFileSize);

        ui->listWidget->addItem(pItem);

    }

}

main.cpp


#include "MainWidget.h"

#include <QApplication>

 

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    CMainWidget w;

    w.show();

 

    return a.exec();

}

程序运行效果如下:



该测试程序,如果没有给QListWidget设置delegate(即注释掉MainWidget.cpp中的ui->listWidget->setItemDelegate(new CMyListWidgetDelegate(ui->listWidget));)


则只会显示Qt::DisplayRole的数据,效果如下:




QListWidget自定义item的两种方式(二)——使用QWidget作为item

一. 效果

使用QWidget作为QListWidget的Item,测试程序效果如下(下面内容包含测试程序源代码):


1.png


 


二. 步骤

使用QWidget作为QListWidget的Item的步骤如下


1. 定义要作为QListWidget的Item的自定义的QWidget


class CItemWidget : public QWidget

2. 创建自定义的QWidget并和QListWidget的Item关联起来


CItemWidget* pItemWidget = new CItemWidget(this);

QListWidgetItem* pItem = new QListWidgetItem();

pItem->setSizeHint(QSize(350, 40));

ui->listWidget->addItem(pItem);

ui->listWidget->setItemWidget(pItem, pItemWidget);

注意:如果要QListWidget的每行显示多个自定义的QWidget,则需进行下面的设置


ui->listWidget->setResizeMode(QListView::Adjust);

ui->listWidget->setViewMode(QListView::IconMode);

三. 测试程序及效果

ItemWidget.h


#ifndef ITEMWIDGET_H

#define ITEMWIDGET_H

 

#include <QWidget>

 

namespace Ui {

class CItemWidget;

}

 

class CItemWidget : public QWidget

{

    Q_OBJECT

 

public:

    explicit CItemWidget(QWidget *parent = 0);

    ~CItemWidget();

 

    //设置数据

    void SetData(const QString& qstrFileName, int iFileSize, const QString& qstrPic);

 

private:

    Ui::CItemWidget *ui;

 

};

 

#endif // ITEMWIDGET_H

ItemWidget.cpp


#include "ItemWidget.h"

#include "ui_ItemWidget.h"

 

CItemWidget::CItemWidget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::CItemWidget)

{

    ui->setupUi(this);

}

 

CItemWidget::~CItemWidget()

{

    delete ui;

}

 

void CItemWidget::SetData(const QString& qstrFileName, int iFileSize, const QString& qstrPic)

{

    ui->label_fileName->setText(qstrFileName);

    ui->label_fileSize->setText(QString::number(iFileSize));

 

    QPixmap pixmapPic(qstrPic);

    int iWidth = ui->label_pic->width();

    int iHeight = ui->label_pic->height();

    QPixmap pixmapPicFit = pixmapPic.scaled(iWidth, iHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//饱满填充

    ui->label_pic->setPixmap(pixmapPicFit);

}

MainWidget.h


#ifndef MAINWIDGET_H

#define MAINWIDGET_H

 

#include <QWidget>

 

namespace Ui {

class CMainWidget;

}

 

class CMainWidget : public QWidget

{

    Q_OBJECT

 

public:

    explicit CMainWidget(QWidget *parent = 0);

    ~CMainWidget();

 

private:

    Ui::CMainWidget *ui;

 

    //添加Item

    void AddItem(const QString& qstrFileName, int iFileSize, const QString& qstrPic);

};

 

#endif // MAINWIDGET_H

MainWidget.cpp


#include "MainWidget.h"

#include "ui_MainWidget.h"

#include "ItemWidget.h"

 

CMainWidget::CMainWidget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::CMainWidget)

{

    ui->setupUi(this);

 

    ui->listWidget->setResizeMode(QListView::Adjust);

    ui->listWidget->setViewMode(QListView::IconMode);

 

    AddItem("Video1", 1024, ":/images/video_1.png");

    AddItem("Video2", 2048, ":/images/video_2.png");

    AddItem("Video3", 3072, ":/images/video_3.png");

    AddItem("Video4", 4096, ":/images/video_4.png");

    AddItem("Video5", 5120, ":/images/video_1.png");

}

 

CMainWidget::~CMainWidget()

{

    delete ui;

}

 

void CMainWidget::AddItem(const QString& qstrFileName, int iFileSize, const QString& qstrPic)

{

    CItemWidget* pItemWidget = new CItemWidget(this);

    pItemWidget->SetData(qstrFileName, iFileSize, qstrPic);

    QListWidgetItem* pItem = new QListWidgetItem();

    pItem->setSizeHint(QSize(350, 40));

    ui->listWidget->addItem(pItem);

    ui->listWidget->setItemWidget(pItem, pItemWidget);

}

main.cpp


#include "MainWidget.h"

#include <QApplication>

 

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    CMainWidget w;

    w.show();

 

    return a.exec();

}

程序运行效果如下:


Top