Java strange graphics blinking in while lock.await() - java

i have here a strange behaviour of my graphical user interface.
At first here a piece of code:
/**
*
*/
#Override
protected Void doInBackground() throws Exception {
final ModelGameState actualGameState = controller.getActualGameState();
final ModelCoinState actualCoinState = (actualGameState.getPlayersTurn() == ModelConstants.PLAYER_ONE_ID? actualGameState.getCoinsPlayerOne() : actualGameState.getCoinsPlayerTwo());
final List<ModelCoinState> temp = MoveCalculator.getMoves(actualCoinState, this.cellID);
final CountDownLatch lock = new CountDownLatch(temp.size());
int time = 500;
for(int i = 0; i < temp.size(); i++) {
final int index = i;
Timer timer = new Timer(time, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(actualGameState.getPlayersTurn() == ModelConstants.PLAYER_ONE_ID) {
actualGameState.setCoinsPlayerOne(temp.get(index));
} else {
actualGameState.setCoinsPlayerTwo(temp.get(index));
}
controller.setActualGameState(new ModelGameState(actualGameState));
lock.countDown();
}
});
timer.setRepeats(false);
timer.start();
time += 500;
}
lock.await();
return null;
}
at second here my gui:
and here my problem: everytime lock.await is called my screen
looks like that:
As you can see, behind each of my circles the top left corner
of my gui is shown everytime lock.await() is called (At least i
think it is when lock.await()is called because when i delete lock.await()
i cant see the whole animation of my gui but i also cant
see this strange behaviour and that behaviour appears always
when the program is through all code of doInBackground().
What causes this strange behaviour?

not an answer only disagree with, my comments against, no reviews, not tried your code, apologize me that there are any reason, maybe my bad
doInBackground() is bridge between AWT/Swing EDT and Workers Thread(s), by default never notified EDT
process, publish, setProgress and done() notify EDT
then Swing Timer inside doInBackground() is against all intentions, why was SwingWorker implemented in official APIs, there is place to execute long running, hard or non_safe code
again SwingWorker is designated as bridge between AWT/Swing and Workers Thread(s)
_____________________________
there are two ways
use CountDownLatch with invokeLater() or Swing Timer. don't mix that together nor from SwingWorker
use CountDownLatch, util.Timer, SheduledExecutor with notify EDT by wrap (only relevant, only output, methods will be repainted on the screen) Swing methods to the invokeLater()
use only Swing Timer (non_accurate on hour period)

Related

How do I create another thread so that my ActionListener still listens for button press events while my planet simulation runs?

I have a JButton and have added an ActionListener to it. I also have a SimulationPanel class which extends JPanel and have an instance of it named simulationPanel. Both are added to a JFrame. Within in the actionPerformed method of the ActionListener I call simulationPanel.startSimulation().
class StartButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent arg0) {
if(mode == MODE_DEFAULT){
mode = MODE_SIMULATING;
startButton.setText("Stop Simulation");
simulationPanel.startSimulation();
} else if(mode == MODE_SIMULATING){
mode = MODE_DEFAULT;
startButton.setText("Start Simulation");
simulationPanel.stopSimulation();
}
}
}
The stopSimulation() method sets running to false.
public void startSimulation() {
running = true;
long time = System.currentTimeMillis();
long time2 = System.currentTimeMillis();
while(running){
time = System.currentTimeMillis();
if(time - time2 > 1000/60){
for(int i = 0; i < planets.size(); i++) {
planets.get(i).setLocation(planets.get(i).getNewLocation());
}
this.paintImmediately(0, 0, 950, 680);
time2 = System.currentTimeMillis();
}
}
}
This sets the new location of my planets and repaints the screen every 60th of a second.
The problem is that once it has entered the while(running) loop the ActionListener no longer responds to the button being pressed and therefore stopSimulation() can't be called. How would I go about creating a new thread that makes the button able to respond while simulation is running?
The entire program
Swing is a single threaded environment, if you block the Event Dispatching Thread, then it can't process the Event Queue, which involves processing user input and reaping events.
When you need to execute long running tasks, you should offload the task to another thread.
Just remember, Swing is also not thread safe, this means that you should NEVER create or modify the UI from outside the context of the Event Dispatching Thread.
There are tools you can use to make your life easier in this respect.
You could use a SwingWorker, which would allow you to run the long running/blocking tasks in the background, but use the publish/process methods to send updates to the UI which does not violate the single thread rules of Swing
You could use a Swing Timer, which would allow you schedule regular callbacks which are executed within the context of the EDT, making it safe to update the UI from within
You could use a plain old Thread and use SwingUtilities.invokeLater to synchronise updates with the EDT, but I'm not a fan of this approach is its a pain to pass stateful information through
Yes of course, because your start simulation is blocking your main thread.
I suggest you use Swing Timer http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html

