JavaFX, problem with Platform.runLater(), delayed rendering of Canvas graphic - java

I am trying to make a simple event driven TicTacToe game using JavaFX. Currently I am struggling with termination once certain conditions are met. To put it into more details, the player can click on the GridPane elements, which are Canvas Objects, and then they are filled with "X" or "O" shapes respectively (I am using strokeLine and strokeOval methods of GraphicsContext). Code below:
private static void placeX(Canvas square){
GraphicsContext gc = square.getGraphicsContext2D();
gc.setLineWidth(10.0f);
gc.setStroke(Color.CORNFLOWERBLUE);
gc.strokeLine(square.getWidth()*0.2, square.getHeight()*0.2, square.getWidth()*0.8, square.getHeight()*0.8);
gc.strokeLine(square.getWidth()*0.8, square.getHeight()*0.2, square.getWidth()*0.2, square.getHeight()*0.8);
}
Once 3 of the same shapes appear in line or diagonally the program should terminate. I am doing this using Platform.exit(). Code below:
class HandleGame implements EventHandler<MouseEvent>{
#Override
public void handle(MouseEvent e){
Canvas can = (Canvas)e.getTarget();
//function to check if the canvas is clear
placeX(can);
if(game.isEnded()){ //checks if the same shape appears three times
Platform.runLater(new Runnable() {
#Override
public void run(){
try {
Thread.sleep(5000);
}
catch(InterruptedException exc){
System.out.println("Got something: " + exc.getMessage());
}
Platform.exit();
}
});
}
}
}
This event handler is attached to every Canvas object in GridPane and triggers on mouse release. The problem I am having is that once the last Canvas is clicked, before the shape appears on the Canvas the specified Runnable is executed and the rendering is unnaturally delayed (the "X" shape appears only for a second before closing). Strangely enough 1 out of 10 runs it executes as expected. How can I make the rendering trigger before the Thread.sleep() and following Platform.exit()? Why on rare occasions the rendering is actually performed before Thread.sleep()? I did a little research but could not find anything decisive, I am newbie when it comes to JavaFx. Appreciate your help.

Based on #Slaw and #VGR comments I managed to solve the problem using two different methods, one utilizing PauseTransition and second Thread. Code below:
Using PauseTransition:
if(game.isEnded()){
PauseTransition termination = new PauseTransition(Duration.seconds(1d));
termination.setOnFinished(event -> Platform.exit());
termination.play();
}
Using Thread:
if(game.isEnded()){
new Thread(() -> {
try{
Thread.sleep(2000);
}
catch(InterruptedException exc){
; //exception handling code here
}
Platform.exit();
}).start();*/

Related

JDialog created before Thread.sleep() shows after sleep is finished

I just encountered a problem when trying to add an imprint to a swing application which is shown for five seconds when the application is closed.
I had planned to open a JDialog just containing a simple image when the main frame is closed.
I got a function showing the JDialog (I removed everything which is not necessary).
public static void show() {
JDialog d = new JDialog();
JLabel l = new JLabel(new ImageIcon(MainController.class.getClass().getResource("/path/to/endlogo.png")));
d.add(l);
d.setVisible(true);
}
The function is called by the following snippet (in the window listener of my main window)
#Override
public void windowClosing(WindowEvent e) {
show();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
The problem is that the dialog is shown AFTER the five seconds (which is when the windows is already closed and the application exited, I tested it and it showed up perfectly after five seconds when run on application start).
Do you guys know a way to achieve it the other way round?
You're blocking the UI thread. Don't do that, basically... use a swing timer or something similar if you want to do something on the UI thread at a later time.
Golden rules:
Don't do anything time consuming on the UI thread
Only access the UI on the UI thread

Impossible Java Memory Consistency Error

first of all I'm not an English native speaker so I apologize for any eventual “weird” writing.
I'm developing a Swing Java application on Eclipse that updates a Jpanel. This panel contains several sub-panels, and I'm constantly switching the panels “modes”, what happens to be a MouseListener changing so they respond in a slightly different manner to the user mouse inputs.
Regardless of what the application do, it's happening an error that seems to have no logical explanation to me. At some point in my code I try to update the panels to what I called neutralMode. This happens on the following method:
//Guarded block (see http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html)
private synchronized boolean waitsForUserSatisfactionAnswer()
{
while(!userIndicatedSatisfaction)
{
try {
wait();
} catch (InterruptedException e) {}
}
userIndicatedSatisfaction = false; //reset for future new query
getObjectSetVisualizationPanel().neutralMode();
//getObjectSetVisualizationPanel().queryPatternMode();
return userSatisfied;
}
This updating doesn't work (the call to neutralMode() dont do what is expected). However the call to queryPatternMode() (commented on the line right below) works perfectly. So I decided to COPY queryPatternMode()'s body and PASTE it on neutralMode()'s body ECXATLY THE SAME! AND IT STILL DOESNT WORK!
The methods code is like this:
public void queryPatternMode()
{
System.out.println("Inside queryPatternMode!!!");
System.out.println("panels.size(): " + panels.size());
for (DigitalObjectPanel panel : panels)
{
System.out.println("Inside the loop!!!");
panel.resetBehavior();
panel.setQuerySelectionBehavior(gui);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.validate();
}
});
}
}
public void neutralMode()
{
System.out.println("Inside neutralMode!!!");
System.out.println("panels.size(): " + panels.size());
for (DigitalObjectPanel panel : panels)
{
System.out.println("Inside the loop!!!");
panel.resetBehavior();
panel.setQuerySelectionBehavior(gui);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.validate();
}
});
}
}
What happens is that, when I call neutralMode(), the “panels” collection happens to be empty (panels.size() equals zero). However when I call queryPatternMode() instead, the collection happens to have it's expected size (20 panels). But both methods are equals, and both are called from the same place!!!
What it could be??? Is there any possible explanation for that??
It definitely looks like a synchronisation issue. You should check how many threads are accessing the collection 'panels'.
It is just a stroke of luck that it works for you with queryPatternMode() all the time, and not with neutralMode(). On another fine day, it might be other way around.

