I currently have a JTable that is populated with a series of data that forms the basis of a import screen. When I have finished selecting which updates I want or do not want, I press on the Apply button and the updates are applied successfully but the JTable does not fully update.
This is the code for the method that deals with applying the changes:
private void doProcessChanges() {
ChangeProcessor cp = new ChangeProcessor();
final List<Integer> rowsToRemove = new ArrayList<Integer>();
BeanTableModel<UpdateModel> model = (BeanTableModel<UpdateModel>) table.getModel();
for (int i=0; i<model.getRowCount(); i++) {
UpdateRow ur = mode.getObject(i);
if (ur.isAccepted() <> ChangeAcceptance.NO_ACTION) {
cp.processChange(ur);
rowsToRemove.add(i);
}
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
for (int row : rowsToRemove) {
model.removeObject(row);
model.fireTableDataChanged();
}
}
);
}
The method is called from within a SwingWorker thread as below:
SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
#Override
protected Object doInBackground() throws Exception {
doProcessChanges();
return null;
}
#Override
protected void done() {
try {
get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
I do not get any exceptions from executing this so I am I doing anything wrong?
Thanks in advance.
Your fragment shows incorrect synchronization. In particular, you access BeanTableModel, a subclass of AbstractTableModel, from the background thread. Instead, pass the List<Integer> rowsToRemove to your worker in its constructor.
Addendum: Instead of invokeLater(), you can update the TableModel in your implementation of process(), which executes on the EDT. Also, you shouldn't have to fireTableDataChanged(), which "Notifies all listeners that all cell values in the table's rows may have changed." The removeObject() implementation should fire the least pervasive event required to effect the change.
Related
I have the following method:
public Object someMethod(Object param) {
return performLongCalculations();
}
Some time consuming calculations I placed in a separate method:
private Object performLongCalculations() {
...
}
The problem is that it returns some calculation result. These calculations are performed in the EDT and lead to freezing the UI.
I tried to solve it with the following way:
public Object someMethod(final Object param) {
Object resultObject = new Object();
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<Object> future = executorService.submit(new Callable<Object>() {
#Override
public Object call() {
return performLongCalculations(param);
}
});
executorService.shutdown();
try {
resultObject = future.get();
} catch (InterruptedException | ExecutionException e) {
// ...
}
return resultObject;
}
But the thread is blocked on the call to future.get(); until the calculations are completed. And I think it also runs in EDT.
Next I tried to use SwingWorker:
public Object someMethod(final Object param) {
SwingWorker<Object, Void> worker = new SwingWorker<Object, Void>() {
#Override
protected Object doInBackground() {
return performLongCalculations(param);
}
#Override
protected void done() {
try {
get();
} catch (InterruptedException e) {
}
catch (ExecutionException e) {
}
}
};
worker.execute();
// what should I return here?
}
Here I need to return the result, but it returns before the end of the thread that runs in parallel with EDT.
Your question essentially is:
How can I return a value directly into my Swing GUI from a method where the solution is obtained from long-running code called within a background thread?
And the answer, succinctly, is: you don't.
Trying to do this in any way, shape or fashion would mean forcing the background thread to block the GUI event thread until the background thread has completed its task, and if the task takes any appreciable time, then this will always cause the GUI to freeze. Instead, you must extract the information when the background thread has completed, and not get the result from the method itself. This is usually done using a call-back mechanism of some sort.
For example, in this code:
public void someMethod(final Object param) {
SwingWorker<Object, Void> worker = new SwingWorker<Object, Void>() {
#Override
protected Object doInBackground() {
return performLongCalculations(param);
}
#Override
protected void done() {
try {
Object something = get();
// (A)
} catch (InterruptedException e) {
// do handle these exceptions!
}
catch (ExecutionException e) {
// do handle these exceptions!
}
}
};
worker.execute();
// (B)
}
You would give the result to the GUI at location (A) not as a return value from the method, location (B)
Alternatively, you could attach a PropertyChangeListener to the SwingWorker, listen for when Worker's state property changes to SwingWorker.StateValue.DONE, and then call .get() on the worker and push the value returned onto the GUI. This is my preferred way of doing this because it usually allows for lower code coupling.
I have a table view that lists user's friends and I need to update it every 5 seconds with data that I retrieve from database.
This is the code I use:
Main.java
private List<Friend> userFriends;
fx controller:
ObservableList<FriendWrapper> friendList = FXCollections.observableList(
new ArrayList<FriendWrapper>());
private void updateFriendList() {
new Thread(new Runnable() {
public void run() {
while (Params.loggedUser != null) {
Main.setUserFriends(Params.dao.listUserFriends(Params.loggedUser));
friendList.clear();
for (Friend friend : Main.getUserFriends()) {
friendList.add(new FriendWrapper(friend.getFriendName(), friend.getOnline(), friend.getFriendId(), friend.getWelcomeMessage()));
}
Params.dao.updateOnlineStatus(Params.loggedUser, 3);
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "updateFriendList").start();
}
Friend is database model. FriendWrapper is object used for table rows.
however I get IllegalStateException: Not on FX application thread on line friendList.clear();
How can I change the items of TableView from a thread running in the background?
Instead of a quick Platform.runLater() hack, you should probably make use of the Task class:
protected class LoadFriendsTask extends Task<List<FriendWrapper>>
{
#Override
protected List<FriendWrapper> call() throws Exception {
List<Friend> database = new ArrayList<>(); //TODO fetch from DB
List<FriendWrapper> result = new ArrayList<>();
//TODO fill from database in result
return result;
}
#Override
protected void succeeded() {
getTableView().getItems().setAll(getValue());
}
}
You can launch this one as a Thread, for example:
new Thread(new LoadFriendsTask()).start()
For further reference:
JavaFX - Background Thread for SQL Query
How can I do asynchrous database in JavaFX
Multithreading in JavaFX
Use this...
Platform.runLater(new Runnable() {
#Override
public void run() {
}
});
Im trying to display series of photos on one page with time interval. In countinuos while loop i got:
while(true){
if (zmienna == fa.length) zmienna = 0;
Image obrazek = new Image("",pliki[zmienna]);
layout.replaceComponent(staryObrazek, obrazek);
obrazek.requestRepaint();
staryObrazek = obrazek;
zmienna++;
try {
Thread.sleep(2000) ;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
It showing only waiting icon, dispaying mwthod works fine without loop. Do anyone has an idea how I should fic this issue?
In all modern UI systems you will have to not suspend the main thread, but use a background thread to update the UI. Otherwise you block the whole UI.
In Vaadin 7 you can enable server push and then use a background thread to change the image every 2 seconds.
Enabling push is described in the book of vaadin https://vaadin.com/de/book/vaadin7/-/page/advanced.push.html
Your code could look like this:
public class PushyUI extends UI {
#Override
protected void init(VaadinRequest request) {
// Set first component/image
setContent(chart);
// Start the update thread
new ImgUpdThread().start();
}
class ImgUpdThread extends Thread {
#Override
public void run()
{
// Update the data for a while
while (count < 100) {
Thread.sleep(1000);
access(new Runnable() {
#Override
public void run() {
//update the UI as in your code above
}
});
}
}
It is important to use the access(...) method to sync access to the UI elements.
I want JList to be populated with multiple threads.
I tried this way but jlist is empty.
It would be good if jlist was updated on the fly
There are two threads, the other one loads in anouther direction
new Thread(new Runnable() {
#Override
public void run() {
for(i=0; i<cells.size()/2; i++){
System.out.println("thread");
try{
HtmlPage p = client.getPage("https://tbilisi.embassytools.com/en/slotsReserve?slot="+cells.get(i).getAttribute("data-slotid"));
pages.add(p);
if(!p.getUrl().toString().contains("slotsReserve"))
model.add(i,p.getUrl().toString());
}
catch (Exception e){
e.printStackTrace();
}
}
}
});
list1.setModel(model)
Thanks in advance
UPDATE*
So I fixed by using SwingWorker
Swing is a single threaded framework, that is, it is expected that all updates and modifications to the UI are done from within the context of the Event Dispatching Thread.
Equally, you should do nothing in the EDT that might block or otherwise prevent it from processing the Event Queue (like downloading content from the web).
This raise a conundrum. Can't update the UI outside the EDT, need to use some kind of background process to execute time consuming/blocking tasks...
So long as the order of items is unimportant, you would use multiple SwingWorkers in place o of the Threads, for example...
DefaultListModel model = new DefaultListModel();
/*...*/
LoadWorker worker = new LoadWorker(model);
worker.execute();
/*...*/
public class LoaderWorker extends SwingWorker<List<URL>, String> {
private DefaultListModel model;
public LoaderWorker(DefaultListModel model) {
this.model = model;
}
protected void process(List<String> pages) {
for (String page : pages) {
model.add(page);
}
}
protected List<URL> doInBackground() throws Exception {
List<URL> urls = new ArrayList<URL>(25);
for(i=0; i<cells.size()/2; i++){
try{
HtmlPage p = client.getPage("https://tbilisi.embassytools.com/en/slotsReserve?slot="+cells.get(i).getAttribute("data-slotid"));
pages.add(p);
if(!p.getUrl().toString().contains("slotsReserve")) {
publish(p.getUrl().toString());
urls.add(p.getUrl());
}
}
catch (Exception e){
e.printStackTrace();
}
}
return urls;
}
}
This allows you execute your blocking/long running in the backround (doInBackground) and publish the results of this method which are then processed within the context of the EDT...
See Concurrency in Swing for more details
Swing is not thread safe you should use SwingUtilities to run multiple threads updating swing.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
doWhateverYouWant();
}
});
read more
I have a button click event that will fire a swing worker thread which in return fire another thread to do a long calculation including writing a file. Then this file is read to draw some graphics. However drawing part never happens if i don't add a delay in between.. (It says file not found although the file is there..What is the better way to fix this without adding a delay..
private void buttonFragmentActionPerformed(java.awt.event.ActionEvent evt) {
try
{
ESIPlusFragmenterWorker epfw = new ESIPlusFragmenterWorker(10, sdfFile, cidSpectrum);
epfw.execute();
Thread.sleep(1000);
holder.molTable1.drawMolViewPanel(currDir+sep+"esiFragments"+sep+"esiFrag.sdf");
}
catch (Exception e)
{
e.printStackTrace();
}
}
Swing Worker
public class ESIPlusFragmenterWorker extends SwingWorker<Void, Void>{
int mzppm_;
String SDF_;
String spectrum_;
Double mion_;
MolTable holder_;
ESIPlusFragmenterWorker(int mzppm, String SDF, String spectrum)
{
mzppm_ = mzppm;
SDF_ = SDF;
spectrum_ = spectrum;
}
#Override
protected Void doInBackground() {
try
{
Molecule mol;
MolImporter importer = new MolImporter(SDF_);
ExecutorService te = Executors.newFixedThreadPool(1);
while ((mol = importer.read()) != null)
{
Runnable epf = new ESIPlusFragmenter(mol, spectrum_, mzppm_);
Thread t = new Thread(epf);
te.execute(epf);
}
importer.close();
te.awaitTermination(10, TimeUnit.MINUTES);
}
catch (Exception e)
{
//
}
finally
{
return null;
}
}
#Override
protected void done() {
try {
//
} catch (Exception e) {
//e.printStackTrace();
}
}
}
Never, never, never call Thread.sleep(...) on the EDT as this will put your entire GUI to sleep. And besides, what if you estimate wrong, and the background process takes longer than your sleep delay time?
One possible solution is to add a PropertyChangeListener to the SwingWorker and listen on the "state" property for the SwingWorker.StateValue to be SwingWorker.StateValue.DONE, then do your drawing.
e.g.
private void buttonFragmentActionPerformed(java.awt.event.ActionEvent evt) {
try {
ESIPlusFragmenterWorker epfw = new ESIPlusFragmenterWorker(10,
sdfFile, cidSpectrum);
epfw.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if ("state".equals(pcEvt.getPropertyName())) {
if (pcEvt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
holder.molTable1.drawMolViewPanel(currDir + sep
+ "esiFragments" + sep + "esiFrag.sdf");
}
}
}
});
epfw.execute();
So what this does is waits until the SwingWorker has completed its business before calling the code inside of the listener.
Another option is to call your holder.molTable1.drawMolViewPanel inside of the SwingWorker's done() method, and this will work too, but by doing it as noted above with a PropertyChangeListener, the SwingWorker doesn't have to have any knowledge about the code called in the listener (as opposed to using SwingWorker's done() method), and this may allow for looser coupling.