Swing GUI doesn't update during data processing

I'm having a problem where my Swing GUI components aren't updating for me while the program is busy. I'm creating an image editor and while heavy processing is happening, I try to change a "status" label while its working to give the user an idea of whats going on. The label won't update until after the processing is finished though.
How can I update the label IMMEDIATELY instead of having to wait? My labels are all on a JPanel by the way.
My label isn't set until after the for loop and the following method finishes.
labelStatus.setText("Converting RGB data to base 36...");
for (int i = 0; i < imageColors.length; i++) {
for (int j = 0; j < imageColors[0].length; j++) {
//writer.append(Integer.toString(Math.abs(imageColors[i][j]), 36));
b36Colors[i][j] = (Integer.toString(Math.abs(imageColors[i][j]), 36));
}
}
String[][] compressedColors = buildDictionary(b36Colors);//CORRECTLY COUNTS COLORS
I'm having a problem where my Swing GUI components aren't updating for me while the program is busy.
That is because you are executing a long running task on the Event Dispatch Thread (EDT) and the GUI can't repaint itself until the task finishes executing.
You need to execute the long running task in a separate Thread or a SwingWorker (for a better solution). Read the section from the Swing tutorial on Concurrency for more information about the EDT and examples of using a SwingWorker to prevent this problem.
You can do something like this, not the best but it can give you some idea
Create a thread dispatcher class and call it from main class
public class ThreadDispatcher implements Runnable {
public ThreadDispatcher() {
}
public void run() {
//call the method related heavy process here
}
}
It may be like this in your main class
Thread thread = new Thread(new ThreadDispatcher());
thread.start();
sleep(100);
catch the InterruptedException ex.
And check the Java thread examples.

Displaying graphics as they paint in swing

