Weird thing with GUI and Threads in java - java

Given the following code:
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
ClientGUI gui = new ClientGUI();
gui.start();
}
});
}
everything works fine, I get a nice GUI window. OK.
Now, lets add an infinite loop after gui.start() :
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
ClientGUI gui = new ClientGUI();
gui.start();
while (true) {
}
}
});
}
and the output is a blank window that does not react to window exiting.
Can someone explain me what exactly happans here?

Can someone explane me what exactly happans here?
Sure. You're keeping the event dispatch thread tied up in an infinite loop, so it never gets to react to events such as "close window". Don't do that.
You should keep the event dispatch thread available for as much of the time as possible - don't perform any long-running tasks on it, including IO operations such as reading from files or the network.
See the "Concurrency in Swing" tutorial for more details.

Related

can't get sequential behavior Java Swing [duplicate]

basically, I have this code which was initially working with console i/o now I have to connect it to UI. It may be completely wrong, I've tried multiple things although it still ends up with freezing the GUI.
I've tried to redirect console I/O to GUI scrollpane, but the GUI freezes anyway. Probably it has to do something with threads, but I have limited knowledge on it so I need the deeper explanation how to implement it in this current situation.
This is the button on GUI class containing the method that needs to change this GUI.
public class GUI {
...
btnNext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.startTest(index, idUser);
}
});
}
This is the method startTest from another class which contains instance of Question class.
public int startTest() {
for (int i = 0; i < this.numberofQuestions; i++) {
Question qt = this.q[i];
qt.askQuestion(); <--- This needs to change Label in GUI
if(!qt.userAnswer()) <--- This needs to get string from TextField
decreaseScore(1);
}
return actScore();
}
askQuestion method:
public void askQuestion() {
System.out.println(getQuestion());
/* I've tried to change staticaly declared frame in GUI from there */
}
userAnswer method:
public boolean userAnswer() {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
if( Objects.equals(getAnswer(),userInput) ) {
System.out.println("Correct");
return true;
}
System.out.println("False");
return false;
}
Thanks for help.
You're correct in thinking that it related to threads.
When you try executing code that will take a long time to process (eg. downloading a large file) in the swing thread, the swing thread will pause to complete execution and cause the GUI to freeze. This is solved by executing the long running code in a separate thread.
As Sergiy Medvynskyy pointed out in his comment, you need to implement the long running code in the SwingWorker class.
A good way to implement it would be this:
public class TestWorker extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
//This is where you execute the long running
//code
controller.startTest(index, idUser);
publish("Finish");
}
#Override
protected void process(List<String> chunks) {
//Called when the task has finished executing.
//This is where you can update your GUI when
//the task is complete or when you want to
//notify the user of a change.
}
}
Use TestWorker.execute() to start the worker.
This website provides a good example on how to use
the SwingWorker class.
As other answers pointed out, doing heavy work on the GUI thread will freeze the GUI. You can use a SwingWorker for that, but in many cases a simple Thread does the job:
Thread t = new Thread(){
#Override
public void run(){
// do stuff
}
};
t.start();
Or if you use Java 8+:
Thread t = new Thread(() -> {
// do stuff
});
t.start();

Java Swing to JavaFX (RMI/Threads)

Currently teaching myself advanced object programming and I've hit a snag when trying to write a Javafx solution to a given Swing solution.
The problem was to develop an RMI program with Polling. I have the Swing version working perfectly, and I believe I've narrowed the problem down this section of code.
Swing
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
new PollClient().setVisible(true);
}
});
}
JavaFx
public static void main(String args[])
{
Platform.runLater(new Runnable(){
#Override
public void run()
{
launch(args);
}
});
}
Any advice would be appreciated.
Platform.runLater specifies in its documentation that it executed on the JavaFX application thread at an unspecified time in the future. For this thread to exist you must first launch your application.
This means that since the application isnt launched the action to launch the application is never executed.
Regardless, JavaFX should be launched from within the main function, no need to insert a delay:
Application.launch(PollClient.class, args)

Cannot make use of a .jar file if it has more than one thread

