Update JTable with Separate Thread - java

The purpose of the application is to query a table, and take that information and update a JTable. Right now the ThreadTask() is able to query the table and obtain the information. My question is how do I update the JTable GUI object with the information obtained from the database?
public class AdminManager extends JFrame {
public static void main(String[] args) throws InterruptedException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
AdminManager frame = new AdminManager();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
// Setup connection pool
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(new ThreadTask(connection), 2000, 100, TimeUnit.MILLISECONDS);
}
/**
* Create the frame.
*/
public AdminManager() {
// Setup GUI
DefaultTableModel model = new DefaultTableModel();
model.addColumn("#");
tableQueue = new JTable(model);
tableQueue.getColumnModel().getColumn(0).setPreferredWidth(3);
scrollPane.setViewportView(tableQueue);
}
class updateTable extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
model.addRow(new Object[]{order_num});
return null;
}
}
}
class grabData implements Runnable {
private Connection connection;
private DefaultTableModel model;
private String order_num;
public grabData(Connection c, DefaultTableModel m) {
connection = c;
model = m;
}
#Override
public void run() {
System.out.println("Working ... ");
String sql = "SELECT * FROM order_queue;";
Statement st;
try {
st = connection.createStatement();
ResultSet rs = st.executeQuery(sql);
while(rs.next()) {
order_num = rs.getString("order_num");
System.out.println(order_num);
updateTable.execute()
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

If you are adding rows to a TableModel object that is held by a visualized JTable, then you must do so on the Swing event thread, the EDT. If you're creating a completely new TableModel one that isn't visualized, then I think it is safe to fill it off of the EDT, and then set it as the JTable's model on the EDT.
One consideration, if you want to add rows the JTable as they become available, consider using a SwingWorker<Void, RowObject>, and then pass the RowObject obtained in the while (rs.next()) { via a publish/process method pair.
Edit:
You could just skip the SwingWorker and just queue up adding the table's row on the EDT:
while(rs.next()) {
final String order_num = rs.getString("order_num");
// System.out.println(order_num);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
model.addRow(new Object[]{order_num});
}
});
}

Related

Update JTable in SwingWorker

I have a JTable which populates data from DB. I want to refresh data in JTable every 10 minutes (for testing 10 sec is enough). I tried do it with a Thread, but I found that it is not good idea, and I need to use SwingWorker
public class Monitor extends JFrame{
JTable jtable = null;
JTabbedPane jtp = null;
JPanel jp1 = null;
JPanel jp2 = null;
JLabel label1 = null;
JLabel label2 = null;
public void setJTable(Vector data, Vector columnNames) {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jtable = new JTable(data, columnNames);
JScrollPane scrollPane = new JScrollPane(jtable);
jp1.add(scrollPane);
}
public void updateJTable(Vector data, Vector columnNames) {
jtable = new JTable(data, columnNames);
}
public Monitor() {
setTitle("Monitor System");
//Panel with tabs for navigation
jtp = new JTabbedPane();
getContentPane().add(jtp);
//tab1, info from dba_jobs
jp1 = new JPanel();
//tab2 info from QueueInfo
jp2 = new JPanel();
label1 = new JLabel();
label1.setText("tab1");
label2 = new JLabel();
label2.setText("tab2");
jp1.add(label1);
jp2.add(label2);
jtp.add("Tab1", jp1);
jtp.add("Tab2", jp2);
}
}
And my Demo class:
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Statement stmt = null;
Connection conn = null;
ResultSet rs = null;
Vector<String> columnNames = new Vector<String>();
Vector<Vector> rowData = new Vector<Vector>();
DBMonitor dbmonitor = new DBMonitor();
Monitor monitor = new Monitor();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
rowData = dbmonitor.getJobsData();
columnNames = dbmonitor.getColumnNames();
monitor.setJTable(rowData, columnNames);
monitor.setSize((int) dim.getWidth(), (int) dim.getHeight());
monitor.setVisible(true);
boolean interrupt = true;
while (interrupt) {
try {
rowData = dbmonitor.getJobsData();
columnNames = dbmonitor.getColumnNames();
monitor.updateJTable(rowData, columnNames);
try {
Thread.sleep(10000);
} catch (InterruptedException ie) {
return;
}
JOptionPane.showMessageDialog(null, "SLEEP!");
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage());
System.err.println(e.getMessage());
}
}
}
}
How I can do it with SwingWorker? I don't get a concept of that way.
In the doInBackground() method of the SwingWorker you have you while loop that:
retrieves the data from the database
creates your TableModel
use the publish() method of the SwingWorker to pass the TableModel to the 'process()` method of your SwingWorker
sleeps for 10 seconds
Then in the process() method of the SwingWorker you:
use the TableModel that was passed to the process() method to update your JTable.
Read the section from the Swing tutorial on Concurrency for more information and a working example or search the forum for more SwingWorker examples.
First, I would start with a Timer of some sort, I'm going to use a Swing Timer as it easy, but you could use a java.util.Timer instead...
private Timer updateTimer;
//...
updateTimer = new Time(10000, new ActionListener() {
public void actionListener(ActionEvent e) {
}
});
updateTimer.setRepeats(false);
updateTimer.start();
This allows you to be notified in about 10 seconds time...
Then you need a SwingWorker that can do the actual work...
public class UpdateWorker extends SwingWorker<TableModel, Void> {
private Monitor monitor;
private Timer updateTimer;
public UpdateWorker(Monitor monitor, Timer updateTimer) {
this.monitor = monitor;
this.updateTimer = updateTimer;
}
#Override
protected TableModel doInBackground() throws Exception {
Vector<Vector> rowData = dbmonitor.getJobsData();
Vector columnNames = dbmonitor.getColumnNames();
DefaultTableModel model = new DefaultTableModel(rowData, columnNames);
return model;
}
#Override
protected void done() {
try {
TableModel model = get();
monitor.updateTable(model);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
updateTimer.restart();
}
}
Now in the actionPerformed method of the ActionListener assigned to the timer, you would do something like..
public void actionListener(ActionEvent e) {
UpdateWorker worker = new UpdateWorker(monitor, this);
worker.execute();
}
To execute the worker. The reason for having a non-repeating timer is to ensure that the next update is set for n seconds from when the update completes, so you don't get overlapping updates
Oh, and this will require to update your Monitor to accept a TableModel rather then the Vectors you are using to create one, it's just simpler that way...

JTextField Doesn't Update With Thread.sleep()

I'm trying to figure out why the text field isn't updating. I'm aware that using SwingWorker will probably fix this problem, but I can't understand why it doesn't work in the first place.
public class waitExample {
private JFrame frame;
private JTextField txtLeadingText;
private String one = "update string 1";
private String two = "update string 2";
private String three = "update string 3";
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
waitExample window = new waitExample();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public waitExample() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
txtLeadingText = new JTextField();
txtLeadingText.setHorizontalAlignment(SwingConstants.CENTER);
txtLeadingText.setText("leading text");
frame.getContentPane().add(txtLeadingText, BorderLayout.SOUTH);
txtLeadingText.setColumns(10);
JButton btnClickMeTo = new JButton("CLICK ME TO UPDATE TEXT");
btnClickMeTo.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
try {
updateOne();
Thread.sleep(1000);
updateTwo();
Thread.sleep(1000);
updateThree();
Thread.sleep(1000);
updateLast();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
frame.getContentPane().add(btnClickMeTo, BorderLayout.CENTER);
}
private void updateOne() {
txtLeadingText.setText(one);
}
private void updateTwo() {
txtLeadingText.setText(two);
}
private void updateThree() {
txtLeadingText.setText(three);
}
private void updateLast() {
txtLeadingText.setText("default text");
}
}
From what I understand, the default Thread will prevent any GUI updates. That shouldn't matter because I am setting the textField BEFORE the Thread.sleep.
Why doesn't the text field update? Shouldn't the text be set, then the Thread wait?
EDIT: As per the answers, the above code has been updated.
You are invoking Thread.sleep(1000); on EDT. This means that when your method will end - only then the repaint() will fire (at some point in time later).
Until then your GUI is freezed.
Consider that this is going on one thread (so processing is straightforward):
txtLeadingText.setText(one);
Thread.sleep(1000);
txtLeadingText.setText(two);
Thread.sleep(1000);
txtLeadingText.setText(three);
Thread.sleep(1000);
...
<returning from updateText()>
<processing other events on button click>
...
// some time later
<Swing finds out that GUI needs repaint: calls rapaint()>
This is what you should do (I didn't compile or test it):
public class MyRunnable implements Runnable {
private List<String> strsToSet;
public MyRunnable(List<String> strsToSet) {
this.strsToSet = strsToSet;
}
#Override
public void run() {
try {
if(strsToSet.size() > 0) {
final String str = strsToSet.get(0);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
txtLeadingText.setText(str);
}
});
Thread.sleep(1000);
List<String> newList = new LinkedList<String>(strsToSet);
newList.remove(0);
new Thread(new MyRunnable(newList)).start();
}
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
new Thread(new MyRunnable(Arrays.asList(one, two, three))).start();
It is hard to do in Swing but in contrast in dynamically languages (like Groovy) it would go as simple as that (you'll get a better grasp of what is going on):
edt {
textField.setText(one)
doOutside {
Thread.sleep(1000);
edt {
textField.setText(two)
doOutside {
Thread.sleep(1000);
edt {
textField.setText(three)
}
}
}
}
}
The GUI event loop updates the screen, but it can't update the screen until you return.
I suggest you avoid doing any blocking operations in the GUI event thread.

ExecutorService InvokeAll Progress Bar

I have an ExectorService and the following code, everything is working fine with the futures and the concurrency. However, when I try to update my progress bar from SomeClass(), it seems to only update the UI after the invokeAll() is complete...basically the progress bar only updates once everything is complete which deems it useless.
How can I resolve this? I've looked at CompletionServices as well as SwingWorkers but I don't know how to apply them to my code. Any assistance will be appreciated.
class SomeClass() {
private static class Result {
private final String someVar;
public Result(String code) {
this.someVar = code;
}
}
public static Result compute(Object obj) {
// ... compute stuff
someVar = "computedResult";
return Result(someVar);
}
public someFunction() {
List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
for (Object f : listOfObjects) {
Callable<Result> c = new Callable<Result>() {
#Override
public Result call() throws Exception {
someClassUI.jProgressBar.setValue(50);
return compute(file);
}
};
tasks.add(c);
}
List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
List<Future<Result>> results = executorService.invokeAll(tasks);
for (Future<Result> fr : results) {
String value = fr.get().resultValue;
}
}
}
class SomeClassUI {
public static jProgressBar;
public someClassUI() {
jProgressBar = new JProgressBar(0,100);
}
private void button1ActionPerformed(ActionEvent e) {
SomeClass theClass = new SomeClass();
theClass.someFunction();
}
}
edit: edited to add some extra code to help understanding
You're accessing a Swing component from a thread other than the event dispatch thread. That is forbidden by the Swing threading policy.
Use this code to update the progress bar from the background thread:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
someClassUI.jProgressBar.setValue(50);
}
});
Read more about concurrency in swing in the official swing tutorial.
I've been testing with code similar to yours, until I realized the following:
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException
Executes the given tasks, returning a list of Futures holding their status and results when all complete.
That "when all complete" is what is causing the behaviour of the progress bar. In other words, if you get the list of Future only when all tasks complete, then, obviously, iterating over them and updating the bar would be so fast that you see only the last update, when the bar is full.
What you could do, like I did, is calling submit for each of your tasks, and add the Futures individually to a list.
The example code below has been tested, and works here. You should be able to adapt it to your own purposes.
Listener interface:
public interface UpdateListener {
void update(double percent);
}
Task Executor:
public class SomeClass {
// instance variables
private UpdateListener listener;
private ExecutorService executor;
/** Parameter constructor of objects of class SomeClass. */
public SomeClass(UpdateListener l) {
listener = l;
executor = Executors.newFixedThreadPool(4);
}
/** */
public void doIt() throws InterruptedException, ExecutionException {
int numOfTasks = 5, completedTasks = 0;
List<Future<Integer>> results = new ArrayList<>();
// Submit each of your tasks. Here I create them manually.
for (int i = 0; i < numOfTasks; ++i) {
final int I = i;
Callable<Integer> c = new Callable<Integer>() {
#Override
public Integer call() throws Exception {
try {
Thread.sleep((long) I * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return new Integer(I);
}
};
results.add(executor.submit(c));
}
// Retrieve individual results and update progress bar.
for (Future<Integer> fr : results) {
Integer i = fr.get();
++completedTasks;
listener.update((double) completedTasks / numOfTasks);
}
}
}
UI class:
public class SomeClassUI implements Runnable, UpdateListener {
// instance variables
private JProgressBar bar;
private JFrame frame;
private SomeClass t;
/** Empty constructor of objects of class SomeClassUI. */
public SomeClassUI() {
t = new SomeClass(this);
}
/** Builds the interface. */
public void run() {
bar = new JProgressBar(0, 100);
bar.setStringPainted(true);
JPanel panel = new JPanel(new FlowLayout());
panel.setPreferredSize(new Dimension(200, 100));
panel.add(bar);
frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
/** Method from the interface. Updates the progress bar. */
#Overrides
public void update(double percent) {
final double PERCENT = percent;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int v = (int) (100 * PERCENT);
bar.setValue(v);
}
});
}
/** Tests the program. */
public void go() {
SwingUtilities.invokeLater(this);
try {
t.doIt();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main() {
new SomeClassUI().go();
}
}

How to show a splash-screen, load datas in the background, and hide the splash-screen after that?

I'm designing a simple JavaFX form.
First, I load the JavaFX environment (and wait for it to finish), with something like this :
final CountDownLatch latch_l = new CountDownLatch(1);
try {
// init the JavaFX environment
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new JFXPanel(); // init JavaFX
latch_l.countDown();
}
});
latch_l.await();
}
This works fine. (the reason why I need to first load the JavaFX this way, is because it's mainly a Swing application, with some JavaFX components inside, but they are loaded later)
Now, I'd like to add a splash-screen on launch, and displays it while the JavaFX environment loads (and in fact put in on-screen for like 5 seconds, because there are logo, trademark etc.. of the application I need to show)
So I came up with a SplashScreen class, which just displays a JWindow on-screen, like that :
public class SplashScreen {
protected JWindow splashScreen_m = new JWindow();
protected Integer splashScreenDuration_m = 5000;
public void show() {
// fill the splash-screen with informations
...
// display the splash-screen
splashScreen_m.validate();
splashScreen_m.pack();
splashScreen_m.setLocationRelativeTo(null);
splashScreen_m.setVisible(true);
}
public void unload() {
// unload the splash-screen
splashScreen_m.setVisible(false);
splashScreen_m.dispose();
}
}
Now, I want for the splash-screen to load and display itself 5 seconds.
Meanwhile, I want the JavaFX environment to load, too.
So I updated the CountDownLatch like this :
final CountDownLatch latch_l = new CountDownLatch(2); // now countdown is set to 2
final SplashScreen splash_l = new SplashScreen();
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// show splash-screen
splash_l.show();
latch_l.countDown();
// init the JavaFX environment
new JFXPanel(); // init JavaFX
latch_l.countDown();
}
});
latch_l.await();
splash_l.unload();
}
So, it's working, but the splash only stays for the JavaFX environment to load, so basically it unloads very quickly (which is normal, given the code I wrote).
How to display the splash-screen for 5 seconds minimum (if the JavaFX loads faster) without freezing the EDT ?
Thanks.
The most significant issue is you're blocking the Event Dispatching Thread, meaning that it can't display/update anything while it's blocked. The same problem applies to JavaFX.
You should, also, never update either from anything other then they respective event queues.
Now, there are any number of ways you might be able to go about this, but SwingWorker is probably the simplest for the time been.
I apologise, this is the entire exposure to JavaFX I've had...
public class TestJavaFXLoader extends JApplet {
public static void main(String[] args) {
new TestJavaFXLoader();
}
public TestJavaFXLoader() throws HeadlessException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Loader loader = new Loader();
loader.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state") && evt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
System.out.println("Load main app here :D");
}
}
});
loader.load();
}
});
}
public class Loader extends SwingWorker<Object, String> {
private JWindow splash;
private JLabel subMessage;
public Loader() {
}
protected void loadSplashScreen() {
try {
splash = new JWindow();
JLabel content = new JLabel(new ImageIcon(ImageIO.read(...))));
content.setLayout(new GridBagLayout());
splash.setContentPane(content);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
subMessage = createLabel("");
splash.add(createLabel("Loading, please wait"), gbc);
splash.add(subMessage, gbc);
splash.pack();
splash.setLocationRelativeTo(null);
splash.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
protected JLabel createLabel(String msg) {
JLabel message = new JLabel("Loading, please wait");
message.setForeground(Color.CYAN);
Font font = message.getFont();
message.setFont(font.deriveFont(Font.BOLD, 24));
return message;
}
public void load() {
if (!EventQueue.isDispatchThread()) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
loadSplashScreen();
}
});
} catch (Exception exp) {
exp.printStackTrace();
}
} else {
loadSplashScreen();
}
execute();
}
#Override
protected void done() {
splash.dispose();
}
#Override
protected void process(List<String> chunks) {
subMessage.setText(chunks.get(chunks.size() - 1));
}
#Override
protected Object doInBackground() throws Exception {
publish("Preparing to load application");
try {
Thread.sleep(2500);
} catch (InterruptedException interruptedException) {
}
publish("Loading JavaFX...");
runAndWait(new Runnable() {
#Override
public void run() {
new JFXPanel();
}
});
try {
Thread.sleep(2500);
} catch (InterruptedException interruptedException) {
}
return null;
}
public void runAndWait(final Runnable run)
throws InterruptedException, ExecutionException {
if (Platform.isFxApplicationThread()) {
try {
run.run();
} catch (Exception e) {
throw new ExecutionException(e);
}
} else {
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
lock.lock();
try {
Platform.runLater(new Runnable() {
#Override
public void run() {
lock.lock();
try {
run.run();
} catch (Throwable e) {
e.printStackTrace();
} finally {
try {
condition.signal();
} finally {
lock.unlock();
}
}
}
});
condition.await();
// if (throwableWrapper.t != null) {
// throw new ExecutionException(throwableWrapper.t);
// }
} finally {
lock.unlock();
}
}
}
}
}
I found the runAndWait code here

