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

QT实测经验教训

  • C/C++
  • 2022-04-07
  • 1365人已阅读
摘要

Qt调用自己的 Activity获得返回值

Qt调用java代码及数据类型对照

Java调用Qt

java jin总结

Qt控件内加控件

Qml控件占据剩余空间

ToolButton

发布QML程序

Qt播放视频报错 DirectShowPlayerService::doRender: Unresolved error code 0x80040266

qt android动态权限申请


QT调用自己的Activity得到返回值

activity在AndroidManifest.xml中定义  

<activity android:name=".XNActivity">

            <intent-filter>

                <action android:name="org.qtproject.example.HelloWindow.XNActivity"/> <!--一定要指定通过名称调用-->

                <category android:name="android.intent.category.DEFAULT"/><!--一定要指定默认值-->

            </intent-filter>

        </activity>

XNActivity布局代码

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:background="#ff0000ff"


    >

    <Button

        android:layout_width="match_parent"

        android:text="@string/click"

        android:layout_height="wrap_content"

        android:id="@+id/mybt"


        />



</LinearLayout>

XNActivity.Java代码

package org.qtproject.example.HelloWindow;


import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;


import org.qtproject.qt5.android.bindings.QtActivity;


public class XNActivity extends Activity {


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_xn);

        findViewById(R.id.mybt).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                Intent intent =new Intent(XNActivity.this, QtActivity.class);

                intent.putExtra("select","123中华民族");

                setResult(Activity.RESULT_OK,intent);

                finish();//结束当前Activity,返回上一个activity


            }

        });


    }

}


QT端调用代码


ActivityResult *m_ActivityResult;

 QAndroidJniEnvironment env;

    if(m_ActivityResult!=NULL)

    {

     delete  m_ActivityResult;


    }

    m_ActivityResult = new ActivityResult(0);

    connect(m_ActivityResult,SIGNAL(sendData(QString)),this,SLOT(sendData(QString)));


QtAndroid::runOnAndroidThread([=](){

    QAndroidJniObject action = QAndroidJniObject::fromString("org.qtproject.example.HelloWindow.XNActivity");

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

    QtAndroid::startActivity(intent, 0, m_ActivityResult);

 });



ActivityResult .h


#ifndef ACTIVITYRESULT_H

#define ACTIVITYRESULT_H


#include <QAndroidActivityResultReceiver>

class ActivityResult :  public QObject,public QAndroidActivityResultReceiver

{

    Q_OBJECT


  signals:


      void sendData(QString);


  public:


      int requestId;


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


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

};


#endif // ACTIVITYRESULT_H



ActivityResult .cpp

#include <QDebug>


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


{


    /*

    QString myStr=QString("receiverRequestCode %1 resultCode %2").arg(receiverRequestCode).arg(resultCode);

    emit sendData(myStr);

            return;

*/

    if(receiverRequestCode == requestId)

    {

        if(resultCode ==-1)// RESULT_OK)

        {

            QAndroidJniObject name=QAndroidJniObject::fromString("select");

            QAndroidJniObject str = data.callObjectMethod("getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;",name.object<jstring>());

            if(str==NULL)

            {

                 emit sendData(tr("返回为空"));

                return;

            }

            str.toString();

            emit sendData(str.toString());

            return;           


        }

        else

        {

            //some code here

        }

    }

}



QT调用JAVA代码及数据类型对照

数据类型签名对照表:


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;




MyJavaClass.java

package com;


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 mystatic(int n)             //静态函数;

    {

        return n+1;

    }

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

    {

        return 12345678;

    }

    public int setNum( int n )                     //有参调用

    {

        return n;

    }


    public String getStr(String perfix)                        //返回对象

    {

        String teststr = new String("hello " + perfix);

        return teststr;

    }


    boolean boolTest()                              //返回bool类型

    {

        return true;

    }


}

QT调用

 int n=5;

    jint A=QAndroidJniObject::callStaticMethod<jint>

                               ("com/MyJavaClass" // class name

                               , "mystatic" // method name

                               , "(I)I" // signature

                               , n);

    QString s = QString::number(A);

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

    QAndroidJniObject QAndroidJniObject_MyJavaClass_obj("com/MyJavaClass");

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

    QString ss = QString::number(B);

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

    return;