When I create a .jar file and the program only uses the main thread then everything is fine but when I need concurrency and add my own thread the .jar file does not open or run one I double click it.
Here is an Example:
class idk {
public static void main(String args[]) throws Exception {
new Thread(new Runnable() {
public void run() {
try {
JFrame j = new JFrame();
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.setVisible(true);
j.getContentPane().setPreferredSize(new Dimension(800, 500);
j.pack();
j.setLocationRelativeTo(null);
} catch(Exception e1) {}
}
}).start();
}
}
The above code will not execute for some reason when compiled into a .jar file.
However if I just used the same code but left out the thread it works just fine.
You must only ever use one thread, Swing's event dispatching thread (EDT), to interact with UI components. Create your main window with this code:
SwingUtilities.invokeLater(new Runnable() { ... } );
Concurrency in the application should be done using the SwingWorker objects, and the publish method, if they need to interact with the UI.
Creating your own Threads and creating or using Swing components from those threads will cause you grief.
See Concurrency in Swing

Javafx - Exception in thread "main" java.lang.ExceptionInInitializerError [duplicate]

My application is Swing-based. I would like to introduce JavaFX and configure it to render a Scene on a secondary display.
I could use a JFrame to hold a JFXPanel which could hold a JFXPanel but I would like to achieve this with JavaFX API.
Subclassing com.sun.glass.ui.Application and using Application.launch(this) is not an option because the invoking thread would be blocked.
When instantiating a Stage from Swing EDT, the error I get is:
java.lang.IllegalStateException: Toolkit not initialized
Any pointers?
EDIT: Conclusions
Problem: Non-trivial Swing GUI application needs to run JavaFX components. Application's startup process initializes the GUI after starting up a dependent service layer.
Solutions
Subclass JavaFX Application class and run it in a separate thread e.g.:
public class JavaFXInitializer extends Application {
#Override
public void start(Stage stage) throws Exception {
// JavaFX should be initialized
someGlobalVar.setInitialized(true);
}
}
Sidenote: Because Application.launch() method takes a Class<? extends Application> as an argument, one has to use a global variable to signal JavaFX environment has been initialized.
Alternative approach: instantiate JFXPanel in Swing Event Dispatcher Thread:
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new JFXPanel(); // initializes JavaFX environment
latch.countDown();
}
});
latch.await();
By using this approach the calling thread will wait until JavaFX environment is set up.
Pick any solution you see fit. I went with the second one because it doesn't need a global variable to signal the initialization of JavaFX environment and also doesn't waste a thread.
Found a solution. If I just create a JFXPanel from Swing EDT before invoking JavaFX Platform.runLater it works.
I don't know how reliable this solution is, I might choose JFXPanel and JFrame if turns out to be unstable.
public class BootJavaFX {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JFXPanel(); // this will prepare JavaFX toolkit and environment
Platform.runLater(new Runnable() {
#Override
public void run() {
StageBuilder.create()
.scene(SceneBuilder.create()
.width(320)
.height(240)
.root(LabelBuilder.create()
.font(Font.font("Arial", 54))
.text("JavaFX")
.build())
.build())
.onCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent windowEvent) {
System.exit(0);
}
})
.build()
.show();
}
});
}
});
}
}
Since JavaFX 9, you can run JavaFX application without extending Application class, by calling Platform.startup():
Platform.startup(() ->
{
// This block will be executed on JavaFX Thread
});
This method starts the JavaFX runtime.
The only way to work with JavaFX is to subclass Application or use JFXPanel, exactly because they prepare env and toolkit.
Blocking thread can be solved by using new Thread(...).
Although I suggest to use JFXPanel if you are using JavaFX in the same VM as Swing/AWT, you can find more details here: Is it OK to use AWT with JavaFx?
I checked the source code and this is to initialize it
com.sun.javafx.application.PlatformImpl.startup(()->{});
and to exit it
com.sun.javafx.application.PlatformImpl.exit();
I used following when creating unittests for testing javaFX tableview updates
public class testingTableView {
#BeforeClass
public static void initToolkit() throws InterruptedException
{
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> {
new JFXPanel(); // initializes JavaFX environment
latch.countDown();
});
if (!latch.await(5L, TimeUnit.SECONDS))
throw new ExceptionInInitializerError();
}
#Test
public void updateTableView() throws Exception {
TableView<yourclassDefiningEntries> yourTable = new TableView<>();
.... do your testing stuff
}
}
even though this post is not test related, then it helped me to get my unittest to work
without the BeforeClass initToolkit, then the instantiation of TableView in the unittest would yield a message of missing toolkit
There's also way to initialize toolkit explicitly, by calling:
com.sun.javafx.application.PlatformImpl#startup(Runnable)
Little bit hacky, due to using *Impl, but is useful, if you don't want to use Application or JXFPanel for some reason.
re-posting myself from this post
private static Thread thread;
public static void main(String[] args) {
Main main = new Main();
startup(main);
thread = new Thread(main);
thread.start();
}
public static void startup(Runnable r) {
com.sun.javafx.application.PlatformImpl.startup(r);
}
#Override
public void run() {
SoundPlayer.play("BelievexBelieve.mp3");
}
This is my solution. The class is named Main and implements Runnable. Method startup(Runnable r) is the key.
Using Jack Lin’s answer, I found that it fired off the run() twice. With a few modifications that also made the answer more concise, I offer the following;
import com.sun.javafx.application.PlatformImpl;
public class MyFxTest implements Runnable {
public static void main(String[] args) {
MyFxTest main = new MyFxTest();
PlatformImpl.startup((Runnable) main);
}
#Override
public void run() {
// do your testing;
System.out.println("Here 'tis");
System.exit(0); // Optional
}
}

Waiting for frame creation

i have problem in java and I do not know him and I resolved.
I created a simple program that inserts into the text JPanel using for and sleep function.
Like this(This is an example):
public class example{
JFrame frame....
..
..
public example(){
//ini frame and label.. then..
String s = "abcqweewqewqewqewqweqwqeweqweqwq";
//DO ANIMATION
try
{
for(int i = 0;i<s.length();i++)
{
JLABEL.append(String.valueOf(s.charAt(i)));
Thread.sleep(10);
}
}catch(Exception ex){}
}
public static void main.......{
new example();
}
}
It works perfectly (writes characters after a certain time interval)
But, if i call this main using other class-So waiting until everything renders and then the window appears (so does not animation).
Where is a problem? I hope, you understand me.
Swing is single threaded, and properly written swing code runs in the event dispatch thread. Your sample breaks the threading rule by creating the GUI outside the EDT, and also runs the loop in the main thread. Normally, when created correctly in the EDT, or as a response to an event from a button click or similar, the loop blocks the event dispatch thread so that no drawing can happen until the loop has completed.
You get that behaviour if you initialize the GUI in the event dispatch thread:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new example()
}
});
}
The proper way, instead of sleeping in the EDT, is using a Swing Timer.
To sum the above: your code appears to work only because it has the bug that it runs some of the UI code outside the event dispatch thread.

Categories