I created a maze generator in swing for a class, and it works great. The only thing is, I want to show the maze being created in realtime, but the way I have everything set, it only updates after all the calculations have completed. I am using paintComponent and repaint in my code. How do I have it show the JFrame and draw lines immediately, rather than doing the algorithm and showing them all at the end?
Here is the relevant code:
public void generateMaze() {
Stack<Box> stack = new Stack<>();
int totalCells = Finals.numCol * Finals.numRow, visitedCells = 1;
Box currentCell = boxes[0][0];
Box nextCell;
stack.add(currentCell);
while (visitedCells < totalCells) {
nextCell = checkNeighbors(currentCell.xCoord, currentCell.yCoord);
if (nextCell != null) {
knockWalls(currentCell, nextCell);
stack.add(currentCell);
currentCell = nextCell;
visitedCells++;
} else {
currentCell = stack.pop();
}
}
repaint();
}
Here is my paintComponent method override
public void paintComponent(Graphics g) {
for(int x = 0; x < Finals.numRow; x++) {
for(int y = 0; y < Finals.numCol; y++) {
if(boxes[y][x].top != null)
boxes[y][x].top.paint(g);
if(boxes[y][x].bottom != null)
boxes[y][x].bottom.paint(g);
if(boxes[y][x].left != null)
boxes[y][x].left.paint(g);
if(boxes[y][x].right != null)
boxes[y][x].right.paint(g);
}
}
}
The knockWalls method sets certain walls equal to null, which causes them to not be drawn in the paintComponent method. I'm still fairly new at a lot of this, so I apologize if some of the code isn't super high quality!
Thanks everyone.
As MadProgrammer already pointed out in the comments, you are almost certainly blocking the Event Dispatch Thread. This is the thread that is responsible for repainting the GUI, and for handling the interaction events (like mouse clicks and button presses).
So presumably, you start the computation via a button click, roughly like this:
// The actionPerformed method of the button that
// starts the maze solving computation
#Override
void actionPerformed(ActionEvent e)
{
generateMaze();
}
That means that the event dispatch thread will be busy with executing generateMaze(), and not be able to perform the repainting.
The simplest solution would be to change this to something like
// The actionPerformed method of the button that
// starts the maze solving computation
#Override
void actionPerformed(ActionEvent e)
{
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
generateMaze();
}
});
thread.start();
}
However, some care has to be taken: You may not modify Swing components from this newly created thread. If you have to modify Swing components, you have to put the task that performs the actual modification of the Swing component back on the EDT, using SwingUtilities.invokeLater(task). Additionally, you have to make sure that there are no other synchronization issues. For example, the lines
if(boxes[y][x].top != null)
boxes[y][x].top.paint(g);
are still (and have to be!) executed by the event dispatch thread. In this case, you have to make sure that no other thread can set the boxes[y][x].top to null after the EDT has executed the first line and before it executes the second line. If this may be an issue in your case, you might have to provide a bit more code, e.g. the code that is showing where and how the boxes[y][x] are modified.

Updating Swing components while blocking the GUI

I was programming a GUI today, which is doing longer calculations when pressing a button. While the calculations are running, I wanted to use intermediate results of the still running calculation and write them to a JLabel. The GUI however, should not be operable by the user before the calculation has finished.
At first I was doing something like this:
(1)
public class GUI extends JFrame {
JLabel label = new JLabel("Status: ");
public GUI(){...}
public void calculate() {
for(int i = 0; i < 10; i++) {
String one = calculationPartOne(i);
label.setText("Status: " + one);
label.repaint(); //*
calculationPartTwo(i);
}
}
}
This did not work, the JLabel would only update after the calculation has finished. I also tried to .repaint() and .validate() all components involved at the line commented *, but it did nothing.
So, after trying and searching Google/StackoOverflow the whole day I finally have a working solution, but I still do not understand why above does not work. I wanted the GUI to block, so naturally I ran the calculation in the same thread. However, calling any methods to repaint the GUI -inbetween- the calculation (making the calculation stop while the GUI is updated) did not work, and I do not understand why. Can someone explain?
In the end, I used the SwingWorker class to do my calculations, and use it's functions to update the JLabel while calculating. However, as I need the GUI to block, I now disable -all- the components before excuting the SwingWorker and have the SwingWorker re-enable all the components after finishing the calculation.
So, I use SwingWorker, to not block the EDT, but then "fake" to block the EDT by disabling everything? This seems really paradox to me.
Here is an outline of what I have now:
public class GUI extends JFrame {
JLabel label = new JLabel("Status: ");
//I didn't use a list, but it works to illustrate it here
List<Component> GUIComponents = ...;
public GUI() {...}
public void calculate() {
SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
protected Void doInBackground() throws Exception {
for(int i = 0; i < 10; i++) {
String one = calculationPartOne(i);
publish(one);
calculationPartTwo(i); //**
}
}
protected void done() {
setEnableGUI(true);
}
protected void process(List<String> chunk) {
label.setText(chunk.get(chunk.size() - 1));
}
};
setEnableGUI(false);
worker.execute();
}
public void setEnableGUI(boolean e) {
for(Component c : GUIComponents) {
c.setEnabled(e);
}
}
//**
public void calculationPartTwo() {...}
}
This works.
I hope someone can clarify. This solutions feels wrong.
why wrong? the gui thread is for responding to user events only - so you should be doing your heavy lifting in the background - which is what youre doing with a SwingWorker.
also, the best way to prevent a user from changing a componenet is to do exactly that - disable the component before starting the heavu lifting, and enable once its done.
only thing you might want to consider is displaying the results of your calculation in a modal dialog - a JDialog that will pop above the parent window and block it. you could display the intermediate results and progress in this dialog and then once the calculation is done the dialog will close and unblock the UI obscured by it. this will save you fron having to disable all gui components indiviually in a loop and will also give you an option to have a "cancel" button to halt the work immediately.
However, calling any methods to repaint the GUI -inbetween- the calculation (making the calculation stop while the GUI is updated) did not work, and I do not understand why. Can someone explain?
repaint() requests are handled by the RepaintManager and are not done immediately. The RepaintManager basically schedules the repaint. Since repainting is done on the EDT, it can't be done until the EDT is free.
So, I use SwingWorker, to not block the EDT, but then "fake" to block the EDT by disabling everything? This seems really paradox to me.
You can always use an indeterminated JProgressBar. See How to Use Progress Bars.
Or maybe you would prefer to use the Disabled Glass Pane approach.
In some cases you can use:
label.paintImmediately(...);
to force the repainting of a component. But you still have the issue of disabling the GUI so its probably not a solution you should really be using.