Pass SQL Connection to ActionListener (Thread)

How can I pass a SQL connection to a Action Listener. I want to have an infinite loop, that sleeps for 100ms. Every iteration the loop is suppose to query a database. Is swing timer the best way to do this? If so how can I pass the connection to the Action Listener. If not, can someone please advise on how this can be done. Much thanks.
Code:
public static void main(String[] args) throws InterruptedException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
AdminManager frame = new AdminManager();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
BoneCP connectionPool = null;
Connection connection = null;
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
return;
}
try {
// setup the connection pool
BoneCPConfig config = new BoneCPConfig();
config.setJdbcUrl("jdbc:mysql://192.162.0.0");
config.setUsername("root");
config.setPassword("");
connectionPool = new BoneCP(config); // setup the connection pool
connection = connectionPool.getConnection(); // fetch a connection
if (connection != null){
System.out.println("Connection successful!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// Define listner
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
String sql = "SELECT * table;";
Statement st = connection.createStatement();
ResultSet rs = st.executeQuery(sql);
while(rs.next()) {
String symbol = rs.getString("name");
System.out.println(symbol);
}
}
};
Timer timer = new Timer( 100 , taskPerformer);
timer.setRepeats(true);
timer.start();
Thread.sleep(1000);
//connectionPool.shutdown(); // shutdown connection pool.
}
Do not the javax.swing.Timer class to periodically execute a non-Swing task. Instead, use a ScheduleExecutorService,
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.schedule(new Runnable(){
#Override
public void run(){
// query database
}
}, 0, 100, TimeUnit.MILLISECONDS);
If the background task must continually update a Swing component, use SwingWorker to process() periodic updates to the component's model. In this example, a JTextArea is updated with data obtained from an H2 database.

Categories