Java - JProgress bar not showing (threaded) - java

I am adding a feature to a program to save some content to file. The progress is shown by a progress bar (in its own JFrame), but the progress bar is only being displayed on the last value it reads. I have a global being updated by the main thread, that represents the % of work completed, and the other thread reads this global and updates the progress bar accordingly.
Right now when it runs, the JFrame is empty, then activity completes, then the progress bar shows itself with complete amount. How do i make it update the progress as it goes along (and show the JProgressbar from the start)? Here is my code:
public class GenomeAnnotator{
private JProgressBar csvProgressBar;
private JFrame csvSaveLoadFrame; //for the progress bar
private Container csvCon;
private double csvPercentSaved; //% of work completed
public JFrame m_frame; //main program frame
....
public static void main(String[] args){
...
showGUI();
...
}
public void showGUI(){
...
JMenu file = new JMenu("File");
JMenu exptann = new JMenu("Export annotation..);
JMenuItem exptcsv = newJMenuItem("CSV format");
exptcsv.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
..determine output file + checks...
System.out.println("Writing to .csv file......");
csvSaveLoadFrame = new JFrame("Saving to csv file..");
csvProgressBar =new JProgressBar(0,100);
csvSaveLoadFrame.setSize(300,100);
csvCon = csvSaveLoadFrame.getContentPane();
csvCon.setLayout(null);
csvProgressBar.setBounds(10,10,280,20);
csvCon.add(csvProgressBar);
csvSaveLoadFrame.setResizable(false);
csvSaveLoadFrame.setVisible(true);
ORF [] ora= orfPanel.getAcceptedOrfs();
int val;
double toload = blastData.size() + ora.length; //how much work
double loaded=0.0; //how much work completed
/*Thread that will read % value from global and update prog. bar*/
Thread progressBarMover = new Thread() {
#Override
public void run() {
int previous=0;
while(csvPercentSaved<100){
csvProgressBar.setValue((int)csvPercentSaved);
//tried putting a sleep() in here when testing
//values from global is read successfully
}
}
System.out.println("Thread done!");
csvPercentSaved = 0; //reset value when done
csvSaveLoadFrame.setVisible(false);
}
};
progressBarMover.start();
for (int k=0; k<blastData.size(); k++) {
..do output work...
loaded+=1; //update % values
csvPercentSaved = (loaded/toload)*100;
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
for (int k=0; k<ora.length; k++) {
...do more ouput work...
loaded+=1;
csvPercentSaved = (loaded/toload)*100; //update % value
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
System.out.println("Output file finished!");
csvPercentSaved = 100;
}
});
exptann.add(exptcsv);
file.add(exptann);
}
EDIT
found solution here:
https://weblogs.java.net/blog/mkarg/archive/2010/01/03/did-you-know-swingworker-can-send-progress-status

Several issues there:
Most most important (and I missed this initially!), you're not doing your long running code within the background thread but rather within the Swing event thread, the EDT. I am meaning these two for loops: A) for (int k=0; k<blastData.size(); k++) {...} and B) for (int k=0; k<ora.length; k++) {...} which looks to be the code where you're loading or saving information. This will freeze your GUI right up.
Also important, you're doing Swing calls from within a background thread, including setting the progress bar's value and setting a JFrame's visiblity, something that you never want to do, and that mostly negates the benefits of using the background thread in the first place.
In other words, you're doing all your Swing threading work exactly backwards -- making Swing calls from the background thread and running the long process in the event thread.
Instead, do the opposite -- do all the long-running work in a background thread and make all of the non-thread-safe Swing calls on the EDT.
One way to do this is to use a SwingWorker, do your loading and saving from within its doInBackground(...) method
and set its progress field as progress is being made..
You would then monitor the worker's progress field in a PropertyChangeListener, this being done on the EDT, and then use this to set your progress bar's value.
Or if you have to use your own background thread, then
Have the inner class implement Runnable, not extend Thread
If you make Swing calls from within your background thread, then wrap these calls in a Runnable and queue them onto the Swing event thread via SwingUtilities.invokeLater(yourRunnable)
More minor issues:
You should not be using null layouts and absolute positioning but rather use layout managers. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Your secondary dialog window should be a JDialog, and probably a modal JDialog, not another JFrame. You're not creating and showing a new stand-alone program, but rather are displaying a dialog window off of the main GUI window. If you want the main GUI window non-functioning while the dialog is displayed, then the modal JDialog is the way to go, as it works just like a JOptionPane (which is a form of a modal JDialog), and makes the calling window non-functional while its visible.
For some of my code examples:
How do I make my SwingWorker example work properly?
For a lot more of my examples

Related

Java - Swing form appear empty when get called

