Modifying a Swing component based on PropertyChangeEvent from separate thread - java

If I understand correctly, when I modify a Swing component directly from another thread, that action should be placed on the EDT's event queue to prevent synchronization issues with the GUI:
public class SwingFrame extends JFrame {
private JTextField _textField;
public SwingFrame() {
_textField = new JTextField();
Thread thread = new Thread(new SomeRunnable(_textField));
thread.start();
}
}
public class SomeRunnable implements Runnable {
private final JTextField _textField;
public SomeRunnable(final JTextField textField) {
_textField = textField;
}
#Override
public void run() {
// _textField.setText("Goodbye, Swing!"); /* wrong */
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
_textField.setText("Hello, Swing!");
}
});
}
}
My question is, do I need to follow this same idiom when the Swing component is not modified directly within the non-EDT thread, but instead by a PropertyChangeListener executing on the EDT that receives a PropertyChangeEvent from another thread?
public class SwingFrame extends JFrame implements PropertyChangeListener {
private JTextField _textField;
public SwingFrame() {
_textField = new JTextField();
Thread thread = new Thread(new SomeRunnable());
thread.start();
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("text")) {
_textField.setText(String.valueOf(evt.getNewValue()));
}
}
}
public class SomeRunnable implements Runnable {
private final PropertyChangeSupport _propertyChangeSupport;
public SomeRunnable() {
_propertyChangeSupport = new PropertyChangeSupport(this);
}
#Override
public void run() {
// Ok? Or wrap in EventQueue.invokeLater()?
_propertyChangeSupport.firePropertyChange("text", null, "Hello, Swing!");
}
}
It doesn't look like there's anything in PropertyChangeSupport that would make it inherently "Swing safe", but I don't want to clutter up my code with unnecessary calls to EventQueue.invokeLater() if they're not required.

Only AWTEvent objects are processed from the context of the Event Dispatching Thread, all other types of events are generally raised manually (generally using a for-loop and a list of registered listeners).
This means, in the context of your example, the property change event would actually be triggered outside of the EDT. Because most Swing components assume that they are being notified within the EDT, this is indeed dangerous.
Now, you could modify any of your PropertyChangeListeners to check that they are being executed within the context of the EDT, but what you can't do is change how other registered listeners might work.
If you need to do this (I would question the reason's why), you should wrap the firePropertyChange in an invokeLater call to re-sync it back to the EDT.
Equally, you could use a SwingWorker and publish the changes so that they are processed within the EDT for you...

Related

How to keep focus on JTextField. SwingWorker

I need to keep focus on JTextField. Application uses Swing library. I need set focus on that field from time to time in order to avoid user mistakes that would change focus to other comonents. I suppose I need to use SwingWorker. Set focus is an operation on Swing
component so it should be invoked in EDT. My question is how to write SwingWorker to do that?
I know that method done() pass tasks to be invoked in EDT but I need this task to be invoked every let's sey 2 seconds. Method done() is called one time.
So maybe sth like this will be ok?
public class myWorker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//here set focus on JTextField
return null;
}
});
}}
Edit:
I noticed that method process() that is a part of SwingWorker may be appropriate beacuse it is invoked in EDT. I'm not sure but this method is probably invoked always when I call publish() metod. So could you tell me if this code is valid to do this task?
private class KeepFocusWorker extends SwingWorker<Void, Void>
{
#Override
protected Void doInBackground() throws Exception
{
while(true)
{
publish();
}
}
#Override
protected void process(List<Void> chunks)
{
codeBar.requestFocusInWindow();
}
}
Use javax.swing.Timer instead of SwingWorker. In this case actionPerformed will be executed in EDT. Also to set focus in a component, you need to call requestFocus. As the name suggests, it is a request only and not guaranteed. So you may change you approach.
Timer timer = new Timer(2000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
codeBar.requestFocus();
}
});
timer.setRepeats(true);
timer.start();
Surely it's better to limit the user's ability to take focus away from the textfield in the first place? Personally I don't see why it's an issue but I suppose it's better to keep focus in the one component rather than letting the user shift focus only for it to be shifted back every few seconds.
Therefore you could add a FocusListener to the component, override the focusLost method and basically requestFocus() again.
codeBar.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
codeBar.requestFocus();
}
});
NB I've not actually tried this myself but can't see why it wouldn't work.
Alternatively you can use an InputVerifier which always returns false to prevent focus being taken away.