Java调用QT

** Java QtActivity 代码**

package org.qtproject.qt5.android.bindings;

public class QtActivity extends Activity

{

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


JAVA代码

package com.qdian.testmyapp;

public class MainActivity extends AppCompatActivity {

{

public native String stringFromJNI();

}


C++代码

#include <jni.h>

#include <string>


extern "C" JNIEXPORT jstring JNICALL

Java_com_qdian_testmyapp_MainActivity_stringFromJNI(

        JNIEnv* env,

        jobject /* this */) {

    std::string hello = "Hello from C++";

    return env->NewStringUTF(hello.c_str());

}



java  jni c++ 工作过程中常用的一些东西,在这里总结一下

获取field

env->getFieldID(class,"name",field类型);

获取method

env->getMethodID(class,"方法名",(参数)返回类型 );

1.jpg

还有三个特殊的string  object  arraylist  


string---Ljava/lang/String


object---Ljava/lang/Object


array---Ljava/util/ArrayList


char *转jstring

JNIEnv* env,

std::string hello = "Hello from C++";

jstring str=env->NewStringUTF(hello.c_str());


jstring转char *

JNIEnv* env,

jboolean isCopy;

//evn:JNI 接口指针,data:Java 字符串对象,isCopy:指向布尔值的指针

char *startData = (*evn)->GetStringUTFChars(evn, data, &isCopy);

//使用了GetStringUTFChars一定要调用ReleaseStringChars函数释放资源

//env:JNI 接口指针,data:Java 字符串对象,指向UTF-8 字符串的指针

(*evn)->ReleaseStringUTFChars(evn,data,startData);



JNIEnv的使用在C和C++中的区别

2人收藏此文章, 我要收藏发表于1个月前(2012-10-09 18:59) , 已有 121次阅读 ,共 0个评论

对于JNIEnv *env来说,在C中调用:


(*env)->NewStringUTF(env, "Hello from JNI!");


而在C++中如果按照上述调用则会发生'base operand of '->' has non-pointer type '_JNIEnv''错误,需要如下调用:


env->NewStringUTF("Hello from JNI!");




JNIEXPORT jstring JNICALL getString(JNIEnv *evn, jobject jobject1, jstring data) {

    jboolean isCopy;

    //evn:JNI 接口指针,data:Java 字符串对象,isCopy:指向布尔值的指针

    char *startData = (*evn)->GetStringUTFChars(evn, data, &isCopy);

    char *endData="Hello";

    int size1=strlen(startData);

    int size2= strlen(endData);

    char resultData[256];

    int index=-1;

    for(int k=0;k<size1;k++){

        resultData[++index]=startData[k];

    }

    for(int k=0;k<size2;k++){

        resultData[++index]=endData[k];

    }

    jstring  result=(*evn)->NewStringUTF(evn,resultData);

    //使用了GetStringUTFChars一定要调用ReleaseStringChars函数释放资源

    //env:JNI 接口指针,data:Java 字符串对象,指向UTF-8 字符串的指针

    (*evn)->ReleaseStringUTFChars(evn,data,startData);

    return  result;

}

jni 方法签名: {"getString","(Ljava/lang/String;)Ljava/lang/String;",(void*)getString},

 

java调用 getString("jni-") 返回:jni-hello




使用JNIEnv全局变量调用FindClass()等函数发生crash

jclass remotedesktopclass = sJniEnv->FindClass("com/xxx/xxx/xxx/xxx");  

sJniEnv是一个全局变量,但其实不应该全局保存JNIEnv。

这是一个和thread紧密关联的变量,在其他线程中使用就会crash。

【解决方式】

JNIEXPORT void JNICALL Java_xx_xx_xx_xx_xxx(JNIEnv * jniEnv, jobject object, jstring jflowNum, jstring judid){  

     jniEnv->GetJavaVM(&javaVm);  

     jclass tmp = jniEnv->FindClass("com/nationsky/rc/util/RemoteDesktopUtil");  

     remotedesktopclass = (jclass)jniEnv->NewGlobalRef(tmp);  

     .............  


static void keyPress(int keyCode){  

    JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, __null };  

    JNIEnv* jniEvn = __null;  

    int res = javaVm->AttachCurrentThread(&jniEvn, &args);  

        method = jniEvn->GetStaticMethodID(remotedesktopclass,  

                               "pressHome", "()V");



使用JNIEnv全局变量调用FindClass()等函数发生crash

jclass remotedesktopclass = sJniEnv->FindClass("com/xxx/xxx/xxx/xxx");  

sJniEnv是一个全局变量,但其实不应该全局保存JNIEnv。

这是一个和thread紧密关联的变量,在其他线程中使用就会crash。

【解决方式】

JNIEXPORT void JNICALL Java_xx_xx_xx_xx_xxx(JNIEnv * jniEnv, jobject object, jstring jflowNum, jstring judid){  

     jniEnv->GetJavaVM(&javaVm);  

     jclass tmp = jniEnv->FindClass("com/nationsky/rc/util/RemoteDesktopUtil");  

     remotedesktopclass = (jclass)jniEnv->NewGlobalRef(tmp);  

     .............  


static void keyPress(int keyCode){  

    JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, __null };  

    JNIEnv* jniEvn = __null;  

    int res = javaVm->AttachCurrentThread(&jniEvn, &args);  

        method = jniEvn->GetStaticMethodID(remotedesktopclass,  

                               "pressHome", "()V");




Qt控件内加控件

Qt5增强了很多安全性验证,如果出现setGeometry: Unable to set geometry,请将该控件的可见移到加入布局之后。


可以将控件A添加到布局,然后控件B设置该布局,这种灵活性大大提高了控件的组合度,比如可以在文本框左侧右侧增加一个搜索按钮,按钮设置图标即可。


QPushButton *btn = new QPushButton;

btn->resize(30, ui->lineEdit->height());

QHBoxLayout *layout = new QHBoxLayout(ui->lineEdit);

layout->setMargin(0);

layout->addStretch();

layout->addWidget(btn);



Qml控件占据剩余空间

 Label {

                id: titleLabel

                text: listView.currentItem ? listView.currentItem.text : "Gallery"

                font.pixelSize: 20

                elide: Label.ElideRight

                horizontalAlignment: Qt.AlignHCenter

                verticalAlignment: Qt.AlignVCenter

                Layout.fillWidth: true

            }



ToolButton

工具栏

ToolBar {                        //工具栏

    RowLayout{                //横向

        ToolButton{}            //工具按钮

        ToolButton{}

        ToolButton{}

        ToolButton{}

    }

}   


发布QML程序

windeployqt  g:\HelloPlayer\HelloPlayer.exe  --qmldir  F:\QtAll\5.12.3\mingw73_32\qml


Qt播放视频报错 DirectShowPlayerService::doRender: Unresolved error code 0x80040266

下载并安装  LAVFilters:https://github.com/Nevcairiel/LAVFilters/releases

installer方式直接按步骤安装

压缩包方式解压右键以管理员运行 install_video.bat 文件

切记:需要重启电脑

完美,没毛病

播放器代码

QT += quick   multimedia

CONFIG += c++11


#include <QGuiApplication>

#include <QQmlApplicationEngine>


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

{

    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);


    QGuiApplication app(argc, argv);


    QQmlApplicationEngine engine;

    const QUrl url(QStringLiteral("qrc:/main.qml"));

    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,

                     &app, [url](QObject *obj, const QUrl &objUrl) {

        if (!obj && url == objUrl)

            QCoreApplication::exit(-1);

    }, Qt::QueuedConnection);

    engine.load(url);


    return app.exec();

}



import QtQuick 2.12

import QtMultimedia 5.0

import QtQuick.Window 2.12

import QtQuick.Controls 2.12



//运行报错:Warning: "No decoder available for type 'video/x-h264, stream-format=(string)avc...

//sudo apt-get install gstreamer1.0-libav 即可解决  Qt在linux下使用gstreamer,Windows下使用directshow

ApplicationWindow {

    width: 800

    height: 600

    title: "视频"

    visible: true

    property var date: new Date()


    function parseTotalDuration(time) {

        date.setTime(time)

        if (time / 3600000 >= 1) {

            //大于1小时

            return Math.floor(time / 3600000) + ":" + Qt.formatDateTime(date,

                                                                        "mm:ss")

        } else {

            return Qt.formatTime(date, "mm:ss")

        }

    }


    Rectangle {

        id: video

        width: parent.width

        height: 450


        MediaPlayer {

            id: mediaPlayer

            source: "http://www.1xn1.com/upfiles/files/201912/2d67b6b5758c4098b8b8bd9534532b7c.mp4"

            autoPlay: true

            onError: {

                console.log(errorString)

            }

            //position 属性 单位是毫秒,onPositionChanged 信号处理器,实时显示进度

            onPositionChanged: {

                progress.text = parseTotalDuration(

                            position) + progress.time

            }

            //duration 单位是毫秒,onDurationChanged 信号处理器,可以取到时长

            onDurationChanged: {

                progress.time = " / " + parseTotalDuration(duration)

            }

            onPlaybackStateChanged: {

                switch (playbackState) {

                case MediaPlayer.PlayingState:

                    state.text = "播放中"

                    break

                case MediaPlayer.PausedState:

                    state.text = "已暂停"

                    break

                case MediaPlayer.StoppedState:

                    state.text = "已结束"

                    break

                }

            }

            //多媒体元信息

            onStatusChanged: {

                switch (status) {

                case MediaPlayer.Loaded:

                    console.log(metaData.albumArtist, metaData.albumTitle,

                                metaData.author, metaData.channelCount)

                    break

                }

            }

        }


        VideoOutput {

            anchors.fill: parent

            source: mediaPlayer

        }


        MouseArea {

            anchors.fill: parent

            onDoubleClicked: {

                if (mediaPlayer.playbackState === 2) {

                    mediaPlayer.play()

                } else if (mediaPlayer.playbackState === 1) {

                    mediaPlayer.pause()

                } else {

                    mediaPlayer.play()

                }

            }

        }

    }

    Connections {

        target: mediaPlayer

        onStopped: {


        }

    }

    Text {

        id: progress

        anchors.left: video.left

        anchors.top: video.bottom

        anchors.topMargin: 10

        color: "#000000"

        font.pointSize: 12

        property string time

    }

    Text {

        id: state

        anchors {

            verticalCenter: progress.verticalCenter

            horizontalCenter: parent.horizontalCenter

        }


        color: "#000000"

        font.pointSize: 12

    }

    Rectangle {

        id: progressBar

        anchors.left: parent.left

        anchors.right: parent.right

        anchors.top: progress.bottom

        anchors.topMargin: 6

        height: 6

        radius: 3

        color: "darkgray"


        Rectangle {

            anchors.left: parent.left

            anchors.top: parent.top

            anchors.bottom: parent.bottom

            radius: 3

            width: mediaPlayer.duration > 0 ? parent.width * mediaPlayer.position

                                              / mediaPlayer.duration : 0

            color: "#4A6DBC"

        }


        MouseArea {

            property int pos

            anchors.fill: parent


            onClicked: {

                //seekable 属性指示媒体是否支持 seek,为true时,用seek(pos)方法来定位播放

                if (mediaPlayer.seekable)

                    pos = mediaPlayer.duration * mouse.x / width

                mediaPlayer.seek(pos)

            }

        }

    }


}

qt android动态权限申请

前端时间要用qt开发一个android程序,结果发现在真机运行的时候莫名其妙无法加载写入文件,导致项目被搁置,今天重新研究了一下,终于搞定了,特此记录一下


工程中引入模块 androidextras


接下来项目中加入如下代码


bool MainWindow::requestPermission()

{

    QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");

    if(r == QtAndroid::PermissionResult::Denied) {

        QtAndroid::requestPermissionsSync(QStringList()<<"android.permission.WRITE_EXTERNAL_STORAGE");

        r = QtAndroid::checkPermission("android.permission.CAMERA");

        if(r == QtAndroid::PermissionResult::Denied) {

             return false;

        }

   }

   return true;

}

使用权限前调用函数requestPermission();就可以了

但是里面要修改成相应权限

android.permission.WRITE_EXTERNAL_STORAGE

android.permission.READ_EXTERNAL_STORAGE

android.permission.CAMERA

 

另外手机内存卡的根目录就是/storage/emulated/0/



Top