I've wrote a simple application to use QSettings. Can someone tell me what wrong I'm doing here..
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QWidget *pMainWidget;
QHBoxLayout *pMainLayout;
QSettings *pSetting;
QLabel *pLabel;
QPushButton *pButtonShow;
QPushButton *pButtonSet;
QLineEdit *pLineEdit;
QString pSettingFile;
public slots:
void showSettingData();
void setData();
};
mainwindow.cpp
#include "mainwindow.h"
#include <QtCore/QCoreApplication>
#include <QApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
pMainWidget = new QWidget (parent);
pMainLayout = new QHBoxLayout(pMainWidget);
pLabel = new QLabel("Output comes here",pMainWidget);
pLineEdit = new QLineEdit();
pButtonShow = new QPushButton("Show", pMainWidget);
pButtonSet = new QPushButton("Set", pMainWidget);
setCentralWidget(pMainWidget);
pMainWidget->setLayout(pMainLayout);
pMainLayout->addWidget(pLabel);
pMainLayout->addWidget(pButtonShow);
pMainLayout->addWidget(pLineEdit);
pMainLayout->addWidget(pButtonSet);
pSettingFile = QApplication::applicationDirPath()+"settings.ini";
QObject::connect(pButtonShow, SIGNAL(clicked()), this, SLOT(showSettingData()));
QObject::connect(pButtonSet, SIGNAL(clicked()), this, SLOT(setData()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::setData()
{
QSettings Setting(pSettingFile, QSettings::NativeFormat);
QString data = pLineEdit->text();
Setting.setValue("baseurl", data);
}
void MainWindow::showSettingData()
{
QSettings Setting(pSettingFile, QSettings::NativeFormat);
if (Setting.contains("baseurl"))
{
QString data = Setting.value("baseurl").toString();
pLabel->setText(data);
}
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
When I debug, it doesn't returns false for "Setting.contains("baseurl")"... Do we have to create the setting file?
The issue is that you are not calling sync after setting the data. Try to insert this line after the setting:
Setting.setValue("baseurl", data);
Setting.sync();
This should not be needed on Windows, but it seems to be necessary on Windows.
Also, as suggested in comments, I would suggest to use QSettings::IniFormat instead of QSettings::NativeFormat if you really want to use files rather than potentially registry on Windows.
You should also consider making the settings object as a class member rather than constructing it all the time.
Also, note that you may be using the wrong path unintentionally because you would have to add the "slash" character explicitly before the "settings.ini" file name. This is just a side note, however.
Related
I have a.so which defines void a() and b.so which defines void b(). They are both put in the .apk so they are available to the Android application.
Now suppose that I'm calling a() through JNI. Is it possible to call b() from a() while completely bypassing JNI?
Can I do it this way in Android (the code is only for illustration, so it might have some errors)?
void a() {
void *handle = dlopen("b.so", RTLD_LAZY);
void (*b)() = dlsym(handle, "b");
b();
}
Would I need to add the fully qualified path, or is b.so already in LD_LIBRARY_PATH for the app?
You can do it this way on Android, though take care of where the shared library has been put in Android folders. It can change from a version to another.
On api 17 for example, it remains in /data/app-lib/. You can hardwrite it, but the best is to make calls to Java (through JNI) to know where the libraries should be.
We're doing something like this in our project :
JNIEnv* env;
const char* temp;
jobject oActivity = state->activity->clazz;
jclass cActivity = env->GetObjectClass(oActivity);
// get the path to where android extracts native libraries to
jmethodID midActivityGetApplicationInfo = env->GetMethodID(cActivity, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
jobject oApplicationInfo = env->CallObjectMethod(oActivity, midActivityGetApplicationInfo);
jclass cApplicationInfo = env->GetObjectClass(oApplicationInfo);
jfieldID fidApplicationInfoNativeLibraryDir = env->GetFieldID(cApplicationInfo, "nativeLibraryDir", "Ljava/lang/String;");
jstring sNativeLibraryDir = (jstring)env->GetObjectField(oApplicationInfo, fidApplicationInfoNativeLibraryDir);
temp = env->GetStringUTFChars(sNativeLibraryDir, NULL);
strcpy(libpath, temp);
strcat(libpath, "/");
Then you push your dlopen + dlsym combo in the fight and it should work.
As mentioned here : How do I load a shared object in C++?
There are two ways of loading shared objects in C++
For either of these methods you would always need the header file for the object you want to use. The header will contain the definitions of the classes or objects you want to use in your code.
#include "blah.h"
int main()
{
ClassFromBlah a;
a.DoSomething();
}
gcc yourfile.cpp -lblah
Dynamically (In Linux):
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen ("libm.so", RTLD_LAZY);
if (!handle) {
fprintf (stderr, "%s\n", dlerror());
exit(1);
}
dlerror(); /* Clear any existing error */
cosine = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fprintf (stderr, "%s\n", error);
exit(1);
}
printf ("%f\n", (*cosine)(2.0));
dlclose(handle);
return 0;
}
PS : for the dynamic approach, it depends on platform : on Linux, you use dlopen, on windows, you use LoadLibrary.
Pretty new to Cocos2dx and C++ from an Objective-C background. I'm making a game where you can have between 1-10 enemies on the screen at a time. Each enemy has 1 sprite for its body and 2 stats (health & damage).
In the past with Objective-C I've been able to define a Class.h/Class.mm file and fill it with as many variables as I want (health, speed, height, weight, color, etc), but I'm having trouble figuring out how to do this in Cocos2dx.
Here is a simplified project that isolates the problem I'm having. I'm sure it's something straight forward but I'm having trouble looking up the solution
//-----Enemy.h
#include "cocos2d.h"
USING_NS_CC;
class Enemy : public cocos2d::Layer{
public:
virtual bool init();
CREATE_FUNC(Enemy);
void setHealth(int val);
private:
int myHealth;
Sprite *body;
Sprite *weapon;
};
//-----Enemy.cpp
#include "Enemy.h"
USING_NS_CC;
bool Enemy::init()
{
if ( !Layer::init() )
return false;
return true;
}
void setHealth(int newHealth)
{
//myHealth = newHealth;
}
//-----HelloWorldScene.h
#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(HelloWorld);
};
//-----HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include <iomanip>
#include "Enemy.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
if ( !Layer::init() )
return false;
Enemy *newEnemy = Enemy::create();
//newEnemy->setHealth(100);
this->addChild(newEnemy);
return true;
}
This compiles fine except for the 2 commented lines, which cause errors.
Please help, thanks in advance!
Without knowing your error message exactly, it looks like you are missing the class name in your setHealth method in Enemy.cpp
Change your method declaration in your Enemy.h
From:
void setHealth(int val);
To:
void setHealth(int newHealth);
Then in your Enemy.cpp file change the following.
From:
void setHealth(int newHealth)
To:
void Enemy::setHealth(int newHealth)
I want get notification from my app using qt-android, I found this examole in qt examples , it's in QML and I want use it in QWidgets,To use code in QWidget I changed it as follow:
notificationclient.h
#ifndef NOTIFICATIONCLIENT_H
#define NOTIFICATIONCLIENT_H
#include <QObject>
class NotificationClient : public QObject
{
Q_OBJECT
public:
explicit NotificationClient(QObject *parent = 0);
void setNotification(QString notification);
QString notification() const;
signals:
void notificationChanged();
private slots:
void updateAndroidNotification();
private:
QString m_notification;
};
#endif // NOTIFICATIONCLIENT_H
notificationclient.cpp
#include "notificationclient.h"
#include <QtAndroidExtras/QAndroidJniObject>
NotificationClient::NotificationClient(QObject *parent)
: QObject(parent)
{
connect(this, SIGNAL(notificationChanged()), this, SLOT(updateAndroidNotification()));
m_notification = "";
}
void NotificationClient::setNotification(QString notification)
{
if (m_notification == notification)
return;
m_notification = notification;
emit notificationChanged();
}
QString NotificationClient::notification() const
{
return m_notification;
}
void NotificationClient::updateAndroidNotification()
{
QAndroidJniObject javaNotification = QAndroidJniObject::fromString(m_notification);
QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/notification/NotificationClient",
"notify",
"(Ljava/lang/String;)V",
javaNotification.object<jstring>());
}
For use in Main class:
notification = new NotificationClient(this);
And for get notification:
void myclass::on_btn_clicked(){
notification->setNotification("hello world");
}
and follow code in .pro file too:
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
QT += core gui androidextras
when on_btn_clicked() called the program suddenly exits
NOTE:This is the java code and I set package name with my app package
I solved the problem , we should add this attribute to activity tag in AndroidMainifest.xml
android:name="MY.APP.PACKAGE.NAME.NotificationClient"
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();
}
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.
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.
I have some problems when using the dynamic loading API (<dlfcn.h>: dlopen(), dlclose(), etc) on Android.
I'm using NDK standalone toolchain (version 8) to compile the applications and libraries.
The Android version is 2.2.1 Froyo.
Here is the source code of the simple shared library.
#include <stdio.h>
int iii = 0;
int *ptr = NULL;
__attribute__((constructor))
static void init()
{
iii = 653;
}
__attribute__((destructor))
static void cleanup()
{
}
int aaa(int i)
{
printf("aaa %d\n", iii);
}
Here is the program source code which uses the mentioned library.
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
void *handle;
typedef int (*func)(int);
func bbb;
printf("start...\n");
handle = dlopen("/data/testt/test.so", RTLD_LAZY);
if (!handle)
{
return 0;
}
bbb = (func)dlsym(handle, "aaa");
if (bbb == NULL)
{
return 0;
}
bbb(1);
dlclose(handle);
printf("exit...\n");
return 0;
}
With these sources everything is working fine, but when I try to use some STL functions or classes, the program crashes with a segmentation fault, when the main() function exits, for example when using this source code for the shared library.
#include <iostream>
using namespace std;
int iii = 0;
int *ptr = NULL;
__attribute__((constructor))
static void init()
{
iii = 653;
}
__attribute__((destructor))
static void cleanup()
{
}
int aaa(int i)
{
cout << iii << endl;
}
With this code, the program crashes with segmentation fault after or the during main() function exit.
I have tried couple of tests and found the following results.
Without using of STL everything is working fine.
When use STL and do not call dlclose() at the end, everything is working fine.
I tried to compile with various compilation flags like -fno-use-cxa-atexit or -fuse-cxa-atexit, the result is the same.
What is wrong in my code that uses the STL?
Looks like I found the reason of the bug. I have tried another example with the following source files:
Here is the source code of the simple class:
myclass.h
class MyClass
{
public:
MyClass();
~MyClass();
void Set();
void Show();
private:
int *pArray;
};
myclass.cpp
#include <stdio.h>
#include <stdlib.h>
#include "myclass.h"
MyClass::MyClass()
{
pArray = (int *)malloc(sizeof(int) * 5);
}
MyClass::~MyClass()
{
free(pArray);
pArray = NULL;
}
void MyClass::Set()
{
if (pArray != NULL)
{
pArray[0] = 0;
pArray[1] = 1;
pArray[2] = 2;
pArray[3] = 3;
pArray[4] = 4;
}
}
void MyClass::Show()
{
if (pArray != NULL)
{
for (int i = 0; i < 5; i++)
{
printf("pArray[%d] = %d\n", i, pArray[i]);
}
}
}
As you can see from the code I did not used any STL related stuff.
Here is the source files of the functions library exports.
func.h
#ifdef __cplusplus
extern "C" {
#endif
int SetBabe(int);
int ShowBabe(int);
#ifdef __cplusplus
}
#endif
func.cpp
#include <stdio.h>
#include "myclass.h"
#include "func.h"
MyClass cls;
__attribute__((constructor))
static void init()
{
}
__attribute__((destructor))
static void cleanup()
{
}
int SetBabe(int i)
{
cls.Set();
return i;
}
int ShowBabe(int i)
{
cls.Show();
return i;
}
And finally this is the source code of the programm that uses the library.
main.cpp
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include "../simple_lib/func.h"
int main()
{
void *handle;
typedef int (*func)(int);
func bbb;
printf("start...\n");
handle = dlopen("/data/testt/test.so", RTLD_LAZY);
if (!handle)
{
printf("%s\n", dlerror());
return 0;
}
bbb = (func)dlsym(handle, "SetBabe");
if (bbb == NULL)
{
printf("%s\n", dlerror());
return 0;
}
bbb(1);
bbb = (func)dlsym(handle, "ShowBabe");
if (bbb == NULL)
{
printf("%s\n", dlerror());
return 0;
}
bbb(1);
dlclose(handle);
printf("exit...\n");
return 0;
}
Again as you can see the program using the library also does not using any STL related stuff, but after run of the program I got the same segmentation fault during main(...) function exit. So the issue is not connected to STL itself, and it is hidden in some other place. Then after some long research I found the bug.
Normally the destructors of static C++ variables are called immediately before main(...) function exit, if they are defined in main program, or if they are defined in some library and you are using it, then the destructors should be called immediately before dlclose(...).
On Android OS all destructors(defined in main program or in some library you are using) of static C++ variables are called during main(...) function exit. So what happens in our case? We have cls static C++ variable defined in library we are using. Then immediately before main(...) function exit we call dlclose(...) function, as a result library closed and cls becomes non valid. But the pointer of cls is stored somewhere and it's destructor should be called during main(...) function exit, and because at the time of call it is already invalid, we get segmentation fault. So the solution is to not call dlclose(...) and everything should be fine. Unfortunately with this solution we cannot use attribute((destructor)) for deinitializing of something we want to deinitialize, because it is called as a result of dlclose(...) call.
I have a general aversion to calling dlclose(). The problem is that you must ensure that nothing will try to execute code in the shared library after it has been unmapped, or you will get a segmentation fault.
The most common way to fail is to create an object whose destructor is defined in or calls code defined in the shared library. If the object still exists after dlclose(), your app will crash when the object is deleted.
If you look at logcat you should see a debuggerd stack trace. If you can decode that with the arm-eabi-addr2line tool you should be able to determine if it's in a destructor, and if so, for what class. Alternatively, take the crash address, strip off the high 12 bits, and use that as an offset into the library that was dlclose()d and try to figure out what code lives at that address.
I encountered the same headache on Linux. A work-around that fixes my segfault is to put these lines in the same file as main(), so that dlclose() is called after main returns:
static void* handle = 0;
void myDLClose(void) __attribute__ ((destructor));
void myDLClose(void)
{
dlclose(handle);
}
int main()
{
handle = dlopen(...);
/* ... real work ... */
return 0;
}
The root cause of dlclose-induced segfault may be that a particular implementation of dlclose() does not clean up the global variables inside the shared object.
You need to compile with -fpic as a compiler flag for the application that is using dlopen() and dlclose(). You should also try error handling via dlerror() and perhaps checking if the assignment of your function pointer is valid, even if it's not NULL the function pointer could be pointing to something invalid from the initialization, dlsym() is not guaranteed to return NULL on android if it cannot find a symbol. Refer to the android documentation opposed to the posix compliant stuff, not everything is posix compliant on android.
You should use extern "C" to declare you function aaa()