How do you repaint a container immediately as components are added in a separate thread in JAVA?

I am using java.
I have a click event that adds "squares" to a container, in a loop. I want each square to show up RIGHT when it is added. I tried running the 'adding of the squares' in a separate thread, but it is not working.
Here is some code I use for 'public class GuiController implements ActionListener, MouseListener':
#Override
public void mouseClicked(MouseEvent e)
{
//createBoardPane();
new Thread
(
new Runnable()
{
public void run()
{
showAnimation();
}
}
).start();
}
public void showAnimation()
{
for(int i = 0; i < model.getAnimationList().size(); i++)
{
String coord = model.getAnimationList().get(i);
int x = Integer.parseInt(coord.substring(0, coord.indexOf(',')));
int y = Integer.parseInt(coord.substring(coord.indexOf(',') + 1, coord.length() - 2));
boolean shouldPlacePiece = (coord.charAt(coord.length() - 1) == 'p');
if(shouldPlacePiece)
{
model.getView().getBoardPane().getComponent(x + (y * model.getBoardSize())).setBackground(Color.BLACK);
}
else
{
model.getView().getBoardPane().getComponent(x + (y * model.getBoardSize())).setBackground(Color.WHITE);
}
model.getView().getBoardPane().repaint();
long time = System.currentTimeMillis();
while((System.currentTimeMillis() - time) < 250)
{
// wait loop
}
}
}
any help is appreciated!
Creating a separate Thread to run for this longish-running task was an excellent idea - unless you want to lock-up interactions with your GUI while doing your animation.
Now, Swing GUI objects are not Thread safe (with few exceptions), so you cannot work with them from a thread other than Swing's Event Dispatch Loop's thread. So take all the GUI update code in your for-loop, and wrap it with a new Runnable (yes, another one).
Then call SwingUtilities.invokeLater(Runnable doRun) with that Runnable on each iteration of the loop.
Your GUI update code will then be scheduled to run ASAP on the Event Dispatch Loop, which will occur while your worker thread goes to sleep (do you have anything against Thread.sleep?).
Alternative: Use SwingWorker instead of Thread
SwingWorker will create and manage a new the Thread for you, and publish data that it (SwingWorker) will cause to be run on the Event Dispatch Loop's thread. You'll override doInBackground with your code. Call publish with parameters to push across into the Event Dispatch Thread. Override process with code to process those parameters and update your GUI.
On gotcha with SwingWorker is that it accumulates published events over a period of about 33 milliseconds. If you're publishing more frequent than that you may get all your events bunched together every 33 milliseconds or so. In you case, 250 milliseconds between updates shouldn't be a problem.

Categories

Resources