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.
Related
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
I tried to figure this out myself but I can't. I'm stuck at a strange problem.
I have a Java Program with multiple classes and forms (I use Intellij and the build in GUI-Creator). When I switch from one Screen to another I just call frame.setVisible(false); at the leafing window and frame.setVisible(true); at the window I want to show next.
On a Button Click I make this:
In Class 1:
if (e.getSource() == umschaltenButton) {
this.mainW.goToMainWindow();
logger.log(Level.INFO, "Switched Back to MainMenu");
frame.setVisible(false);
}
And here is the weird part.
In Class 2:
public void goToMainWindow() {
frame = tvElectronics.drawMainWindow(); // I get a new Frame with new Images and so on
frame.addMouseListener(al);
frame.add(BotomPanel); // in here is the JComboBox
frame.setSize(LENGTH, HEIGHT);
comboBox1.removeAllItems(); // Here it tryes to refere to the old frame before i made frame = tvElectronics.drawMainWindow();
Vector<String[]> content = tvElectronics.getContent();
for (int i = 0; i < tvElectronics.getAnz(); ++i) {
comboBox1.addItem((i + 1) + ". " + content.get(i)[3]);
}
comboBox1.setSelectedIndex(chanel);
frame.setVisible(true);
}
And so it tries to update the old frame from class2 which no longer exists because of the new one I just created. And so I have 2 frames open: one as I want it and one strange old frame form class2.
My problem is that I want bind my JComboBox to a new Frame and update it but it is still connected to the old one and that causes weird problems like jumping back in the function. I mean it is at the last line of goToMainWindow() and then it starts again at the first line.
First off you should avoid swapping JFrames as your program does since this is a very annoying GUI design. Please read The Use of Multiple JFrames, Good/Bad Practice? for more on this.
Next, it's impossible for us to tell what GUI view your JComboBox is associated with.
But having said that, it really shouldn't matter. Instead of doing what you're doing, I would give the display class that holds a JCombBox a public method that you call on the containing display class that clears the contained JComboBox's model or that places items in the model. This way, there will be no ambiguity as to which JComboBox you're referring to, and this way you avoid directly exposing a view's inner components.
As an aside, I try to gear my display or view classes towards creating JPanels, not JFrames as this will give my code much greater flexibility.
For example
// my display class
class Display1 {
private DefaultComboBoxModel<String> myModel = new DefaultComboBoxModel<>();
private JComboBox<String> myCombo = new JComboBox<>(myModel);
public void removeAllComboElements() {
myModel.removeAllElements();
}
public void addElement(String ele) {
myModel.addElement(ele);
}
}
Same for your Display2 class. Then you can call the correct method on the JComboBox that is held by the correct view/display.
This way, when you swap displays, perhaps by using a CardLayout, you can clear the JComboBox in the display that is being shown by calling its own method to clear its own combobox's model.
I am not sure how to explain this. But I'll try.. Fest slows down to crawl while working with JXTreeTable of swingx. It doesn't slow down initially. It works fine for a while, but after a while when the same actions are repeated it slows down badly.
I have raised a bug for this in github. Please tell me if this is something that I am doing wrong instead. I am not able to reproduce the problem when I tried to create an SSCCE.
Anyway, here's a video of it slowing down.
http://screencast.com/t/liNttCw2In0w
At times 0.39s to 0.40 a set of operations are performed. These are done when there is one row in the JXTreeTable.
At time 0.49 to end of recording the same operation is repeated but there are now 3 rows in the table, it takes very long for the mouse to click.
I have attached a screenshot taken at the time when fest slows down, which attempts to explain it more
This is the code that does the work:
Step 1) Selecting a node from the tree is done as below:
JTreeFixture folioTreeFixture = importShareholders.panel("treePanel").tree("folioTree");
folioTreeFixture.separator("~");
folioTreeFixture.selectPath(new StringWrapper("Shareholders", true)+"~"+
(ShareType.isEquity(shareType) ? new StringWrapper("Equity Folios", true) : new StringWrapper("Preference Folios", true))+"~"+
new FolioTreeRep(folio.getName(),folioNo, shareType).toString());
Step 2) Searching and selecting a row from the JXTreeTable
int selectRow=-1;
JTableFixture table=importShareholders.table("historyTable");
for(int i=0;i<table.rowCount();i++){
String certificateNumber = table.cell(TableCell.row(i).column(ShareholderHistoryTable.columnIndex(ShareholderHistoryTable.CERT_NO))).value();
String remarks=table.cell(TableCell.row(i).column(ShareholderHistoryTable.columnIndex(ShareholderHistoryTable.REMARKS))).value();
if(StringUtils.isEmpty(remarks) && StringUtils.isNotEmpty(certificateNumber) && Integer.parseInt(certificateNumber)==certNo){
selectRow=i;
break;
}
}
if(selectRow==-1){
fail("Couldn't find certificate number to transfer");
}
Step 3) Showing the pop up menu and clicking the row
table.showPopupMenuAt(TableCell.row(selectRow).column(0)).menuItem("btnTransfer").click();
I am not sure why its slowing down. Please let me know if there is any more info I can help with. Would be grateful for some help in solving the problem
I have profiled the application and I dont find anything untoward happening. I dont have a lot of experience profiling applications. I would be grateful if someone could have a second look at this. I profiled it with yourkit and have uploaded the snapshot dump here:
https://www.dropbox.com/s/dh976v01q9c3sgj/ImportShareholderData.shouldTransferAndSplit-2013-06-14-shutdown.snapshot.zip
Any help will be greatly appreciated..
EDIT:
I think I forgot to mention the same thing works when I do it manually. It only slows down with fest. That leads me to believe that there is an issue with fest maybe?
Sorry about that.
EDIT 2:
As request by Marcin (sorry for the delay Marcin).. Here's the code when the first row is getting split
public List<Integer> splitRowEqually(ShareType shareType, String date, int folioNo, int certNo, int... certnos) throws NoSuchFieldException, TorqueException {
//select a tree node
selectFolioInTree(shareType, folioNo);
Pause.pause(new Condition("Wait until tab is created") {
#Override
public boolean test() {
return importShareholders.tabbedPane().tabTitles().length>0;
}
});
//select a row on the table to split
int row=selectRowWithCertNunber(certNo);
List<Integer> rowsIndexes=new ArrayList<Integer>();
JTableFixture table = importShareholders.table();
//show popup menu on that row and select split
table.showPopupMenuAt(row(row).column(columnIndex(TRANS_TYPE))).menuItem("btnSplit").click();
DialogFixture splitDialog=FinderUtilities.getDialogWithTitle("Split Share Certificate");
splitDialog.textBox("tfDateOfSplit").setText(date);
int noOfShares= Integer.parseInt(table.cell(row(row).column(columnIndex(NO_OF_SHARES))).value());
int distFrom= Integer.parseInt(table.cell(row(row).column(columnIndex(DIST_NO_FROM))).value());
int distTo= Integer.parseInt(table.cell(row(row).column(columnIndex(DIST_NO_TO))).value());
//split the row into the number of times decided by the certnos array
int noOfSharesInEachSplit=noOfShares/certnos.length;
for(int i=0;i<certnos.length;i++){
int distToInSplit = distFrom + noOfSharesInEachSplit-1;
enterSplitRowDetails(splitDialog, certnos[i], distFrom, distToInSplit<=distTo ? distToInSplit : distTo);
distFrom=distToInSplit+1;
rowsIndexes.add(row++);
}
splitDialog.button("btnSplit").click();
return rowsIndexes;
}
//selects a node from the left hand side tree
public void selectFolioInTree(final ShareType shareType,final int folioNo) throws TorqueException {
JTreeFixture folioTreeFixture = importShareholders.panel("treePanel").tree("folioTree");
folioTreeFixture.separator("~");
// I use these wrapper classes - StringWrapper and FolioTreeRep, so that I can get a html
// string for the tree node like <html><b>Shareholder</b></html>
String treePath = new StringWrapper("Shareholders", true) + "~" +
(ShareType.isEquity(shareType) ? new StringWrapper("Equity Folios", true) : new StringWrapper("Preference Folios", true)) + "~" +
new FolioTreeRep(mapOfFolioNames.get(folioNo), folioNo, shareType).toString();
folioTreeFixture.clickPath(treePath);
}
//search the table for a row that contains the cert no provided in the Certificate Number column.
private int selectRowWithCertNunber(int certNo) throws NoSuchFieldException {
int selectRow=-1;
JTableFixture table=importShareholders.table("historyTable");
for(int i=0;i<table.rowCount();i++){
String certificateNumber = table.cell(row(i).column(columnIndex(CERT_NO))).value();
String remarks=table.cell(row(i).column(columnIndex(REMARKS))).value();
if(StringUtils.isEmpty(remarks) && StringUtils.isNotEmpty(certificateNumber)
&& Integer.parseInt(certificateNumber)==certNo){
selectRow=i;
break;
}
}
if(selectRow==-1){
fail("Couldn't find certificate number to transfer");
}
return selectRow;
}
// enter details on the table in the SplitDialog
private void enterSplitRowDetails(DialogFixture splitDialog, int cert, int distFrom, int distTo) {
splitDialog.button("btnAdd").click();
int row = splitDialog.table().rowCount();
splitDialog.table().enterValue(row(row - 1).column(0), String.valueOf(cert));
splitDialog.table().enterValue(row(row - 1).column(1), String.valueOf(distFrom));
splitDialog.table().enterValue(row(row - 1).column(2), String.valueOf(distTo));
}
Emm... It is quite interesting question;
I suppose the question contains less really required details especially the robot integration and IO solutions details so I cannot just give you a proper answer...
Anyway, I'll try to analyze the problem in voice a little bit in my way...
First. According to your screenshot comments, I can notice that all "30s pauses or so" occur on some, as I can get it, stream reading process "select/search" (your app gets some data to output etc). So maybe it is much deeper than you think because it is probably thread problem;
I couldn't find the GuiQuery/GuiTask/GuiActionRunne classes usage in your code snippets so I may suggest the "synch problem" may take place in the mentioned case...
Second. OK... If it is still the thread problem I may suggest the robot and IO solutions are both in some ONE thread (the Main thread or something) because, according to your tips as "At times 0.39s to 0.40 a set of operations are performed. These are done when there is one row in the JXTreeTable." ... GUI is waiting for some process to be completed...
Third.
And again... According to this issue as
"It is recommended to turn on an automated check to verify that all
Swing components updates are done in Swing’s EDT (Event Dispatcher
Thread). For those unfamiliar with the EDT, it is responsible for
handling and updating all Swing widgets in a separate thread, causing
that the application never loses responsiveness to user gestures (just
in short, more about the EDT here). To do that, we add the following
hook to the test:"
import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
import org.junit.BeforeClass;
...
#BeforeClass
public static void setUpOnce() {
FailOnThreadViolationRepaintManager.install();
}
Next step is to launch the frame or dialog. As JUnit runs in its own
thread, we must launch the frame or dialog through Fest, to ensure,
again, that EDT is properly used:
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.fixture.FrameFixture;
import org.junit.Before;
...
private FrameFixture testFrame;
private AllTypesFrame frame;
...
#Before
public void setUp() {
frame = GuiActionRunner.execute(new GuiQuery<AllTypesFrame>() {
protected AllTypesFrame executeInEDT() {
return new AllTypesFrame();
}
});
testFrame = new FrameFixture(frame);
testFrame.show();
}
... makes me think it is maybe the "thread-problem" which is described in the First and Second tips...
so, as a conclusion, I can say that maybe you have to multi-thread your test a little more because it is obviously some kind of synch problem...
P.S.
#sethu, before you start your debugging I want to point a little...
I still suspect threads conflict is taking place here (see my previous tips) because, as I may notice, your code snippets are showing static expressions usage to invoke methods like Pause.pause(...) or FinderUtilities.getDialogWithTitle(...) etc I cannot see the whole project architecture so it is hard to analyze according the represented bits but it is pretty clear the "manual testing" goes fine because action listeners react in real time but fest testing does the annoying delays because it uses some "timer" to countdown until a click emulation occurs etc and of course it is a background process which needs a separate thread... Watch debugging carefully maybe somewhere in your code UI thread and fest thread do conflict (see static methods, thread.sleep etc) the points where fest thread could block (override) the UI's one... :S By the way what method Pause.pause(...) does?
P.P.S.
If you have some additional information please comment my answer
Report if my answer helps you
I do not know what are your robot settings but you can at least try to set idleTimeout and other timeouts for the robot you use. The default timeout is 10 sec (look in org.fest.swing.core.Settings). After I decrease it (first 1000ms, next 100ms) I noticed that robot works faster.
robot().settings().idleTimeout(YOUR_TIMEOUT)
Here is my test setup and one test method. Hope is clear.
Here you have my before/after
private static int testMethodCounter = 0;
private static EmergencyAbortListener mEmergencyAbortListener;
private FrameFixture workbenchFrame;
private Robot robot2;
private static final int myIdleTimeout = 100;
#Before
public void setUp() throws Exception {
// my workaround to be able to start the app once and reuse for all tests
if (testMethodCounter == 0) {
robot2 = BasicRobot.robotWithNewAwtHierarchy();
GuiActionRunner.execute(new GuiTask() {
#Override
protected void executeInEDT() throws Throwable {
ApplicationLauncher.application(ProgramRun.class).start();
}
});
} else {
// the second test method see all before created gui components
robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
}
testMethodCounter++;
robot2.settings().idleTimeout(myIdleTimeout);
workbenchFrame = WindowFinder.findFrame(FrameNames.WORKBENCH.getName()).withTimeout(10000)
.using(robot2);
}
#After
public void tearDown() {
// current window will not be closed
robot2.cleanUpWithoutDisposingWindows();
}
#Test
public void someSmokeTest() throws Exception {
Pause.pause(1000);
// perform some test specific gui actions
// here is very important moment, I need new robot because
// workbenchFrame.button(ButtonNames.SOME_BUTTON_NAME).click(); creates new dialog
// which will be avilable in AWT stack after creation
robot2.cleanUpWithoutDisposingWindows();
robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
// the new Robot needs timeout setup
// without this I have long breaks between gui events
robot2.settings().idleTimeout(myIdleTimeout);
workbenchFrame.button(ButtonNames.SOME_BUTTON_NAME).click();
DialogFixture dialog = WindowFinder.findDialog("dialog2")
.withTimeout(5000).using(robot2);
// some actions on the dialog
// once again next new dialog
workbenchFrame.menuItem(MenuItemNames.NAME).click();
robot2.cleanUpWithoutDisposingWindows();
robot2 = BasicRobot.robotWithCurrentAwtHierarchy();
// and idleTimeout setup once again, new Robot needs new setup
robot2.settings().idleTimeout(myIdleTimeout);
// next actions + assertion
}
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.
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.