I use SwingWorkers to fill several JTables with data from a mysql-database and use following code:
jT.setModel(DbUtils.resultSetToTableModel(results))
This works great. But right after each JTable-Model is set I need to execute different methods (methods may differ for each JTable) which e.g. put the row-count of a JTable into a JLabel or calculate sums of certain columns and put that sum into another JLabel...
My Problem:
I want the GUI to stay responsive (therefore the use of SwingWorkers to get the Data in the background), but I need to find a way to somehow "listen" to changes of the jTable (so I can execute the methods AFTER the Table is refreshed). I can't use the obvious solution to make different SwingWorker-classes for each JTable (which include the respective method of that JTable), because I need a general solution.
I already thought about putting each individual method into a TableModelListener and to add that to each respective JTable, so I tested following:
jT.getModel().addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
System.out.println("TableModelEvent: "+e);
// Probably I could add this jTable's method right here?
}
});
But I never get any Output...
I also tested the swingworker's .get()-method to wait for the set of the model (see first line of code) and then execute the method, but that lead to the same situation where I started from (GUI freezes and waits for execution of the swingworker... so no gain by using a swing worker..).
Any ideas?
The TableModelListener should work, and why might be the subject of another question, where you post your minimal example program, but one thing that likely will work in lieu of the TableModelListener is to use a PropertyChangeListener, add it to your SwingWorker before calling execute, and listen for changes to the SwingWorker state property. The new value you want to listener for is SwingWorker.StateValue.DONE.
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state")) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// TODO: code that you want to call when SwingWorker is done
// **including** calling get() on the SwingWorker itself
// so you can trap and respond to exceptions thrown during its run.
}
}
}
});
mySwingWorker.execute();
Related
i am creating a GUI application and in the background I want to run an additional task. I will paste some code, to prevent pasting a mess of code that was generated by Swing, I will leave some parts out, assume that the window.java is working as intended.
window.java:
public class window {
frame = new JFrame();
JLabel lbl1 = new JLabel("Start Counter");
frame.add(lbl1);
Thread counter = new Thread(new counter());
counter.start();
}
counter.java
public class regCheck extends window implements Runnable
{
public void run()
{
int i = 0;
while (true)
{
window.lbl1.setText(i);
try {Thread.sleep(1000);}
catch (InterruptedException e) {e.printStackTrace();}
i++;
}
}
}
what I want this example to do is create a label within a window and count upwards until the program is closed.
The easy answer here is to say "pass in the Jlabel" however in reality I have multiple things that I need to change not just a label.
the line "window.lbl1.setText(i);" does not work here, it is just to illustrate what I want to achieve.
Use the MVC pattern. Create a model that has the counter with a setValue() method that fires a listener notification. You can extend java.util.Observable to make that easier to do. Add a getValue() method to retrieve the new count. Make the setter and getter synchronized for thread safety.
Now your thread can be passed an instance of the model and call setValue() to update the value in its run() method.
Finally, your view can be passed the same instance of the model and add a listener to it. To make it easier your view can implement java.util.Observer. In the listener update() callback within the view, call the model's getValue() and use the return as the argument to setText(). Since the listener update is not being invoked from the AWT event dispatcher thread, you have to call setText() using javax.swing.SwingUtilities.invokeLater() in order to meet the thread safety requirements of Swing.
it is actually architectural question, you can pass any arguments to other thread and this thread of course could modify different labels, but I prefer another variant:
you have window, which has its objects/controls and can manipulate them
you have separate thread which increase counter
this separate thread should notify main window object about changes and window object should change it's controls accordingly, for example change text in one or several controls
one simple variant is to have interface, like ICounterHandler with one method
void onCounterChanged(int newCounterValue);
counter thread should accept ICounterHandler in constructor, save it and call this method when needed, preferable asynchronous
of course there are many other variants, but you can start with this one
I have two basic questions.
I have a GUI project with Java Swing. When I put buttons on the frame and I double clicked them, I had the code of the actionPerformed, but it is blocked.
How can I put there a button and then use it on a actionListener?
My Project is about Server-client (multithread and sockets)
I call one method to reiceve one string that we can write on a JtextField and it stays on a while cicle with PrintWriter and a getOutputStream.
Something like:
do{
...
}while(thisstring!=null || thisstring!="exit")
So.. when I write something and press the button to send it, it stays on the cicle and the button blocks. How can I unblock the button to write something else?
Edit:
I understood the EDT problem, but I can't solve it.
I tried use the Timer but without success, something like that:
int delay = 1000; //milliseconds
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
//My action calling the Thread class with the while cicle that has the PrintWriter
}
};
new Timer(delay, listener).start();
How can I handle this to do the timer when I press the button?
How can I stay on the that cicle (read the comment line) to send the information by OutputStream every time that one user enter something on the text field?
I know that for example for a console application I use a BufferedReader and then I use the ReadLine() to wait for anything sent from the console, but with GUI interface it freezes all time..
There is a fundamental concept in Java GUI development surrounding which thread in which the developer implements user-interaction processing such as button clicks.
In short, you need to perform your processing outside of the thread that calls your action handling method. This single thread is known as the Event Dispatch Thread (EDT), and if you have logic that runs much more than a few milliseconds, it will prevent the UI from continuing to draw things like the button releasing, etc.
You'll want to move your long-running, socket code off the EDT. Doing so will allow the button to release and let the user interact with other controls (or even the same button).
To avoid duplicating other discussions on the topic, I direct you to this pretty good one. Additionally, this article gives a short overview of threading concepts in Swing.
Regards,
ScottH
According to your comment you have some naming issues there. You need a class that implements the ActionListener-interface like so: class YourListenerClass implements ActionListener, but you could also do that via an anonymous class like new ActionListener {
public void actionPerformed(ActionEvent e) {
//your code for your button here
}
});
when you set your ActionListener.
The crucial thing is that you need to name your method the correct way. It MUST be public void actionPerformed(ActionEvent e) and you definitely have to implement the ActionListener-interface.
The next thing is that you have to register your listener in your button like:
yourButton.addActionListener(new YourListenerClass);
or
insert an anonymous class like I showed to you before.
The 2nd thing sounds like an multithreading issue like I mentioned in my comment. I didnt follow scotth's link, but according to his description this might be a source you want to read to solve any further blocking issues.
EDIT:
Well, at first I didn't want to explain it, because it's quite a chunk of code, but as the problem persists I want to add something about SwingWorkers in my answer.
If you have long running code, it wont help to use a Timer as the code invoked by it will also be on the EDT as it's triggered by an event.
Instead of that you could use a SwingWorker to solve this. This needs some extra code, though.
Here's a simple approach you could follow:
public class WorkingHard{
SwingWorker<String, String> worker;
JButton yourButton = ...;
...
//do some cool stuff, as register those listeners!
...
public void actionPerformed(ActionEvent evt){
if(evt.getSource().equals(yourButton);
// Construct a new SwingWorker
worker = new SwingWorker<String, Void>(){
#Override
protected String doInBackground(){
//do your reading in this method, it will be executed in an own thread
String readText = "i will be read".
/*your reading algorithm, you could also call publish(...) to post your results,
e.g. likewise), then you also have to override process(...). this process will be
thread save, too*/
readText += ... ;
...
return readText;
}
#Override
protected void done(){
try {
//do sth. with your result, now thread safe.
someGuiElement.setText(get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
// Execute the SwingWorker; the GUI will not freeze
worker.execute();
}
}
If you want to know more about those workers... there several threads dealing about it, e.g. this one.
Completely new to Java and I'm at a complete brick wall.
I have a JTextArea on my system that I'd like to have a live update, so when something is added to table2 (in my database), my server pulls the new values from the database and then updates the JTextArea.
I have absolutely no idea how to do this, though I have worked out that I need to use Thread to get it to work.
Any/all help is greatly appreciated (I'm a little pressed for time with this)
What you can do is have your thread poll your database at given periods of time, or else, have the process which is updating the database fire some kind of event which your GUI class can pick up.
Once this happens, you can then use the SwingUtilities.invokeLater() to update your JTextArea. Something like this should do:
if (eventIsFired)
{
final String jtextAreaText = ...
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
jTextArea.setText(jTextAreaText);
}
});
}
The assumption is that jTextArea is your actual JTextArea which is declared as a global variable. jTextAreaText will need to be declared final so that it can be accessed through the inner class.
Some time ago I asked this question. All solutions are workarounds.
Now this can't be. I feel that something is wrong here, but I can't tell if it is Swing's MVC model that is conceptually wrong, or if it is my thinking that is conceptually wrong.
Here is the problem again. I am using a JList to implement a list of thumbnails for the pages of a document. If the user selects another thumbnail from the list, that page is loaded. To do this I added a ListSelectionListener to the JList, which when the selection changes, it loads that page. But the user can also change the page using another control. Naturally, I want this to be reflected in the thumbnail list by having that page selected here. So I setSelectedIndex() to update the JList. Unfortunately this has the unwanted effect of raising a ListSelectionEvent which causes the listener to reload the page.
Now what is wrong here? I just changed the model from somewhere else, so naturally I want the view to update itself, but I don't want it to trigger events. Is Swing not implementing MVC right? Or am I missing a point here?
This is an issue that a lot of us Swing programmers have to face: multiple controls modifying the same data, with the update then reflected in each control. At some point, something has to have the ultimate veto on what updates will be applied to the model: whatever that something is needs to be able to handle multiple (potentially redundant or even contradictory) updates and decide what to do with them. This could happen in the model layer, but ideally it should be the controller that does this - this piece, after all, is most likely where the business logic resides.
The problem with Swing, in this regard, is that the controller piece of MVC is often split somewhat between the view component and the model so it can be difficult to have that logic centralised. To some extent the Action interface rectifies this for actionPerformed() events by putting the logic in one place and allowing it to be shared by different components, but this doesn't help for the other types of event, or when there are multiple different classes of event that need to be coordinated.
The answer, then, is to follow a pattern that's hinted at in Swing but not made explicit: only perform the requested update if the state will actually change, otherwise do nothing. An example of this is in the JList itself: if you attempt to set the selected index of a JList to the same index that's already selected nothing will happen. No events will be fired, no updates will occur: the update request is effectively ignored. This is a good thing. This means that you can, for example, have a listener on your JList that will respond to a newly selected item, and then in turn ask the same JList to reselect that same item and you won't get stuck in a pathologically recursive loop. If all the model-controllers in an application do this then there's no problem with multiple, repeated events firing off all over the place - each component will only update itself (and subsequently fire off events) if it needs to, and if it does update then it can fire off all the update events it wants, but only those components that haven't already got the message will do anything about it.
This is expected behaviour.
From Model-View-Controller [Wikipedia]:
In event-driven systems, the model
notifies observers (usually views)
when the information changes so that
they can react.
So, when you call setSelectedIndex on the JList, you are updating its model, which then notifies each ListSelectionListener. It wouldn't be MVC if you could "silently" update a model without letting anyone know.
Swing is not exactly MVC, but has it's roots in MVC ( the difference lays in the fact, the view and the controller in Swing are more closely related than in other MVC see Swing architecture for more details ).
But this may not seem to be the problem you're facing. It sounds like you have to validate in the event listeners, for the type of event and decide whether to ignore it or not: If the event was originated in the list do change it. If was triggered by some other control, do not.
What #dogbane said.
But to fix the problem you need to add some sort of a state check during the listener to see if the event is one you should ignore. The ListSelectionEvent has a getValueAdjusting() method, but that is mostly internal. What you need to do is simulate it yourself.
So for example when you update the list from an external selection you would have code like...
try {
setSelectionAdjusting(true);
/* ... your old update code ... */
} finally {
setSelectionAdjusting(false);
}
and in the update code for the ListSelectionListenerEvent
public void valueChanged(ListSelectionEvent e) {
if (!isSelectionAdjusting()) {
/* ... do what you did before ...*/
}
}
Scoping and access issues are left as an exercise for the reader. You would have to write the setSelectionAdjusting, and possibly set it on other objects as well.
I still feel like there is something conceptually wrong here.
I empathize, but it may help to consider that you don't have a simple JList observing a ListSelectionModel. Instead, you have a JList and some-other-control observing a hybrid selection model. In #Taisin's example, the hybrid is a CustomSelectionModel that extends DefaultListSelectionModel and allows silent changes. When compatible, it's also possible to share a model, as suggested in this question & answer and this SharedModelDemo from the tutorial.
For reference, this thread cites the article Java SE Application Design With MVC: Issues With Application Design, which addresses the issue in more detail.
I always do like this:
public class MyDialog extends JDialog {
private boolean silentGUIChange = false;
public void updateGUI {
try {
silenGUIChange = true;
// DO GUI-Updates here:
textField.setText("...");
checkBox.setSelected (...);
}
finally {
silentGUIChange = false;
}
}
private void addListeners () {
checkBox.addChangeListener (new ChangeListener () {
public void stateChanged (ChangeEvent e) {
if (silentGUIChange)
return;
// update MODEL
model.setValue(checkBox.isSelected());
}
});
}
}
Typically the way a listener works is it will "go off" every time the event that it is waiting for occurs. If I had to speculate it is a misunderstanding from your side of things.
In my code, two comboboxes are added to actionListener( this );
In another part of my code, I call a combobox function that sets an index to a certain value. This in turn calls actionPerfoemed again and so getSource == comboBox is true. Every time I call a set function it calls actionPerformed again, creating a stack of function calls that then unwinds down to the first.
Is there a way to prevent this?
If the problem is just the initial setting, you can defer adding the listener until after both have been initialized. There's more discussion here.
From the Swing tutorial,
Combo boxes also generate item events, which are fired when any of the items' selection state changes.
These events will be generated either when a user clicks on the items with the mouse, or when your software calls setSelectedIndex().
Perhaps you don't want your actionPerformed() method in this to be called when your software calls setSelectedIndex(). You may need a Boolean eventInitiatedBySoftware. In your main (this) class, you could say
synchronized(eventInitiatedBySoftware) {
eventInitiatedBySoftware=true;
comboboxeditor.setSelectedIndex(n);
}
and then in your listener:
public void actionPerformed(ActionEvent ae) {
synchronized(eventInitiatedBySoftware) {
if (eventInitiatedBySoftware) {
eventInitiatedBySoftware=false; // clear your flag.
return; // don't want to process this event.
}
// the rest of your method goes here
}
When your software wants to adjust the value, it will set the Boolean to true. The actionPerformed method will be called, but your test will realise that this event was initiated by the software, and return before doing any of your existing code. It will clear the Boolean, so that if a user now uses the mouse to perform a selection action, your code will realise that it wasn't softwareInitiated.
BTW, It's possible that you misunderstand the event concept. For example, I suspect you are actually adding "this" as an event listener for each combobox, rather than adding comboboxes as listeners to "this". You might like to look at the Writing Event Listeners trail.