Label issues with GUI

I'm fairly new to Java and I am trying to make a GUI. This is the code in my GUI.java file. It contains a button and a label. When I click the button, the label is supposed to show "loading..." and enter a static void method in the Main class (in Main.java) called searcher and does stuff. After searcher is done, label becomes "".
Problem: Label doesn't change at all when I press press the button. Seems like neither the setText in the actionListener nor searcher() works. However, the other "stuff" I wanted it to do inside searcher() still works fine. I don't see any errors.
Note: If I try to call searcher() from the main it works fine.
GUI.java:
public class GUI extends JFrame implements ActionListener{
public JButton button = new JButton("Refresh!");
public JLabel label = new JLabel("");
public GUI(){
Container pane = getContentPane();
button.addActionListener(this);
button.setActionCommand("refresh");
pane.add(button);
pane.add(label);
}
}
public void actionPerformed(ActionEvent e) {
if ("refresh".equals(e.getActionCommand())) {
label.setText("Loading...");
Main.searcher(this, "", "");
label.setText("");
}
}
Main.java:
public class Main{
public static void searcher(GUI gu, String popup, String url) {
gu.label.setText("Loading...");
//do stuff
gu.label.setText("");
}
public static void main(String[] args) {
GUI gu = new GUI ();
}
}
EDIT: I've changed to code to use SwingWorker and propertylistener as suggested, but I'm having trouble now. Firstly, 'this' no longer refers to the GUI.. what should I pass in the searcher method to pass the current instance of class GUI?
I'm also getting this error and I'm not really sure how to fix it:
.\GUI.java:77: error: is not abstract and does not override abstract method propertyChange(PropertyChangeEvent) in PropertyChangeListener
PropertyChangeListener propChangeListn = new PropertyChangeListener() {^
public void actionPerformed(ActionEvent e) {
if ("refresh".equals(e.getActionCommand())) {
label.setText("Loading...");
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
public Void doInBackground() throws Exception {
Main.searcher(this, "", "http://maple.fm/api/2/search?server=0");
return null;
}
};
//worker.addPropertyChangeListener(new propertyChangeListener listener) {
PropertyChangeListener propChangeListn = new PropertyChangeListener() {
public void propertyChanged(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
label.setText("");
}
}
};
worker.addPropertyChangeListener(propChangeListn);
worker.execute();
}
Yours is a classic Swing threading issue where you are tying the Swing event thread with a long-running process, preventing this thread from updating the GUI's graphics or from interacting with the user. The solution is the same as always -- use a background thread to do your long-running processing. If you used a SwingWorker for this, you could even add a PropertyChangeListener to it and then be notified when the worker has completed its task, allowing you to update the GUI with this information.
Google Concurrency in Swing and click on the first hit for more on this.
e.g.,
public void actionPerformed(ActionEvent e) {
if ("refresh".equals(e.getActionCommand())) {
label.setText("Loading...");
// create a SwingWorker object
final SwingWorker<Void, Void> worker = new Swingworker<Void, Void>() {
// long running code would go in doInBackground
public Void doInBackground() throws Exception {
Main.searcher(...);
return null;
}
}
// add a listener to worker to be notified when it is done
worker.addPropertyChangeListener(PropertyChangeListener listener) {
public void propertyChanged(PropertyChangeEvent pcEvt) {
// if the worker is done...
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
label.setText("");
// you will probably want to call get() on your worker here
// and catch any exceptions that may have occurred.
}
}
}
// it may seem counter-intuitive, but you need to start the worker with
// execute() *after* setting all the above code up.
worker.execute();
}
}

Trouble with SwingUtilities.invokeLater() updating GUI

