Issue with updating a JFrame dynamically - java

I am using NetNeans
I have a JPanel that is supposed to show some already input data along with a progress bar while the remainder of the program carries out some lengthy processing (about 1 minute long). Once the processing is done, this same JPanel is supposed to be updated with a few JTextFields containing the processed data.
The issue:
The JPanel shows up completely blank throughout the time period the processing is going on. Once the processing is done, then all the data shows up. Its baffling me. Here is how i've programmed it:
/*The parent class which calls the output JPanel
*Lister is the output JPanel. The constructor calls initComponents(), and fills
*text fields that contain the data that has already been input.
*fillresult then triggers the processing function that takes a while. Once the
*processing function is done, fillresult fills the processed data into the
*respective Text Fields
*/
ListWords = new Lister();
System.out.println("l");
ListWords.setVisible(true);
ListWords.fillResult();
//Lister constructor:
Lister(){
initComponents();
MatchWords = new Matcher();//Matcher() only contains the initialization of a List
jTextField1.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(0, 1));
jTextField2.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(1, 2));
jTextField3.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(2, 3));
jTextField4.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(3, 4));
jTextField5.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(4, 5));
jTextField6.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(5, 6));
jTextField7.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(6));
}
//fillresult():
public void fillResult(){
int rIndex = 0;
MatchWords.makeWords(rIndex);
while(MatchWords.realWords.Head == null && rIndex < 2){
rIndex++;
updateStatusBar();
MatchWords.makeWords(rIndex);
}
Wordpoints = new String[MatchWords.realWords.getSize()];
for(int i=0; i<Wordpoints.length; i++){
Wordpoints[i] = "";
}
for(int i=0; i<MatchWords.realWords.getSize(); i++){
int total = 0;
for(int j=0; j<MatchWords.realWords.getNode(i).getWord().length(); j++){
for(int k=0; k<Scrabbulous.scrab.scralphabet.length; k++){
if(MatchWords.realWords.getNode(i).getWord().charAt(j) == Scrabbulous.scrab.scralphabet[k].getLetter()){
total += Scrabbulous.scrab.scralphabet[k].getValue();
Wordpoints[i] = ""+total;
}
}
}
}
try{
jTextField8.setText(MatchWords.realWords.Head.getWord());
jTextField13.setText(Wordpoints[0]);
}catch(NullPointerException e){
jTextField8.setText("No Match Found");
jTextField13.setText("-");
}
try{
jTextField9.setText(MatchWords.realWords.getNode(1).getWord());
jTextField14.setText(Wordpoints[1]);
}catch(NullPointerException e){
jTextField9.setText("No Match Found");
jTextField14.setText("-");
}
try{
jTextField10.setText(MatchWords.realWords.getNode(2).getWord());
jTextField15.setText(Wordpoints[2]);
}catch(NullPointerException e){
jTextField10.setText("No Match Found");
jTextField15.setText("-");
}
try{
jTextField11.setText(MatchWords.realWords.getNode(3).getWord());
jTextField16.setText(Wordpoints[3]);
}catch(NullPointerException e){
jTextField11.setText("No Match Found");
jTextField16.setText("-");
}
try{
jTextField12.setText(MatchWords.realWords.getNode(4).getWord());
jTextField17.setText(Wordpoints[4]);
}catch(NullPointerException e){
jTextField12.setText("No Match Found");
jTextField17.setText("-");
}
Could someone please be of assistance? Oh yes, and I read about adding a .refactor() and/or repaint(), but atleast the jForms initialized in the constructor should show up? :/
Also, it might be worth mentioning that all the code that is currently in the fillresult() function was initially in the constructor, and the output was displaying. However, since I want the progressbar here, I've shifted it to a new function. help please?

"The issue: The JPanel shows up completely blank throughout the time period the processing is going on. Once the processing is done, then all the data shows up."
What's happening is that everything is occurring on one thread, the Event Dispatch Thread (EDT). In order for an event to occur, like your JPanel updating, the process before it needs to complete.
As stated about the EDT:
" Tasks on the event dispatch thread must finish quickly; if they don't, unhandled events back up and the user interface becomes unresponsive."
To work around this, you'll want to run your long process in a background thread. You should have look at Concurrency in Swing - the section on Worker Threads and SwingWorker in particular.

Related

How to NOT use thread.sleep for an image slide show in java

I'm trying to create something like an info screen that just scrolls through images (and maybe one day pdfs) that are uploaded with a small managment app I wrote.
After some reading I think I understand why I should NOT have Thread.sleep in my for loop. I read some posts on stackoverflow and other pages that teached me not to do it.
Apparently I'm supposed to use ExecutorService for something like this, but I can't wrap my head around it.
So after preparing my GUI and everything else I finally call my showImages and stay in this while loop.
After each loop I reload the records from the file systems, so any changes are represented on the screen.
I created a metafile containing the preferred delay for each image and just sleep for the amount of time.
private void showImages() {
//noinspection InfiniteLoopStatement
while (true) {
loadRecords();
for (ImageRecord record : records) {
System.out.println("Showing: " + record.getFilename());
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
int delay = record.getDurationAsInt();
System.out.println("Waiting for " + delay + " milliseconds");
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
So if I understand correctly I could use something like
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(showImage(),0, delay, TimeUnit.MILLISECONDS);
But this would trigger the showImage() every "delay" millisecond and I still need to increment some counter, to get to the next image in my records.
Could someone push me in the right direction? I'm kinda lost at the moment.
All the comments on my question can be considered good ideas an and one of them also lead me to my final result. But it in the end, the matter was, that I did not understand how to implement a timer into my loop.
Or in other words, the loops had to be gone and instead use a counter and set the delay for the follow up record inside the timer loop.
private void loopImages() {
loadRecords();
recordSize = records.size();
int delay = records.get(1).getDurationAsInt();
timer = new Timer(delay, e -> showImages());
timer.start();
}
private void showImages() {
if (recordIndex == recordSize) {
loadRecords();
recordSize = records.size();
recordIndex = 0;
}
ImageRecord record = records.get(recordIndex);
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
recordIndex++;
if (recordIndex < recordSize) {
record = records.get(recordIndex);
int delay = record.getDurationAsInt();
timer.setDelay(delay);
}
}

Java - Sleeping thread to simulate changing image

So I've done a lot of searching and I can't quite find a solution to the problem I'm having. Designing GUIs with Java's JFrame seems fairly straight-forward, but updating them visually when it's more than just a setText call seems much more tricky, especially when you want to simulate some wait time for visual recognition in between. I'm not familiar with multi-threading and all of the solutions I found, I couldn't seem to get to work how I needed them to.
What I'm trying to do is to simulate dice rolling before landing on a particular side for a simple dice game. To do this, I'm just switching between different sides of dice images a set number of times (10, for example) every fraction of a second (150 ms in this case).
The interesting thing is that this code at its current state works EXACTLY how I want it to on start-up (which does a full 10 roll cycle once before accepting any user-input, but every component of the GUI is visible and working as expected). The problem is that when re-rolling, the GUI goes blank until all the thread sleeping is completed.
With my lack of knowledge about how this sort of thing, I don't understand why it would work the way I want the first time, but not any time after. Hopefully I can get some assistance about the right way to accomplish this or a fix for what I'm currently doing. Thanks!
private void rollDice()
{
for(int i = 0; i < 10; i++)
{
dice1 = (int) (Math.random() * 6) + 1;
dice2 = (int) (Math.random() * 6) + 1;
img1 = new ImageIcon(dice1 + ".png");
img2 = new ImageIcon(dice2 + ".png");
try
{
Thread.sleep(150);
}
catch(Exception e)
{
}
lbl1.setIcon(img1);
lbl2.setIcon(img2);
}
}
Try this I found here.
new Thread(new Runnable
{
public void run()
{
for(int count = 0; count < PREROLLS; count++)
{
//Add your for loop logic here!
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
//then update you icon here. I would start with one die if I were you.
imgDice1 = new ImageIcon(path + dice1 + ".png");
}
});
try { Thread.sleep(100); } catch(Exception e) {}
}
}
}).start();

Changing text in JTextArea [duplicate]

This question already has answers here:
JFrame not updating before thread.sleep()
(2 answers)
Closed 6 years ago.
I'm trying to change the text in JTextArea whenever the following PRINT() method is run. The entire program is long so I can only post a bit but basically a [50][50] char array contains is being turned into 50 lines of 50-character strings. The JFrame holding this JTextArea is initiated when a button is pushed and immediately prints the char array into the JTextArea (when I've made it so that the char array is never updated). The program is meant to update this text every few seconds.
Instead of updating every second, though, The JFrame appears as basically a blank box until the last time PRINT() has been run. Currently, it is meant to print 1...10 in separate lines through 10 calls to PRINT(), each after output.outputLines has been updated with the added number. Instead, it only shows a blank frame until the last number has been loaded, then prints the whole thing.
PRINT() method:
public boolean PRINT(){
String result = ""; //Used to hold the characters to be printed to the screen
for(int x = 0; x<50; x++){
for(int y = 0; y<50; y++) {
result = result + String.valueOf(output.outputLines[x][y]);
}//End of inner FOR statement
result = result + "\n";
output.jTextArea1.setText(result);
}//End of outer FOR statement
return true;
}
code used to pause between PRINT() calls:
try {
java.lang.Thread.sleep(1000);
catch (Exception ex) {
System.out.println("Exception in Thread.sleep()");
Can anybody help me to figure out what is going wrong?
You're calling Thread.sleep(...) on the Swing event thread, essentially putting it and your GUI to sleep. The solution (as always) is don't do this. If you want to pause use a Swing Timer. If you need to run long-running code, use a background thread such as a SwingWorker.

How can I make a progress complete show in a JTextArea while running code that's figuring out what to display in that JTextArea?

I don't know if this is possible. I'm making a lottery application, and I'm trying to use as few GUI components as possible. So I have a JTextArea that is supposed to show the following message (for example):
"Calculating...55.4%"
When I print it to console, it shows just fine, but it won't print it to the JTextArea. I tried to use SwingUtilities.invokeLater but that's not working either.
for (int x = 0; x < daysBetween; x++)
{
completion = "Calculating..." + df.format((100 * (x + 1)) / daysBetween) + "%";
if (!textArea.getText().equals(completion))
{
textArea.setText(completion);
}
/*
Here I have a lot of irrelevant code that compares your tickets to the winning tickets and counts your winnings and other statistics...
*/
}
ticketReport += "Computation time: " + getElapsedTime(start, System.nanoTime());
ticketReport += "\nEnd date: " + (cal.get(Calendar.MONTH) + 1) + "/" + cal.get(Calendar.DAY_OF_MONTH) + "/" + cal.get(Calendar.YEAR);
ticketReport += "\nTotal # of tickets purchased: " + numOfTicketsPurchased;
/*
etc. with filling out the ticket report
*/
textArea.setText(ticketReport);
As you can guess, I want the JTextArea to update as I set the text of the textArea in the for loop above. It does not update the JTextArea until the end of the method, which is the very bottom when I set the text area to show the ticket report.
MY END GOAL: I want to eventually turn this into an Android phone application, so that's why I don't want to use any pop-ups or anything.
My original SwingWorker approach made it where it showed the completion percentage status in the JTextArea. I added an option to cancel the operation, and when I attempted to cancel, the "thread" (SwingWorker) would not cancel. When I cancel and restart, it would stack constantly. Each successive starting while the other was going would cause the calculations to go slower and slower, and then give incorrect information.
The way I did it this second time solved the original problem and this new problem. I created a new private class that extends Thread, overran its run() method, and inserted the code to be executed in the thread in that method. I noticed any method to stop the thread was deprecated because of potential issues, so I created a boolean to be set when the user requests to stop the thread, and inside the thread it periodically reads the value of the boolean, then exits when it equals to true.
The way I start the thread is by re-initializing the object BackgroundCalc and calling its start() method.
private class BackgroundCalc extends Thread
{
public void run()
{
/*
initializing stuff here
*/
for (int x = 0; x < daysBetween; x++)
{
progress = (100 * (x + 1)) / daysBetween;
if (destroyCalcThread) return;
/*
background calculations here
*/
}
/*
finish up thread here
*/
}
}
I'm a little inexperienced with threads, so hopefully my struggle here and the way I explained it will help someone else who's inexperienced with the concept.
/*
EDIT: This temporarily solved the problem, but I ran into another problem. Now when I attempted to cancel the SwingWorker by calling its cancel method with parameter true, it would not cancel. Please see my other answer for the way I solved this.
*/
I figured it out. The method above I put inside a SwingWorker, as follows:
SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>()
{
#Override
protected Void doInBackground() throws Exception
{
//method here
}
}
Inside that method, I called setProgress method with parameter "(100 * (x + 1)) / daysBetween" to show the correct % completion. Then after that, I added this:
sw.execute();
sw.addPropertyChangeListener(new PropertyChangeListener()
{
#Override
public void propertyChange(PropertyChangeEvent arg0)
{
textArea.setText("Calculating..." + sw.getProgress() + "%");
if (sw.getProgress() == 100)
textArea.setText(ticketReport);
}
});
It shows percentages as whole numbers rather than the #.## I originally wanted, but I can change very easily if I wanted to.

Getting a position in a JTextArea and using a timer

I'm working on a school project and I'm hoping if you guys could help me.
I have this:
JTextArea processTxt = new JTextArea();
And when I press this button,
JButton sortBtn = new JButton("SORT!");
This would display this on processTxt.
public void sort(String type, String order, int size) {
int tempInt, int1, int2;
processTxt.append("Pass 0:\n");
for (int i=0; i<size; i++) {
if (type.equals("int")) {
processTxt.append(intList.get(i)+" ");
}
}
processTxt.append("\n");
for (int i=0; i<size-1; i++) {
int x=0;
processTxt.append("Pass "+(i+1)+": \n");
while (x<size-1) {
int y = x + 1;
if (type.equals("int")) {
int1 = intList.get(x);
int2 = intList.get(y);
if (order.equals("asc")) {
if (int1 >= int2) {
tempInt = int1;
intList.set(x, int2);
intList.set(y, int1);
}
}
else if (order.equals("desc")) {
if (int1 <= int2) {
tempInt = int1;
intList.set(x, int2);
intList.set(y, int1);
}
}
}
for (int z=0; z<size; z++) {
if (type.equals("int")) {
processTxt.append(intList.get(z)+" ");
}
}
processTxt.append("\n");
x++;
}
}
processTxt.append("Sorted:\n");
for (int i=0; i<size; i++) {
if (type.equals("int")) {
processTxt.append(intList.get(i)+" ");
}
}
}
I was thinking:
How could I get the position of int1 on the text area?
Is there any way that I could use a timer to delay the output on the text area? I have used thread.sleep() but it won't work.
1) how could i get int1's position on the textarea;
Apart from physically searching the text, you could maintain reference to the location that it would have been inserted, before you inserted.
You could use the JTextArea's caret position if you are inserting text at the current cursor position, otherwise, you will need to determine the index point from within the Document you are updating.
2) is there any way that i could use a timer to delay the output on the textarea? i have used thread.sleep() but it wont work.
Yes, but understand, this becomes increasingly more complex.
You could use a Swing Timer which will would trigger a callback at a regular interval, allowing you to update state of the sort and update the UI. This is useful as it triggers the callback within the context of the Event Dispatching Thread, making it safe to update the UI from within.
It does, how ever, increase the complexity, as the Timer acts as a kind of pseudo loop.
You could use a SwingWorker instead. This would allow you to take your current code and run it within the doInBackground method, which is executed outside of the Event Dispatching Thread, allowing you to use Thread.sleep to inject a delay where you want them without affecting the UI.
The problem is, you will need to use the SwingWokers publish/process methods to push updates to the UI, this disconnects the time you make a change to the model to the time it appears in the UI, which may present issues with determining the location that a given update should occur (within the context of your first question).
Take a look at:
Concurrency in Swing
How to use Swing Timers
Worker Threads and SwingWorker
for more details

Categories