I know many people have asked this question before but I couldn't find any answer that solved my problem. My code is like this:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
myfunction(); //runs for a very long time
}
The original text of JLabel is "OFF". Now I want to change the text to "ON" when the mouse is clicked but the text doesn't set until myfunction() is complete (which may take several minutes).
I have tried the invalidate function, making a separate function for setting the text but nothing is working.
Please help me with this problem!
The problem is that mouseClicked(...) is executed on the UI Thread. That is the Thread that is responsible for handling all sorts of user actions (like a mouse click) and also the drawing of components (like updating the text of the label on screen). If you execute your long running method call on the UI thread, it will be blocked and can't draw anything until execution is complete. You'll have to use multi threading to get around this problem.
The following might not be the most elegant solution, but if you are new to multi threading it will get the job done:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
(new Thread() {
public void run() {
myfunction();
}
}).start();
}
It will spawn a new Thread that handles your method, which will let the UI Thread continue doing its thing. consider deactivating the button that has just been clicked, so the user can't start the execution while it is already in progress (which usually is what you want..)
Related
The purpose of the following code is to handle menu option choice. Because processing in method update() takes quite a bit time, I want to display info before this takes place.
My info is simply an anchor pane with label in it, which normally is set to be not visible. Unfortunately, the code below sets anchor pane visible only for a very short time after update() terminates. How can I do it properly?
public void updateRates(ActionEvent event)
{
updateInfo.setVisible(true);
update();
updateInfo.setVisible(false);
}
Most user interface libraries are single threaded. That thread is created by and controlled by the UI system. All listeners are called on that thread. When the UI thread is held up, such as when your update() call is running, the UI does not redraw at all, and keyboard events and mouse events will “pile up” in the event queue, appearing to be ignored, until the thread is allowed to continue running (at which point all of them will be processed immediately, in order).
For this reason, lengthy operations must never execute on the UI thread. One option is to run a Task in a different thread:
Task<Boolean> updater = new Task<Boolean>() {
#Override
public Boolean call() {
updateValue(true);
update();
return false;
}
};
updateInfo.visibleProperty().bind(updater.valueProperty());
new Thread(updater).start();
You could also create a Thread from a plain Runnable:
updateInfo.setVisible(true);
Runnable updater = new Runnable() {
#Override
public void run() {
update();
Platform.runLater(() -> updateInfo.setVisible(false));
}
};
new Thread(updater).start();
Platform.runLater is required in this case, because user interface objects, including all JavaFX Nodes, may only be accessed and modified in the UI thread. If your update() method is manipulating any Nodes, it will need to use Platform.runLater to do so.
The Task class has built-in support for showing work progress in the UI, but since you haven’t included the code for your update() method, I can’t tell you whether that support would be useful in your situation.
my app plays different sounds when entering the area of different buttons. when the mouse exits the area of a button, i want all sounds to stop. i start the sounds in the mouseEntered method, and stop them in the mouseExited method:
#Override
public void mouseEntered(MouseEvent mouse) {
allForms.playSounds(mouse);
}
#Override
public void mouseExited(MouseEvent mouse) {
allForms.stopSounds(mouse);
}
the problem is the mouseEntered method won't stop it's execution in the middle to start executing mouseExited.
is there a way to solve this problem?
You appear to be calling a long-running bit of code on the Swing event thread, tying up the thread. A solution is to call the playSounds in a background thread which should free up the Swing thread so that it responds to you and will hopefully allow you to call stopSounds. A SwingWorker can help you create a background thread and allow its code interact with swing in a thread-safe manner.
As an aside, another way to trigger your code would be to listen to your JButton's ButtonModel for roll over events.
Edit
You state in comment:
is it possible to use SwingWorker without having to extend the class? my class already extends JFrame.
Your SwingWorker could be in its own class, or could be an anonymous inner class if it's small enough. If separate, it would be part of your "control" code, assuming that you've factored your code into an M-V-C pattern (or Model-View-Control).
For what it's worth, I rarely if ever create GUI code that extends a top level window such as a JFrame, and avoiding this has saved me from problems and frustration.
I have a dialog containing several buttons. When a particular button is clicked, it's ActionListener iniates a process that takes several seconds to complete. During this time I want to provide some feedback to the user. To take a simple approach, I have a label in the dialog "Computing..." which is initially not visible. A code segment looks like this
button_OpenHoursReport.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
lbl_Computing.setVisible(true);
new runAndRenderReport();
RunAndRenderReport.main(null);
lbl_Computing.setVisible(false);
}
});
The problem is, the lbl_Computing text does not become visible until the RunAndRenderReport is completed. Obviously, that's not much help to the user. Don't know where to go from here. Does this have to do with threads? If so, I could use some guidance on how to get started.
actionPerformed() is executed on the GUI-thread (EDT), so avoid executing intensive operations on it. Instead use SwingWorker.
See How SwingWorker works.
A trick which is much easier than using SwingWorker is to call paintImmediately on your label after calling setVisible(true). You should see the effects - immediately.
lbl_Computing.paintImmediately(0, 0, lbl_Computing.getWidth(), lbl_Computing.getHeight());
But SwingWorker is the way to go if you want your GUI to be responsive in other ways as well while the reporting is running.
I'm re doing a specific application, just a basic text editor and I remember I had tabs and a JMenu so if you went File --> New it would add or 'Open' another tab on the JTabbedPane. But this time it's not doing it for me, could someone help? Here is how im doing it:
newFile.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
tabs.addTab("new file", text);
}
}
);
So when it's clicked it should add another tab but it's not for some reason...
If it matters there is a default tab open at the beginning and when you click new it wipes out the old one.
Thanks for any help! (Please ask if you need anymore explanation)
Here I uploaded my code here since the editor here kept saying I the way I was putting it in wasnt formatted correctly:
http://nardcake.com/java
There is 2 files there, one initializes it and the other is everything else
thanks!
try:
tabs.revalidate();
tabs.repaint();
I have removed these two lines (those two are anyhow called in the end by addTab() method), and rewritten your init.java like this:
public static void main(String[] args) {
System.out.println(SwingUtilities.isEventDispatchThread()); // 1
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.out.println(SwingUtilities.isEventDispatchThread()); //2
EBLFWE window = new EBLFWE();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setExtendedState( window.getExtendedState()|JFrame.MAXIMIZED_BOTH );
window.setSize(1024, 728);
window.setVisible(true);
}
});
It works now. To quote myself:
Every usage of Swing components must be done thorugh the Event Dispatch Thread (abbreviated EDT) or you will probably get unwanted visual effects. See here for explanation.
EDIT:
All the GUI related code must be executed on the EDT. You can test if some part of your code is run by EDT like this:
System.out.println(SwingUtilities.isEventDispatchThread());
If it prints true you are safe to do a GUI update (e.g. call methods on Swing components instances) - like in 1 or anywhere in EBLFWE class. However 2 will print false - it is because the thread that runs your program is not EDT.
When calling SwingUtilities.invokeLater() you are actually placing that code to be executed (at some appropriate time the EDT sees fit) in the Event dispatch thread.
EDT does the actual painting, and a lot of other tasks, so when you call GUI update code from another thread you can mess up the order and get unwanted visual apperance.
I want to write a program, which search some HTML addresses. I assume that one search will be going more than 1 minute. When I print the results to console everything is OK, but when I make a frame the results don't appear in textpane. Now I have two classes one to search and one for frame. I write the simply function in the frame class to check if adding text working:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String Txt=(String)jComboBox1.getSelectedItem();
jTextPane1.setText("");
addText(Txt);
SecondClass.find(Txt);
}
public void addText(String text){
StyledDocument doc = jTextPane1.getStyledDocument();
try{
doc.insertString(doc.getLength(), text, null);
}catch(Exception e) { System.out.println(e); }
}
And in SecondClass I write the same line to addText, but it is working only from frame class. The second problem is that if the search is going I cannot do anything in program and the text from frame class is see after search is finished. I want see text immediately after finding it like in console which is working and I want to have possibility to click this link before search is ended ( I don't implement clickable links yet and don't know how to do this). I think that I must synchronize the processes, but I don't know how to do this.
It sounds like you are doing the main downloading task on the special Swing event thread, which handles all the work for Swing components. This will prevent the GUI from responding properly.
This will happen 'by accident' if you do long-running actions within event handlers (e.g. when you press a JButton).
You need to do long-running tasks in a separate thread, then 'hand over' the data to the Swing thread to update the GUI, using SwingUtilities.invokeLater(runnable); Edited: or see Max's answer about SwingWorker.
For example, your worker thread would do something like this when it has some data ready, using an anonymous Runnable:
final String text = getText();
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
appendText(text);
}
});
where getText() just indicates some means of acquiring the downloaded text, and appendtext() is a method you will need to write to update your textpane.
Check out SwingWorker. It should help you with handling lengthy operation and managing intermediate results as well. There is a sample in this manual that illustrates use of publish() and process() methods to append intermediate text results to JTextArea.