I have been trying this all day in different variations with little success. Could someone please help explain what I am doing wrong? I am just a beginner with regards to threads.
private JTextArea text = new JTextArea();
private JButton button = new JButton("Cancel");
public StatusFrame() {
text.setEditable(false);
this.add(text);
this.add(button, BorderLayout.EAST);
this.setSize(new Dimension(150, 100));
this.setVisible(true);
}
public void updateStatus(String textIn) {
text.setText(textIn);
}
public JButton getButton() {
return button;
}
In another class, I am calling methods which may take a while to complete. I want to be able to call the StatusFrame.updateStatus() method to keep the user informed on the progress.
This is what I have:
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
//do something
status.update("process 1 completed");
//do something else
status.updateStatus("Process 2 completed");
}
The frame appears but none of the code after the runnable appears to be run/processed. It just stops/blocks/something. But the GUI remains active
Thanks for any advice.
P.S.: I have tried using invokeAndWait() method but again not sure if I am doing it the right way. For now a quick fix would be preferred as I have not learned much about threads yet. Any instructions are welcome.
You have the concepts backwards.
Here's your code
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
//do something
status.update("process 1 completed");
//do something else
status.updateStatus("Process 2 completed");
You should execute the long running code in a thread, and use the SwingUtilities invokeLater method to update the GUI.
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
new Thread(r).start();
// inside the StatusFrame
//do something
SwingUtilities.invokeLater(new Runnable() {
public void run() {
update("process 1 completed");
}
);
//do something else sometime later
SwingUtilities.invokeLater(new Runnable() {
public void run() {
update("Process 2 completed");
}
);
I don't know if I was clear in my answer.
Execute SwingUtilities.invokeLater when you start your Java application to make sure Swing components are on the Event Dispatch thread (EDT).
From the EDT, invoke long running processes as a runnable thread.
In the runnable thread, since you're not on the EDT, execute SwingUtilities.invokeLater whenever you're updating Swing components. This ensures that Swing components are updated on the EDT.
Every Swing application should start with a class like this:
import javax.swing.SwingUtilities;
import com.ggl.text.entry.model.TextEntryModel;
import com.ggl.text.entry.view.TextEntryFrame;
public class TextEntry implements Runnable {
#Override
public void run() {
new TextEntryFrame(new TextEntryModel());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new TextEntry());
}
}
This class does 3 things.
Constructs the GUI data model.
Constructs the GUI JFrame.
Ensures that the Swing components are on the EDT.
You'll need to call the updates on EDT too. I would suggest to sleep on the main thread, to give GUI a chance to show up before any other work:
someMethod() {
// prevent GUI from freezing using threads
Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
//do something
r = new Runnable() {
public void run() {
status.update("process 1 completed");
}
};
SwingUtilities.invokeLater(r);
//do something else
r = new Runnable() {
public void run() {
status.update("Process 2 completed");
}
};
SwingUtilities.invokeLater(r);
}
See Concurrency in Swing.
You may find using a Swing Worker easier to work with since it uses a Thread and has methods that will allow you to update the GUI properly.

update jtextpane with java swing

I'm making an application with java swing. In a button of the application I need to every x minutes to make something.
I think that I must do it with a new thread, but I have two problems. The first is that I must to pass a parameter to these thread. I solved it with a class that extends of a Thread and a constructor. I think these way is correct no?
The second thing I cannot resolve it is that I need to update a jtextpane while the thread is running but if I try to update the JTextPane propierties Eclipse says me that cannot be resolved. I think that the problem is that these thread is not the main thread. But... there is some way to fix it?
Many thanks and sorry for my english!
The code is:
btnIniciar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//String file = "D:\\prueba.torrent";
// while (true) {
Hilo ejecutar = new Hilo(listaBuscar);
ejecutar.run();
public class Hilo extends Thread {
public Hilo(List<String> aBuscar){
}
public void run(){
System.out.println("Trabajo por hacer dentro de MiHilo");
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
lblNewLabel.setText("hola");
}
});
}
}
It says me lblNewLabel cannot be resolved.
Any help?
Thanks
I'm trying with these code now and doesnt works:
public class Hilo implements Runnable {
private JLabel etiqueta;
public Hilo (List <String> aBuscar, JLabel label){
System.out.println("Hemos entrado en el puto hilo");
etiqueta = label;
}
#Override
public void run() {
etiqueta.setText("hola");
System.out.println("vamos a coneseguirlo");
// TODO Auto-generated method stub
SwingUtilities.invokeLater(new Runnable() {
public void run() {
etiqueta.setText("hola");
System.out.println("vamos a coneseguirlo");
}
});
}
}
Use Swing timer. It is very much like invisible button that is pressed periodically in the given intervals. It will call your actionPerformed already in a Swing thread from where you can manipulate components (same as from the JButton ActionListener). Hence most likely you do not need to run your own threads for this task.
You mention JTextPane in your question title but only refer to JLabel?
The main problem though you are having I see is that you have not declared the JLabel within the scope of your Thread, you could pass your JLabel instance which has a method to get a reference to the JLabel to your Thread via a constructor thus it has a reference to the JLabel, right now it doesnt.
Also I'd recommend using SwingUtilities and not EventQueue
And do not extend Thread class (unless adding custom functionality) rather implement a Runnable
Something like:
GUI.java:
public class GUI {
private JFrame frame;
private JLabel label;
private JButton btnIniciar;
public void getJLabel() {
return label;
}
public void initComponents() {
//create UI and components here
btnIniciar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//String file = "D:\\prueba.torrent";
Hilo ejecutar = new Hilo(listaBuscar,Gui.this);//pass reference of to our class
}
}
}
Hilo.java:
public class Hilo implements Runnable {
private Thread t;
private final GUI gui;
public Hilo(List<String> aBuscar, GUI ui){
this.gui=ui;
t=new Thread(this);
startThread();
}
#Override
public void run(){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gui.getJLabel().setText("hola");
}
});
}
//so we can start thread from other class
public void startThread() {
if(!t.isAlive()) //if the thread is not started alreade
t.start();
}
}
Though depending on what you are doing a Swing Timer might be what you need, it will allow you to run code, at intervals/delays and all this is done on the EDT already.

