Java Swing GUI freezes - observer pattern - java

For a program I am creating I have used the observer pattern, yet whilst my Observable sends data almost constantly, simulated with a loop here because the actual code is hooked up to a device and measures the data, my update(); method runs as it should but swing doesn't.
Swing only updates the JTextField AFTER the loop has finished, yet when I use System.out.println() it iterates nicely each update.
Observable code:
public void collectData()
{
for(int i = 0; i < 10; i++)
{
currRandom = (Math.random() * 10);
for(Observer o : observers)
{
notify(o);
}
}
}
Observer (SWING) code:
public void update()
{
jRecievedData.setText(jRecievedData.getText() + "\n" + Double.toString(PVC.pc.getCurr()));
jlAverage.setText("Average: " + PVC.getAverage());
jlMin.setText("Minimum: " + PVC.getMin());
jlMax.setText("Maximum: "+ PVC.getMax());
// setText updates slow
}
Any help would be greatly appreciated! (I have the feeling this is going to be a threading problem , but I'm not sure and if it is, I still don't know how to do that with swing)

Instead of looping through your observers explicitly, let Observable do it, as shown here.
this.setChanged();
this.notifyObservers();
Also, verify that you are not blocking the event dispatch thread. If so, consider using SwingWorker, which greatly simplifies Swing threading.

Related

JavaFX pausing during for loop WITHOUT using Thread.Sleep()

I'm coding this in JavaFX using a FXMLController and the scene builder.
I'm currently creating an Instagram "liker" mostly just for practice as I'm a Computer Science major, and I just like coding.
My issue is, I have a for loop that "likes" 20 photos for each hashtag, I obviously need a delay or I'd get banned rather quickly. The user can choose the delay, then I randomize this delay so it's not too robotic.
The only way I've been able to use this delay is through the thread.sleep method, but since I'm coding on the FXMLController, it freezes the whole UI, and somehow stops other textareas from being updated. This is obviously not ideal.
I've looked everywhere trying to find an alternative to this, but I can't find anything that works in my program.
I'll give the whole method:
private void start() {
sleepWhen();
tagNumber++;
int delays = Integer.valueOf(delay.getText());
delays = delays * 1000;
if (loaded) {
runTime.clear();
runTime.appendText("Liking...");
for (int i = 0; i < 20; i++) {
webEngine.executeScript("document.getElementsByClassName('btn btn-default btn-xs likeButton')[" + i + "].click()");
try {
double random = Math.random() * 1000;
random = random + delays;
delays = (int) random;
System.out.println(delays);
likes++;
likesNumber.clear();
likesNumber.appendText(String.valueOf(likes));
System.out.println(String.valueOf(likes));
Thread.sleep(delays);
delays = Integer.valueOf(delay.getText());
delays = delays * 1000;
} catch (InterruptedException ex) {
//do nothing
}
System.out.println("tag number" + tagNumber + "numbertags" + numberTags);
if (!sleep) {
if (tagNumber < numberTags) {
System.out.println("Loading tag:" + tagNumber);
loadTag(tagNumber);
}
}
}
}
}
This likes 20 photos, then loads another hashtag, and repeats.
Any help with this would be great, thanks.
The freeze is because you are trying to do everything on the same thread, when you are using Thread.sleep(delays), you are actually putting your UI thread to sleep and hence the lag !
Try using different thread for your work, you can either use a worker thread, if whatever you are doing will not affect the UI
or you can use Platform.runLater(), if you want the changes to shown on the UI !
For more details :
concurrency in javafx !
Platform.runLater and Task in JavaFX

Java strange graphics blinking in while lock.await()

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)

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.

Where should I use thread.sleep in a java swing program?

Right now I've programmed a method for use in a swing program that is supposed to slow down the speed the method it's called in is run (via thread.sleep(x) ) and instead of slowing down the speed of refresh it's just causing the whole program to hang and the screen to refresh at the end of the methods execution.
Here's my code:
public final void doBubbleSort(String numbers[], JButton numButton[]){
for (int k = 0; k < numbers.length - 1; k++){
String str1 = "";
boolean isSorted = true;
for (int i = 1; i < numbers.length - k; i++){
if (Integer.parseInt(numbers[i]) < Integer.parseInt(numbers[i - 1]) ){
try{
String tempVariable = numbers[i];
numbers[i] = numbers[i - 1];
numbers[i - 1] = tempVariable;
isSorted = false;
str1 = numButton[i].getText();
numButton[i].setText(numButton[i-1].getText());
Thread.sleep(100);
}catch(Exception e){ System.err.println("Error: " +e.getMessage()); }
numButton[i-1].setText(str1);
}
}
if (isSorted)
break;
}
}
If anyone can tell me where I should be putting thread.sleep() to make it delay the display of items being swapped I would be greatly appreciate it.
Thanks!
You should never sleep in the event dispatch thread.
Create a model, (in your case a list)
Create a view (your swing gui)
Make sure the view gets updated once the model changes (by adding a listener for instance)
Create a separate thread that updates the model regularly (using Thread.sleep if you so like)
When changing "speed" change the sleep times for the updater-thread
You may want to have a look at the Timer class (which provides a nicer interface for doing things at a regular interval) or the Swing Workers.
You cannot do animation by putting a loop in an event handler like this. You must do it by using a separate thread, either explicitly or by using the javax.swing.Timer class. If you loop and sleep on the event thread, as you're doing, then during the entire time the loop is running, the event thread is unavailable to perform its other duties, like painting the screen.
See this article for information about using the Swing timer class.

How to immediately change text of jLabel inside MouseEvent handler?

I have problem with refreshing swing components inside Mouse action event handler. Problem is that all jLabels which are changed in this function that their changes are visible after jButton1MouseClicked() is done. Here is my function:
private void jButton1MouseClicked(java.awt.event.MouseEvent evt) {
int cycles = Integer.parseInt(c.settings.get("cycles"));
statusMessageLabel.setText("Data collection in progress...");
for(int i=1;i <= Integer.parseInt(c.settings.get("cycles"));i++) {
jLabelCycle.setText(Integer.toString(i));
//here are some functions which are implementing data collection
if(i < cycles){
int counter = Integer.parseInt(c.settings.get("interval"));
while(counter >= 0){
jLabelTimer.setText(Integer.toString(counter));
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(AppCView.class.getName()).log(Level.SEVERE, null, ex);
}
counter--;
}
}
}
statusMessageLabel.setText("Data collection has been finished.");
}
Can you please help me ? I really need this changes to be visible immidiaetly because one jLabel visualize counting till next cycle and second one is visualizating actual cycle number. I tried call function repaint() on all jLabels after i changed them but it didnt help.
Thank you all for any help.
In AWT, much like most other GUI systems, all events including repaints are done on a single thread, the AWT Event Dispatch Thread (EDT). You are blocking that thread in the event listener, so the repaint events on the label cannot get through.
I suggest rewriting your code to use use javax.swing.Timer instead of Thread.sleep.
In general, long running operations should be handled on a separate thread. Any operations touching AWT or Swing components should be queued for running on the AWT EDT using java.awt.EventQueue.invokeLater.

Categories

Resources