I got a mouse click event that call and display a new JFrame form when click. But when I click, the form show up with no element inside it. There are 2 events of that type in my project and I handle them both with the same mechanism. The first one works perfectly, but the second one got the problem. I also use pack() but the problem is still there. Could any one show me how to work this out? Thanks a lot! My project is in Vietnamese, so if any one wants to mention any element in the code or the UI, just writing it with no sign is good enough!
Here are the event handlers:
//The first event
private void tblClientResultMouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
DefaultTableModel dm = (DefaultTableModel) tblClientResult.getModel();
int row = tblClientResult.getSelectedRow();
int col = tblClientResult.getSelectedColumn();
if (col == 7) {
SearchTruyenFrm searchTruyenFrm = new SearchTruyenFrm(listKH.get(row));
searchTruyenFrm.setVisible(true);
}
}
//The second one
private void tblTruyenResultMouseClicked(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
DefaultTableModel dm = (DefaultTableModel) tblTruyenResult.getModel();
int row = tblTruyenResult.getSelectedRow();
int col = tblTruyenResult.getSelectedColumn();
if (col == 6) {
MuonTruyen muonTruyen = new MuonTruyen();
muonTruyen.setTruyen(listTruyen.get(row));
muonTruyen.setPhieuMuon(phieuMuon);
//Trouble here (?)
ThueTruyenInfoFrm infoFrm = new ThueTruyenInfoFrm(listTruyen.get(row));
infoFrm.setVisible(true);
while (infoFrm.isVisible()) {
}
muonTruyen.setDieuKien(infoFrm.getTxtDieuKien().getText());
muonTruyen.setGiaMuon(Float.parseFloat(infoFrm.getTxtGiaThue().getText()));
muonTruyen.setTienPhat(0);
muonTruyen.setPaid(false);
}
}
Inside the constructor:
public ThueTruyenInfoFrm(Truyen selected) {
initComponents();
txtTenTr.setText(selected.getTen());
txtTacGia.setText(selected.getTacGia());
pack();
}
How it happens:
Desired outcome:
Welcome to the wonderful world of "Honey, I've blocked the Event Dispatching Thread (and now nothing works)"
Mouse events (like all GUI based events) are delivered within the context of the EDT, so doing something like...
private void tblTruyenResultMouseClicked(java.awt.event.MouseEvent evt) {
//....
while (infoFrm.isVisible()) {
}
}
will block the EDT and prevent any further processing of events, including paint events, basically hanging your program.
I suggest you start by having a read of The Event Dispatch Thread to get a nutter understand of the overall issue.
To solve your problem, you will want to make use of a modal dialog, which will wait at the point the dialog is made visible and continue executing after it's closed.
Have a look at How to make dialogs.
This is an important lesson, as you should never start with a top level container (like JFrame), but instead, base all you GUIs on something like a JPanel instead, this gives you greater freedom in deciding on when and how those components are displayed.
Form editors won't teach you techniques which produce re-usable or self contained code and I would highly recommend that you consider spending sometime coding them by hand.

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.

Swing setText not updates text before long loop [duplicate]

This question already has an answer here:
MVP, JFrame, JDialog : GUI is freezing
(1 answer)
Closed 8 years ago.
i'm trying to update Swing JSLable text before processing loop, but it's not updating:
public void actionPerformed(ActionEvent event)
{
title.setText("Ready"); // Initialize display
if (source == uploadButton) {
int returnVal = fc.showOpenDialog(UserInterface.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File[] files = fc.getSelectedFiles();
if (files.length < 2) {
title.setText("<html>Text1</html>"); // Is shown
} else {
title.setText("<html>Text2</html>"); // Not displaying, feels like UI is locked here
for (int i = 0; i < files.length; i++) {
filesUploaded.add(uploadFile(files[i]));
}
Images imgs = new Images();
imgs.processImages(filesUploaded); // Some processing loop inside, takes around 0.5~1s
title.setText("Completed"); // displayed corectly.
}
}
}
}
So basically i wish to have sequence:
Ready
Text2
Completed
but i get this sequence (with missing Text2 output):
Ready
Completed
The GUI thread is blocked, you should wrap this part of code into SwingUtilities.invokeLater:
SwingUtilities.invokeLater(new Runnable() {
for (int i = 0; i < files.length; i++) {
filesUploaded.add(uploadFile(files[i]));
}
Images imgs = new Images();
imgs.processImages(filesUploaded); // Some processing loop inside, takes around 0.5~1s
title.setText("Completed"); // displayed corectly.
});
BTW, C-style for loops are not appreciated in Java, you should use "enhanced for" construction:
for (File file: files)
filesUploaded.add (files);
Or even
filesUploaded.addAll(Arrays.asList(files))
That is ok because you block the GUI thread (action is performed in that thread) and it won't update before you exit the method.
Look for "block gui thread java". You'll find that often happens in actionPerformed.
Swing works on Single Threaded Model and that thread of Swing application is called EDT (EventDispatchThread) Its always suggested not to do any heavy processing on this thread because it will block UI thread and your UI will become un-responsive for that duration. I would suggest moving the file upload part in a separate thread to keep your UI responsive.
And use SwingUtilities.invokeLater to schedule any UI related work on EDT from this separate thread.
Another approach may be to use SwingWorker
Use SwingWorker or Foxtrot to implement your long running Task. Your long running task blocks the EDT (Event Dispatcher Thread - main Swing thread) and the Repaint event which must come after event processing cannot be executed.
You should put those HTML tags somewhere else, since it confuses swing when you put text outside of HTML. E.g. You should put the opening tag before the ready word and the closing tag after the completed string. You should also know that the setText method overwrites the text in the element. You should use:
setText(object.getText()+"Text");
For example
title.setText("<HTML>Ready");
//...
title.setText(title.getText()+"Text2");
//...
title.setText(title.getText()+"Completed</HTML>");
Please note that the actionPerformed() method is called by the event dispatch thread which manages the Graphical User Interface and you are using it for a lengthy task. This freezes the GUI since the EDT is not avaliable for updating the GUI. Use a different thread instead:
Thread t=new Thread(new Runnable(){
public void run(){
<your code here>
}
});
Also if you are accessing swing, Java recommends doing it on the event thread, like when you are calling setText() or something similar:
SwingUtilities.invokeLater(new Runnable(){
public void run(){
<your code to access swing>
}
});

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)

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.

Categories

Resources