Update JProgressBar from new Thread

How can I update the JProgressBar.setValue(int) from another thread?
My secondary goal is do it in the least amount of classes possible.
Here is the code I have right now:
// Part of the main class....
pp.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
new Thread(new Task(sd.getValue())).start();
}
});
public class Task implements Runnable {
int val;
public Task(int value){
this.val = value;
}
#Override
public void run() {
for (int i = 0; i <= value; i++){ // Progressively increment variable i
pbar.setValue(i); // Set value
pbar.repaint(); // Refresh graphics
try{Thread.sleep(50);} // Sleep 50 milliseconds
catch (InterruptedException err){}
}
}
}
pp is a JButton and starts the new thread when the JButton is clicked.
pbar is the JProgressBar object from the Main class.
How can I update its value?(progress)
The code above in run() cannot see the pbar.
Always obey swing's rule
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
What you can do is to create an observer that will update your progress bar -such as
- in this instance you want to show progress of data being loaded on click of a button.
DemoHelper class implements Observable and sends updates to all observers on when certain percent of data is loaded.
Progress bar is updated via public void update(Observable o, Object arg) {
class PopulateAction implements ActionListener, Observer {
JTable tableToRefresh;
JProgressBar progressBar;
JButton sourceButton;
DemoHelper helper;
public PopulateAction(JTable tableToRefresh, JProgressBar progressBarToUpdate) {
this.tableToRefresh = tableToRefresh;
this.progressBar = progressBarToUpdate;
}
public void actionPerformed(ActionEvent e) {
helper = DemoHelper.getDemoHelper();
helper.addObserver(this);
sourceButton = ((JButton) e.getSource());
sourceButton.setEnabled(false);
helper.insertData();
}
public void update(Observable o, Object arg) {
progressBar.setValue(helper.getPercentage());
}
}
Shameless plug: this is from source from my demo project
Feel free to browse for more details.
You shouldn't do any Swing stuff outside of the event dispatch thread. To access this, you need to create a Runnable with your code in run, and then pass that off to SwingUtilities.invokeNow() or SwingUtilities.invokeLater(). The problem is that we need a delay in your JProgressBar checking to avoid jamming up the Swing thread. To do this, we'll need a Timer which will call invokeNow or later in its own Runnable. Have a look at http://www.javapractices.com/topic/TopicAction.do?Id=160 for more details.
There is need not to call pbra.repaint explicitly.
Update JProgressBar shall be done through GUI dispatch thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Remember to make pbar final variable.
pbar.setValue(i);
}
});

Categories