Concurrency in Java - wait for execution to be finished

So I am currently doing some work with Multi-Threading in Java and I'm pretty stuck on a, most likely, simple thing.
I currently have a JButton that, when pressed invokes the method as follows:
private void clickTest() throws InterruptedException{
statOrganizer.incrementHappiness();
Thread t = new Thread(new Happiness(workspaceHappy));
t.start();
}
and then takes around 10-30 seconds to complete. During this time however, it is still possible to re-click the JButton so that it messes with how the information is displayed.
What I want to do is during the time this particular thread is "alive", disable the button so that it is no longer possible to click it(and thus activate this thread once it's already going). Once the thread is finished, I want to re-enable the button again.
The button code just looks like this
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 1) {
try {
clickTest();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Disable the button right before starting the thread. In the thread, at the end, post an event that would re-enable the button (using invokeLater).
You may also need a cancel option, but that's a separate question.
Try the following:
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 1) {
try {
clickTest();
button.setEnabled(false);//this assume 'button' is final
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Then, modify the run method of your Happiness class:
public void run()
{
//your processing code here
...
button.setEnabled(true);
//reference to button can be passed in constructor of Happiness
// or access using getter, ... This really depend on your implementation.
}
The nice solution for this is to use a glass pane to capture all events and stopping them from propagating to any of your UI elements on the panel under the glass pane. Of course while you only have one or two, you can call setEnabled(false) on them manually but glass panes give you more flexibility, you'll never have to worry about adding a new element to your UI and forgetting to disable it during background processing.
Probably an overkill for one button though.
Another, unrelated thing you should consider is the use of Executors instead of launching threads for background tasks. It results in cleaner and more scalable code.

libgdx - doing something in other thread not working

My game has a stats queue, after each game the current game stats goes into the queue.
Whenever the mainmenu starts i want to upload all the game stats to a server, and this take like 1-3 seconds and I want to do this in an other thread.
My code
#Override
public void show() {
Global.key = Global.getKey();
// System.out.println(Stats.getJSONObject(Global.key));
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
Stats.TryUploadGame1();
System.out.println("DONE");
}
});
.....
}
But this also freezes my game.
What should I do?
Your current code is posting a Runnable instance that will be executed by the render thread before the next frame. The Gdx.app.postRunnable API is generally used so background threads can ask for something to happen on the render thread. You want to post a Runnable to execute anywhere but the render thread.
As long as your Stats code doesn't interact with OpenGL context at all (since Android OpenGL APIs assume only a single thread interacts with them), you can just post your Runnable on a new background thread:
new Thread(new Runnable() { ... }).start();
This should unblock your render. (Of course, if your background thread uses a lot of CPU, it can still interfere with the render thread, but if its mostly doing blocking IO or host has spare cores, it shouldn't interfere.)
This could be improved in lots of ways (using a ThreadPool, or using Android-aware background task support), but if your stats update is relatively quick and the thread creation isn't frequent this should work fine.
Okay to do something in a other thread you need to take care of the OpenGL context. Inside of a different thread you cant do anything that does render stuff. You are forced to push such thing into the renderthread in any way. And you need to synchronize everything that can be called from the regular render thread from libgdx. For example you want to call the .act(float delta) from a stage from a different thread you are forced to put the stage indo an synchronized block.
The post runable isn't a thread. It is an runable that get executed at the beginning of the next rendercall. So it will stop the game till it's done but it is inside of the OpenGl context thread. (That's why your game stops)
So here is an example of how to use threading in libgdx. I use this inside of my game. It runs on 210 frames so 210 updatecalls per second. You can change it to as fast as possible or just to 60fps whatever you need:
public class GameLogicThread extends Thread {
private GameScreen m_screen;
private boolean m_runing;
private long m_timeBegin;
private long m_timeDiff;
private long m_sleepTime;
private final static float FRAMERATE = 210f;
public GameLogicThread(GameScreen screen) { //pass the game screen to it.
m_screen = screen;
setName("GameLogic");
}
#Override
public void run() {
m_runing = true;
Logger.log("Started");
while (m_runing) {
m_timeBegin = TimeUtils.millis();
// act of the camera
synchronized (m_screen.figureStage) { //stage with figures
// now figures
if (m_screen.m_status == GameStatus.GAME) {
m_screen.figureStage.act(1f / GameLogicThread.FRAMERATE);
}
}
m_timeDiff = TimeUtils.millis() - m_timeBegin;
m_sleepTime = (long) (1f / GameLogicThread.FRAMERATE * 1000f - m_timeDiff);
if (m_sleepTime > 0) {
try {
Thread.sleep(m_sleepTime);
}
catch (InterruptedException e) {
Logger.error("Couldn't sleep " + e.getStackTrace());
}
} else {
Logger.error("we are to slow! " + m_sleepTime); //meight create it dynamic so if you are to slow decrease the framerate till you are not to slow anymore
}
}
}
/**
* Stops the thread save<br>
*/
public void stopThread() {
m_runing = false;
boolean retry = true;
while (retry) {
try {
this.join();
retry = false;
}
catch (Exception e) {
Logger.error(e.getMessage());
}
}
}
}
This does update all my figures. To not cause any troubles with the rendering thread the figurestage is synchronized. (Kind of critical section)
Dont forget that you need to create a new thread every time you stopped it. so for example inside of the show you need todo this:
#Override
public void show() {
super.show();
m_logic = new GameLogicThread(this); //create a new one inside of the GameScreen
m_logic.start(); //start the thread
}
Also dont forget to savestop it inside of the pause stop and so on.
#Override
public void dispose() {
m_logic.stopThread();
}
According to the wiki
To pass data to the rendering thread from another thread we recommend using Application.postRunnable(). This will run the code in the Runnable in the rendering thread in the next frame, before ApplicationListener.render() is called.
So calling that method is just creating a new thread on to run on the render thread.
You may want to use standard java practice on creating threads unless this is frowned upon in libgdx because of android, that I am not sure of.

Updating GUI gives a flickering effect

I currently have a JFrame where on it's content pane I draw images on from a game loop at 60 frames per second. This works fine, but at the right side, I now have more Swing elements on which I want to display some info on when selecting certain parts of the content pane. That part is a static GUI and does not make use of a game loop.
I'm updating it this way:
public class InfoPanel extends JPanel implements Runnable {
private String titelType = "type: ";
private String type;
private JLabel typeLabel;
private ImageIcon icon;
public void update() {
if (this.icon != null)
this.typeLabel.setIcon(this.icon);
if(this.type != null || this.type != "")
this.typeLabel.setText(this.titelType + this.type);
else
this.typeLabel.setText("");
}
public void run() {
try {
Thread.sleep(150);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.update();
}
(this method is only called when the player has actually moved, so it's just called once - not 60 times per second)
I noticed that, when calling this update()-method from the game loop, I get flickering effects. I assume this is because updating the UI takes some time, so I decided to put it in a new thread. This reduced the flickering, but didn't solve it.
Next, I decided to give the new thread low priority as the part of the screen which is redrawed 60 times a second is far more important. This reduced the flickering again, but it still happened. Then, I decided to use Thread.sleep(150); in the new thread before calling the update()-method, which solved the flickering effect on my system completely.
However, when running it on other systems, it still happens. Not as often as before (maybe one time per 20 seconds), but it's still pretty annoying. Apparantly, just updating the UI in another thread doesn't solve the problem.
Any ideas how to completely eleminate the flickering?
Call the update() in SwingUtilities.invokeAndWait() which stops the thread and updates UI in EDT.
Problem is that you are use Thread.sleep(int), that stop and freeze GUI during EventDispatchTread more in the Concurency in Swing, example demonstrating freeze GUI by using Thread.sleep(int), example for Runnable#Thread
If you want to delay whatever in Swing then the best way is implements javax.swing.Timer

Categories