Qt/Necessitas - reasonable QFileDialog replacement/skin?

元气小坏坏 提交于 2019-12-18 12:38:09

问题


I'm looking for a nice way to address porting Qt applications to Qt/Necessitas (Android).

Some of the QtGUI widgets are absolutely atrocious - unfortunately, including QFileDialog.

Do you know of any replacements with a proper look and feel? Is making QFileDialog usable anywhere near high priority for Necessitas developers?

#include <QApplication>

#include <QFileDialog>

int main(int argc, char* argv[]) {
    QApplication a(argc, argv);

    QString fileName = QFileDialog::getOpenFileName(NULL,
      QObject::tr("Open Image"), "/home/jana", QObject::tr("Image Files (*.png *.jpg *.bmp)"));

    a.exec();
}


回答1:


Android does not have own, native file dialog. We can use QtAndroidExtras to invoke external application which is able to pick a file:

I wrote wrapper, that could be used for that. Here's full code:

androidfiledialog.h

#ifndef ANDROIDFILEDIALOG_H
#define ANDROIDFILEDIALOG_H

#include <QObject>
#include <QAndroidJniObject>
#include <QtAndroid>
#include <QAndroidActivityResultReceiver>

class AndroidFileDialog : public QObject
{
    Q_OBJECT

public:
    explicit AndroidFileDialog(QObject *parent = 0);
    virtual ~AndroidFileDialog();
    bool provideExistingFileName();

private:
    class ResultReceiver : public QAndroidActivityResultReceiver {
        AndroidFileDialog *_dialog;
    public:
        ResultReceiver(AndroidFileDialog *dialog);
        virtual ~ResultReceiver();
        void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data);
        QString uriToPath(QAndroidJniObject uri);
    };

    static const int EXISTING_FILE_NAME_REQUEST = 1;
    ResultReceiver *receiver;
    void emitExistingFileNameReady(QString result);

signals:
    void existingFileNameReady(QString result);
};

#endif // ANDROIDFILEDIALOG_H

androidfiledialog.cpp

#include "androidfiledialog.h"

AndroidFileDialog::ResultReceiver::ResultReceiver(AndroidFileDialog *dialog) : _dialog(dialog) {}
AndroidFileDialog::ResultReceiver::~ResultReceiver() {}

void AndroidFileDialog::ResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
{
    jint RESULT_OK = QAndroidJniObject::getStaticField<jint>("android/app/Activity", "RESULT_OK");
    if (receiverRequestCode == EXISTING_FILE_NAME_REQUEST && resultCode == RESULT_OK) {
        QAndroidJniObject uri = data.callObjectMethod("getData", "()Landroid/net/Uri;");
        QString path = uriToPath(uri);
        _dialog->emitExistingFileNameReady(path);
    } else {
        _dialog->emitExistingFileNameReady(QString());
    }
}

QString AndroidFileDialog::ResultReceiver::uriToPath(QAndroidJniObject uri)
{
    if (uri.toString().startsWith("file:", Qt::CaseInsensitive)) {
        return uri.callObjectMethod("getPath", "()Ljava/lang/String;").toString();
    } else {
        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>(), 0, 0, 0, 0);
        QAndroidJniObject DATA = QAndroidJniObject::fromString("_data");
        jint columnIndex = cursor.callMethod<jint>("getColumnIndexOrThrow", "(Ljava/lang/String;)I", DATA.object<jstring>());
        cursor.callMethod<jboolean>("moveToFirst", "()Z");
        QAndroidJniObject result = cursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
        return result.isValid() ? result.toString() : QString();
    }
}

AndroidFileDialog::AndroidFileDialog(QObject *parent) : QObject(parent)
{
    receiver = new ResultReceiver(this);
}

AndroidFileDialog::~AndroidFileDialog()
{
    delete receiver;
}

bool AndroidFileDialog::provideExistingFileName()
{
    QAndroidJniObject ACTION_GET_CONTENT = QAndroidJniObject::fromString("android.intent.action.GET_CONTENT");
    QAndroidJniObject intent("android/content/Intent");
    if (ACTION_GET_CONTENT.isValid() && intent.isValid()) {
        intent.callObjectMethod("setAction", "(Ljava/lang/String;)Landroid/content/Intent;", ACTION_GET_CONTENT.object<jstring>());
        intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QAndroidJniObject::fromString("file/*").object<jstring>());
        QtAndroid::startActivity(intent.object<jobject>(), EXISTING_FILE_NAME_REQUEST, receiver);
        return true;
    } else {
        return false;
    }
}

void AndroidFileDialog::emitExistingFileNameReady(QString result)
{
    emit existingFileNameReady(result);
}

You have to add to your *.pro file:

QT += androidextras

using example:

AndroidFileDialog *fileDialog = new AndroidFileDialog();
connect(fileDialog, SIGNAL(existingFileNameReady(QString)), this, SLOT(openFileNameReady(QString)));
bool success = fileDialog->provideExistingFileName();
if (!success) {
    qDebug() << "Problem with JNI or sth like that...";
    disconnect(fileDialog, SIGNAL(existingFileNameReady(QString)), this, SLOT(openFileNameReady(QString)));
    //or just delete fileDialog instead of disconnect
}

slot implementation:

void MyClass::openFileNameReady(QString fileName)
{
    if (!fileName.isNull()) {
        qDebug() << "FileName: " << fileName;
    } else {
        qDebug() << "User did not choose file";
    }
}

Please confirm this solution works properly on your device.




回答2:


You could easily build your own file dialog either with QtWidgets or QML, by using the out-of-the-box QFileSystemModel class or the FolderListModel element.

As for whether it is priority or not, at this point it seems that Necessitas will be absorbed by Digia's efforts to support Android. I doubt there will be significant efforts to style QtWidgets appropriately, since the module is marked as DONE and all the focus for UI is on QML. So, I wouldn't hold by breath if I were you. Plus the stock Qt widgets look completely ugly on non-desktop platforms.



来源:https://stackoverflow.com/questions/15079406/qt-necessitas-reasonable-qfiledialog-replacement-skin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!