I need to run a QT application from Java using JNA. The problem is, that QT need to be run from the main thread, however I want to create it on a different thread and manage my java application instantaneously after creating qt window and just send some data to QT via JNA. When I create QTApp from a different thread I get warning "QAppplication was not created in the main() thread" and there is no response to GUI. Is there any clean solution to the problem ?
You can create a QCoreApplication in a QThread. Here is an example snippet from something I recently did:
class MyThread : public QThread
{
public:
void run()
{
int argc = 0;
char* argv[1];// = new char[];
QCoreApplication a(argc,&argv[0]);
// More initilization code here
a.exec();
}
};
Declare your thread in main and call thread.start();
Related
I have an Android app written in Qt that has the same basic structure as the Qt Notifier example : http://doc.qt.io/qt-5/qtandroidextras-notification-example.html
There is a native droid interface using an extension to class org.qtproject.qt5.android.bindings.QtActivity. There is also a QApplication backbone and they communicate bidirectionally via jni. In Qt 5.2 through 5.5, everything worked fine. I skipped 5.6, and just upgraded to 5.7. Now, the Java side works, i.e my Java QtActivity class is launched and displayed, but the app acts like the C++ is non existent!
When I add some qDebug() output to main.cpp it is not displayed on the console at all! (Yet in Qt 5.5 I can see it with no problem). I can, however, see qDebug() output from my static C++ function: JNIEXPORT jint JNI_OnLoad( JavaVM* vm, void* /reserved/ )
If I remove the native Activity by commenting out ANDROID_PACKAGE_SOURCE_DIR in my qmake, then the main C++ function is invoked.
What's going on here? What has changed in Qt to cause this?
I can't believe that no else has had this same problem! At least I can't find anyone else reporting this.
I found that if the Java side QtActivity class calls setContentView() from onCreate(), then the C++ main function is never called! This was not an issue until this new version of Qt. Note that QtNotifier example I referenced in my question doesn't do that, why is why it runs on Qt 5.7 out of the box.
I had to revise my logic so that the Java QtActivity class does it other initialization tasks, but leaves out setContentView() to natively load the ui. Then the C++ fires off. From the C++ side, I issue a jni call to the QtActivity class which then uses setContentView(). Note that I have to use a static instance reference to the QtActivity class (which I assign in onCreate), and I have to run that code on the main/ui thread.
Here are some useful code snippets for you to drop into a Java QtActivity class:
private static MyQtActivityClass instance_;
private static Handler mainHandler_;
...
#Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
instance_ = this;
context_ = this.getApplicationContext();
mainHandler_ = new Handler( Looper.getMainLooper() );
}
public static void runOnMainThread( Runnable runnable ) {
mainHandler_.post( runnable );
}
...
runOnMainThread( new Runnable() {
public void run() {
instance_.setContentView( R.layout.XXXXXX );
}
});
I'm trying to use JNI to access C++ methods from a Java class. I'm able to compile (both in Eclipse or on command line) my Java class fine, but on executing the class at runtime, I'm getting:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.domain.services.CallServiceAPIS.createSession()I
at com.domain.services.CallServiceAPIS.createSession(Native Method)
at com.domain.services.CallServiceAPIS.main(CallServiceAPIS.java:18)
Java code is as follows:
package com.domain.services;
public class CallServiceAPIS {
static {
System.loadLibrary("service.client");
}
public native int createSession();
public static void main(String[] args) {
System.out.println(System.getProperty("java.library.path"));
new CallServiceAPIS().createSession();
}
}
I included the printout of the java.library.path just to make sure it's pointing to the correct location of the C++ library - and it is. I also tried setting the LD_LIBRARY_PATH in my Eclipse environment. But neither worked.
Note that the System.loadLibrary call IS working since 1) the code compiles and 2) the error occurs on line 18, which is the new CallServiceAPIs call.
C++ code:
int createSession(const PosServiceInfo info, const SessionArgs& args, Domain::UUID& uuidSession)
{
return int::undefined;
}
Any ideas?
Never mind. I realized that I was using the JNI interface incorrectly. I was thinking you could load an EXISTING C++ library using EXISTING C++ source. But you basically have to rewrite the existing code to make use of the JNI interface.
I am running a Ruby script from Java using JRuby like so:
public class ScriptletRunnable implements Runnable {
#Override
public void run() {
ScriptingContainer scriptingContainer = new ScriptingContainer();
scriptingContainer.runScriptlet(/*My script*/); // this is a blocking call, never returns
}
}
I pass this runnable to a new thread and start the thread. When I want to stop the script from running, I can't seem to find any way to tell the ScriptingContainer to stop executing the script. Even if I make it a member and call terminate() on it, the script just doesn't stop. If you run the script from the command line you can cleanly shut it down with Ctrl+C. There must be a way to achieve this with JRuby.
I'm using JNI to invoke a static java method which in turn creates a Swing JFrame and displays it. The code is fairly simple, and the Java-code is working standalone (i.e. java StartAWT does what it should) whereas when called from C using JNI the process hangs.
I'm using the JDK 1.7.0_09 on Mac OS X 10.8 Mountain Lion.
This is the C code I'm using to invoke the static method:
JavaVM* jvm;
JNIEnv* env = create_vm(&jvm);
jclass class = (*env)->FindClass(env, "StartAWT");
jmethodID method = (*env)->GetStaticMethodID(env, class, "run", "()V");
(*env)->CallStaticVoidMethod(env, class, method);
(*jvm)->DestroyJavaVM(jvm);
The StartAWT class looks like this:
public class StartAWT {
public static class Starter implements Runnable {
public void run() {
System.out.println("Runnning on AWT Queue.");
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("That's a frame!");
JLabel label = new JLabel("A Label");
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
}
public static class GUI implements Runnable {
public void run() {
try {
System.out.println("Going to put something on the AWT queue.");
SwingUtilities.invokeAndWait(new Starter());
} catch (Exception exc) {
throw new RuntimeException(exc);
}
}
}
public static void run() {
Thread gui = new Thread(new GUI());
gui.start();
}
}
When I start the application, I do see Going to put something on the AWT queue but not Running on AWT Queue.
I believe that the Virtual Machine inside my C Process does not have an AWT event queue but I don't know how to set it up for having one either (nor am I sure that this is the reason).
What is to be done in order to show an AWT based GUI using JNI ?
--
EDIT: I've inserted loops to see which threads are alive and which are not (can be seen in this gist). In this version I do the invocation of SwingUtilities.invokeAndWait in another thread. The result: The main thread is alive (C). The first thread dispatched by Java (not the main thread) is alive; the thread doing the Call invokeAndWait is blocked (I don't think that invokeAndWait did even return), the function which should be run on the EventQueue is not even entered.
I've also tried invoking SwingUtilities.invokeAndWait directly, which will give the following message:
2013-02-02 13:50:23.629 swing[1883:707] Cocoa AWT: Apple AWT Java VM was loaded on first thread -- can't start AWT. (
0 liblwawt.dylib 0x0000000117e87ad0 JNI_OnLoad + 468
1 libjava.dylib 0x00000001026076f1 Java_java_lang_ClassLoader_00024NativeLibrary_load + 207
2 ??? 0x000000010265af90 0x0 + 4335185808
)
This is also what I've read in other questions here on StackOverflow, such as the one suggested in the comments below. However, I could not find a solution to the original problem. Maybe it is worth noting that after the above message came up the main thread is still alive, i.e. neither did the process deadlock nor crash.
--
EDIT: I tested the code on Linux where it is working as expected. So I believe this is a Mac OS X issue with Cocoa AWT, but I don't know how to circumvent it.
--
EDIT: I also tried moving the entire invocation of the JVM onto a new native thread. This works on Mac OS X 10.6 with Apples Java 32-bit (1.6.0_37), but results in the same deadlock as described above. On Mac OS X 10.8 this is worse, the application crases with the only message "Trace/BPT trap: 5" (which seems to be related to loading dynamic libraries).
I also tried bundling the binary as described in this Q&A, but the launch fails with the message lsopenurlswithrole() failed with the message -10810, which is an unknown error, according to Apples Launch Services Reference. The latter also happens without attempting to use AWT (the mere JVM invocation fails).
Finally I found a solution.
The problem is not on which thread the Virtual Machine is being created, the problem is on which thread the AWT Event Queue is being initialized. In other words: The first time that an AWT class is loaded, it may not be loaded on the main thread. Thus step 1: Load (for example) java.awt.Component on another thread.
But now the EventQueue will block, as it delegates work to the Cocoa Main Event Queue which is not running - sure enough, since it will only run on the main thread and the main thread is my application. Thus the main run loop needs to be started on the main thread:
void
runCocoaMain()
{
void* clazz = objc_getClass("NSApplication");
void* app = objc_msgSend(clazz, sel_registerName("sharedApplication"));
objc_msgSend(app, sel_registerName("run"));
}
I had to link my application with the Cocoa framework and include <objc/objc-runtime.h>. The main thread is blocked after the call to runCocoaMain (since the event loop is running there), so one needs to resort to another thread for the application itself.
After running the EventQueue using the above snippet the loading of the AWT class on the other thread will succeed and you can proceed there.
I resolved similar problem by instructions of OSX: JavaVM, AWT/Swing and possibly a deadlock , that is to start CFRunLoopRun() after start JVM in another thread.
I have a java library in jar form which can be used to extract data from files(excel,xml etc). As its in java, it can be used only in java applications. But i need the same library to be used for python projects as well.
I have tried py4j etc which takes the objects from jvm. But the library is not an executable and wont be 'run'. I have checked Jython but i need the library to be accessible from Python projects.
I have thought about using automated java to python translators, but i would take that as the last resort.
Please suggest some way i can accomplish this.
You can make a one class java program with a thread never ending until you send from Python a notification to do so.
This way the lib would be kept in memory and accessible from your Python program.
This class could be like this (add your needed lib import/init) :
public class TestPy {
private Thread thread;
public void die() {
synchronized (thread) {
thread.interrupt();
}
}
public TestPy() {
thread = new Thread(){
public void run() {
try {
while (!Thread.interrupted()) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
thread.start();
}
public static void main(String[] args) {
TestPy tp = new TestPy();
GatewayServer server = new GatewayServer(tp);
server.start();
}
}
You would have to launch the java program, use the lib, and then use the die() method to kill the java program in Python :
gateway = JavaGateway()
do your stuff here using the lib
tp = gateway.entry_point
tp.die()
You can write a simple command line Java program which calls the library and saves the results in a format you can read in Python, then you can call the program from Python using os.system.
Another option is to find Python libraries with equivalent functionality to the Java library: you can read excel, xml and other files in Python, that's not a problem.
I haven't learned how to create new instances of java class in a jar file by java constructors, but accidentally found that it's very easy to use any java static methods to access java objects in py2j.
step 1: download py4j zip file from https://pypi.python.org/pypi/py4j. "py4j0.10.0.jar" is in the zip file.
step 2: install py4j by
pip install 'D:\Downloads\py4j-0.10.0.zip'
step 3: add py4j0.10.0.jar as well as the_lib_you_use.jar (like owlapi-distribution-3.5.0.jar for the example below) to build path in your eclipse project
step 4: create AdditionApplication.java, and copy and paste the code of AdditionApplication.java at https://www.py4j.org/, and run Java application AdditionApplication.java
step 5: after running AdditoinApplication.java, test the example code in a python file:
if __name__ == '__main__':
pass
from py4j.java_gateway import JavaGateway
gateway = JavaGateway() # connect to the JVM
random = gateway.jvm.java.util.Random() # create a java.util.Random instance
number1 = random.nextInt(10) # call the Random.nextInt method
number2 = random.nextInt(10)
print(number1,number2)
(2, 7)
addition_app = gateway.entry_point # get the AdditionApplication instance
addition_app.addition(number1,number2) # call the addition method
Math = gateway.jvm.java.lang.Math
a = Math.max(4, 6);
print a
IRI = gateway.jvm.org.semanticweb.owlapi.model.IRI
abcIRI = IRI.create('fewf#fe')
print 'abcIRi = ' + str(abcIRI)
print 'abcIRI.getFragment() = ' + abcIRI.getFragment()
The result on console is :
(5, 0)
6
abcIRi = fewf#fe
abcIRI.getFragment() = fe