02-29-2012 09:15 AM - edited 02-29-2012 09:22 AM
Hi,
Some days ago I've started looking into the different platforms available for BlackBerry. I've played for some days with BlackBerry Java until I realized it will not be available on BBX (or Black Berry 10). I'm not a fan of Adobe AIR or HTML 5 and I don't like the idea of an app runing in a player (Java Android Runtime).
So, what is left? C\C++ Native SDK. Great!
I've started looking at the existing tutorials and samples for a simple way to create a UI application (buttons, textboxes etc.). My app should be a mix of UI layouts and OpenGL. Cascades UI is not ready yet and the best way to do this seems to be Qt.
First step was to integrate Qt in Momentics IDE. After trying different things I've come to this blog (look at the comments also) - http://bgmotey.blogspot.com/ - and after overcoming some small problems I've managed to run my Hello World app on the PlayBook simulator.
Next I went to the Qt website and started looking at the "How to learn Qt" ( http://developer.qt.nokia.com/doc/qt-4.8/how-to-le
qcc -o Calculator src/main.o src/calculator.o src/button.o -lbps -lQtCore -lQtGui -V4.4.2,gcc_ntox86_cpp -w1 -lang-c++ -g -Wl,-z,relro -Wl,-z,now -L/Developer/SDKs/bbndk-2.0.0-beta3/target/qnx6/..
src/main.o: In function `~Calculator':
/Users/dban/Documents/ndk-2.0.0-workspace/Calculat
/Users/dban/Documents/ndk-2.0.0-workspace/Calculat
...
Back to documentation... The problem was Q_OBJECT and the Qt Signals/Slot. The Meta-Object Compiler (moc) was supposed to do some job but it didn't. The solution was to add some custom build steps for the header files. And I did so for button.h and calculator.h by right-clicking the file->Properties->C/C++ Build->Settings and Build Steps. The values used:
Build again... Failed again:
...
src/moc_calculator.o: (.rodata._ZTV10Calculator[_ZTV10Calculator]+0xb0): undefined reference to `QWidget::x11Event(_XEvent*)'
src/moc_button.o: (.rodata._ZTV6Button[_ZTV6Button]+0xb0): undefined reference to `QWidget::x11Event(_XEvent*)'
...
I've started looking at the flags and their meaning. First thing that came to my mind was to remove the QT_BOOTSTRAPPED. And the build was successful! Tried to run it on the simulator but I got a message saying "Errors exist in a required project. Continue launch?". What errors? Looking in the console I saw the following:
**** Rebuild of configuration Simulator-Debug for project Calculator ****
**** Internal Builder is used for build ****
Build error
null
What? I just built the whole shebang 10 seconds ago... So I just continued with the launch. And the calculator was running in the simulator! Hooray!
Still, I am not happy. When trying to rebuild the project I get:
Errors occurred during the build.
Errors running builder 'CDT Builder' on project 'Calculator'.
java.lang.NullPointerException
I have to open project properties and press ok and then the projects rebuilds just fine. Is there another way?
Also, removing QT_BOOTSTRAPPED doesn't sound like a good solution. Or does it? Another way to make it compile is to leave the QT_BOOTSTRAPPED and add also Q_WS_QPA. Does this sounds right? It doesn't work with Q_OS_MAC or Q_WS_MAC.
I will continue learning Qt until I can create an app with UI layouts and OpenGL scenes combined. I'm thinking that I'm going to make the effort one time and then I can create many application with Qt. But is it worth it? Or is there another way to do it? Will there be an easier way to integrate Qt into Momentics? When? Should I wait for Cascades UI?
Any feedback would be appreaciated!
Thanks & best regards,
Dumi.
02-29-2012 10:54 AM
Nice job!
you are correct about moc stuff. We need to find a way to integrate it in NDK but good think you don't have to run moc very ofthen, you need to run it only against new classes that have Q_OBJECT you introduce.
X11 errors - yes I ran into the same issue but only when I was trying to compile/deploy to Simulator. SInce then I gave up on Qt/Simulator as there are too many differences between SImulator and Readl PlayBook. If you want an advice then: don't waste your time trying to make Qt things work on Simulator, use real device.
And lastly as basically there is not documentation on Qt + NDK, learning/writing Qt apps for Playbook is close to reverse engineering. You have to go through Qt libs + Qt blackberry plugin sources to understand things (fortunately they are available!)
Cheers and good luck!
Please share your findings...
02-29-2012 01:33 PM
may be some useful info can be found via search in this forum
03-01-2012 08:31 AM
In my opinion, there is no need for the Momentics IDE. I have not bothered to even run it yet.
I would suggest just using the Qt SDK with the built in simulator to develop and test your app. Then you will not have to worry about the moc files etc. as they are all handled by Qt IDE. Then just build and deploy it via the command line to your playbook. (https://bdsc.webapps.blackberry.com/android/downlo![]()
I have heard rumor that debugging on the playbook from the Qt IDE will be supported soon too...
Good luck!
Jon
03-01-2012 10:51 AM - edited 03-01-2012 01:02 PM
I was kind of happy with what I've accomplished yesterday. Until I've tried to open Momentics IDE this morning. At that point none of my test projects opened and I got a NPE exception:
Could not open the editor: Editor could not be initialized.
java.lang.NullPointerException
at org.eclipse.cdt.managedbuilder.internal.core.Addit
at org.eclipse.cdt.managedbuilder.internal.core.Input
at org.eclipse.cdt.managedbuilder.internal.core.Tool.
at org.eclipse.cdt.managedbuilder.internal.core.Resou
at org.eclipse.cdt.managedbuilder.internal.core.Resou
at org.eclipse.cdt.managedbuilder.internal.core.Resou
at org.eclipse.cdt.managedbuilder.internal.core.Resou
at org.eclipse.cdt.managedbuilder.internal.core.Confi
at org.eclipse.cdt.managedbuilder.internal.dataprovid
at org.eclipse.cdt.managedbuilder.internal.dataprovid
at org.eclipse.cdt.internal.core.settings.model.CProj
at org.eclipse.cdt.internal.core.settings.model.CConf
at org.eclipse.cdt.internal.core.settings.model.CProj
at org.eclipse.cdt.internal.core.settings.model.xml.X
at org.eclipse.cdt.internal.core.settings.model.xml.X
at org.eclipse.cdt.internal.core.settings.model.CProj
at org.eclipse.cdt.internal.core.settings.model.CProj
at org.eclipse.cdt.internal.core.settings.model.CProj
at org.eclipse.cdt.internal.core.settings.model.CProj
at org.eclipse.cdt.internal.core.model.CProject.compu
at org.eclipse.cdt.internal.core.model.CProject.compu
at org.eclipse.cdt.internal.core.model.CProject.build
at org.eclipse.cdt.internal.core.model.Openable.gener
at org.eclipse.cdt.internal.core.model.CElement.openW
at org.eclipse.cdt.internal.core.model.CElement.getEl
at org.eclipse.cdt.internal.core.model.CElement.getEl
at org.eclipse.cdt.internal.core.model.Parent.getChil
at org.eclipse.cdt.internal.core.model.CProject.getSo
at org.eclipse.cdt.internal.core.model.CModelManager.
at org.eclipse.cdt.core.model.CoreModel.create(CoreMo
at org.eclipse.cdt.internal.ui.editor.CDocumentProvid
at org.eclipse.cdt.internal.ui.editor.CDocumentProvid
at org.eclipse.ui.editors.text.TextFileDocumentProvid
at org.eclipse.cdt.internal.ui.editor.CDocumentProvid
at org.eclipse.ui.texteditor.AbstractTextEditor.doSet
at org.eclipse.ui.texteditor.StatusTextEditor.doSetIn
at org.eclipse.ui.texteditor.AbstractDecoratedTextEdi
at org.eclipse.ui.editors.text.TextEditor.doSetInput(
at org.eclipse.cdt.internal.ui.editor.CEditor.interna
at org.eclipse.cdt.internal.ui.editor.CEditor.doSetIn
at org.eclipse.ui.texteditor.AbstractTextEditor$19.ru
at org.eclipse.jface.operation.ModalContext.runInCurr
at org.eclipse.jface.operation.ModalContext.run(Modal
at org.eclipse.jface.window.ApplicationWindow$1.run(A
at org.eclipse.swt.custom.BusyIndicator.showWhile(Bus
at org.eclipse.jface.window.ApplicationWindow.run(App
at org.eclipse.ui.internal.WorkbenchWindow.run(Workbe
at org.eclipse.ui.texteditor.AbstractTextEditor.inter
at org.eclipse.ui.texteditor.AbstractTextEditor.init(
at org.eclipse.ui.internal.EditorManager.createSite(E
at org.eclipse.ui.internal.EditorReference.createPart
at org.eclipse.ui.internal.EditorReference.createPart
at org.eclipse.ui.internal.WorkbenchPartReference.get
at org.eclipse.ui.internal.EditorAreaHelper.setVisibl
at org.eclipse.ui.internal.EditorManager.setVisibleEd
at org.eclipse.ui.internal.EditorManager$5.runWithExc
at org.eclipse.ui.internal.StartupThreading$StartupRu
at org.eclipse.swt.widgets.RunnableLock.run(RunnableL
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessa
at org.eclipse.swt.widgets.Display.runAsyncMessages(D
at org.eclipse.swt.widgets.Display.readAndDispatch(Di
at org.eclipse.ui.application.WorkbenchAdvisor.openWi
at org.eclipse.ui.internal.Workbench$33.runWithExcept
at org.eclipse.ui.internal.StartupThreading$StartupRu
at org.eclipse.swt.widgets.RunnableLock.run(RunnableL
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessa
at org.eclipse.swt.widgets.Display.runAsyncMessages(D
at org.eclipse.swt.widgets.Display.readAndDispatch(Di
at org.eclipse.ui.internal.Workbench.runUI(Workbench.
at org.eclipse.ui.internal.Workbench.access$4(Workben
at org.eclipse.ui.internal.Workbench$7.run(Workbench.
at org.eclipse.core.databinding.observable.Realm.runW
at org.eclipse.ui.internal.Workbench.createAndRunWork
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(Pl
at org.eclipse.ui.internal.ide.application.IDEApplica
at org.eclipse.equinox.internal.app.EclipseAppHandle.
at org.eclipse.core.runtime.internal.adaptor.EclipseA
at org.eclipse.core.runtime.internal.adaptor.EclipseA
at org.eclipse.core.runtime.adaptor.EclipseStarter.ru
at org.eclipse.core.runtime.adaptor.EclipseStarter.ru
at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ
at sun.reflect.NativeMethodAccessorImpl.invoke(Native
at sun.reflect.DelegatingMethodAccessorImpl.invoke(De
at java.lang.reflect.Method.invoke(Method.java:597)
at org.eclipse.equinox.launcher.Main.invokeFramework(
at org.eclipse.equinox.launcher.Main.basicRun(Main.ja
at org.eclipse.equinox.launcher.Main.run(Main.java:14
I've started looking for a solution but I didn't find one. I had to manually delete all my custom steps from .cproject and add them again after that. The workaround is to enable a custom build step only when you want to moc the corresponding header file and disable it after this.
Also, I had to change the values used for custom build steps with something like this:
In this way the moc_xxx.cpp file is added to the src folder. You have to refresh the project and after that everything works fine...
Dumi.
03-01-2012 02:05 PM
Hello again!
After finishing with the Calculator Example I decided it's time to try the Application Example ( https://qt-project.org/doc/qt-4.8/mainwindows-appl
I've created a new project and did everything the same as for the Calculator. The different thing was a resource file, application.qrc. This one should be picked up by the following line of code:
Q_INIT_RESOURCE(application);
But for this to happen the qrc file needed to be compiled by the Qt Resource Compiler, rcc. Similar to the moc, right? So I've added a custom build step for the application.qrc:
Unfortunatelly this custom step did not execute. What changes are needed to force the compilation of .qrc files? My workaround was to rename the application.qrc to application.h and to have it compiled as aresource to application.cpp:
This works because the file generated by rcc is a C++ source file containing the data specified in the resource file.
And hooray! Here is the Application Example in the simulator:
Dumi.
03-02-2012 07:56 AM - edited 03-02-2012 08:01 AM
Back again! And with a serious problem I've been trying to overcome in the last hours. The Application Example saves the size of its window when receiving the close event. Unfortunatelly this happens only when selecting File->Exit. When closing the application in the BlackBerry way the close event is not raised. And that's because that event is the navigator exit event.
Still, there is a Qt event that may be used for this purpose - QEvent::ApplicationDeactivate. To have access to this event I have created a new class, MyApplication, that overrides some of the QApplication behaviour:
bool Application::event(QEvent *ev) {
qDebug("%d", ev->type());
if (ev->type() == QEvent::ApplicationDeactivate) {
// do something
}
return QApplication::event(ev);
}
Is this the right way? It might be if you just need this navigator event.
But what if you need some other navigator events? Like navigator swipe down, navigator orientation, navigator window active/inactive? How should this be accomplished?
First thing that came into my mind was to create another class where to listen for bps navigator events. I've extended QThread and implemented the following:
NavigatorEventsThread::NavigatorEventsThread() {
bps_initialize();
navigator_request_events(0);
stopped = false;
}
NavigatorEventsThread::~NavigatorEventsThread() {
bps_shutdown();
}
void NavigatorEventsThread::run() {
while (!stopped) {
int rc, domain;
bps_event_t *event = NULL;
rc = bps_get_event(&event, -1);
assert(rc == BPS_SUCCESS);
if (event) {
domain = bps_event_get_domain(event);
if (domain == navigator_get_domain()) {
switch (bps_event_get_code(event)) {
case NAVIGATOR_EXIT:
emit navigatorCloseEventRaised();
stopped = true;
break;
default:
break;
}
}
}
}
}
Unfortunatelly this blocks at the bps_get_event line. Why? Because there are no events coming if you don't create a bps screen also
Right?
Next step was to replace the QApplication event filter with my own:
bool myEventFilter(void *message, long *result) {
return false;
}
int main(int argc, char *argv[]) {
QCoreApplication::addLibraryPath("app/native/lib") ;
Application app(argc, argv);
app.setEventFilter(myEventFilter);
...
}
But myEventFilter is never called. Why? I'm guessing because the event filter function set here is called for all messages received by all threads meant for all Qt objects. It is not called for messages that are not meant for Qt objects. Or why else?
Another way was to replace the event filter function for the QAbstractEventDispatcher:
bool myEventFilter(void *message) {
return false;
}
int main(int argc, char *argv[]) {
QCoreApplication::addLibraryPath("app/native/lib") ;
Application app(argc, argv);
QAbstractEventDispatcher *aed = QAbstractEventDispatcher::instance(app.thread());
aed->setEventFilter(myEventFilter);
...
}
This was better. I got myEventFilter called on swipe down and on orientation changes. But not for application getting active/inactive. And also, to what should I cast the void *message in order to see what's actually in the message?
Is there another way, a better one, to somehow get the navigator events and add them to the Qt event loop? Or is there another solution for this problem?
Thanks & best regards,
Dumi.
03-02-2012 08:45 AM - edited 03-02-2012 08:46 AM
[quote]Back again! And with a serious problem I've been trying to overcome in the last hours. The Application Example saves the size of its window when receiving the close event. Unfortunately this happens only when selecting File->Exit. When closing the application in the BlackBerry way the close event is not raised. And that's because that event is the navigator exit event.[/quote]
I have not had a problem with my settings gettign stored using the blackberry close method (dragging up and clicking X). For Qt c++ widget apps I call my settings function from the destructor. For qml apps I call the settings function from the main page in Component.onDestruction: {}.
This works fine, however I believe using the qcloseEvent is the proper method for widget apps.
https://qt-project.org/doc/qt-4.8/qwidget.html#clo
I am just a rookie so perhaps that is not what your looking for! ![]()
Cheers,
Jon
03-02-2012 11:15 AM
azazello wrote:may be some useful info can be found via search in this forum
I've seen that, but this thread is more about problems encountered after managing to integrate Qt in QNX Momentics IDE; it's about creating different applications for PlayBook using Qt. The HelloWorld application represents only the beginning, it's just the tip of the iceberg. The problems are bigger and uglier from here on...
Dumi.
03-02-2012 11:54 AM
This is how I react to swipedown event. (Mainwindow is a descendant of QMainMindow)
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Menu){
ui->widgetMenu->show();
}
QMainWindow::keyPressEvent(event);
}