Reputation: 1039
I'm trying to make a login screen for an application. During the login, many SQL calls will be made to a MySQL database, and it may take a few seconds to set everything up. I'd like to display a status screen via Card Layout and update a JLabel while the background thread is running.
Here's the gist of what I have for my Worker Thread:
public class LoginPrepThread extends Thread {
private final UIMain parent;
public LoginPrepThread(UIMain w){
parent = w;
}
public void exec(){
EventQueue.invokeLater(this);
}
public void run(){
try{
//SqlHelper sql = SqlHelper.instance;
sleep(500);
parent.getLoadingLable().setText("Fetching preferences...");
parent.getMainFrame().revalidate();
sleep(500);
parent.getLoadingLable().setText("Scanning workbench...");
parent.getMainFrame().revalidate();
sleep(500);
parent.getLoadingLable().setText("Updating permissions...");
parent.getMainFrame().revalidate();
sleep(500);
parent.getLoadingLable().setText("Finished...Please wait");
parent.getMainFrame().revalidate();
sleep(1000);
parent.getLayout().show(parent.getMainFrame().getContentPane(), "view.main");
}catch(Exception e){
}
}
}
Here is how I am calling it (Inside the event of a JButton, after authenticating):
setActiveProfile(user);
layout.show(frame.getContentPane(), "view.loading");
frame.repaint();
LoginPrepThread pt = new LoginPrepThread(thisTrick);
pt.exec();
I put some dummy events in for now, but the status label doesn't change...any suggestions?
Upvotes: 3
Views: 2130
Reputation: 347184
You started out with good intentions...
public class LoginPrepThread extends Thread {
private final UIMain parent;
public LoginPrepThread(UIMain w){
parent = w;
}
public void exec(){
EventQueue.invokeLater(this);
}
public void run(){
try{
//SqlHelper sql = SqlHelper.instance;
sleep(500);
parent.getLoadingLable().setText("Fetching preferences...");
parent.getMainFrame().revalidate();
sleep(500);
parent.getLoadingLable().setText("Scanning workbench...");
parent.getMainFrame().revalidate();
sleep(500);
parent.getLoadingLable().setText("Updating permissions...");
parent.getMainFrame().revalidate();
sleep(500);
parent.getLoadingLable().setText("Finished...Please wait");
parent.getMainFrame().revalidate();
sleep(1000);
parent.getLayout().show(parent.getMainFrame().getContentPane(), "view.main");
}catch(Exception e){
}
}
}
Basically, what's happening, is when you call exec
your actually placing a request onto the EDT to call the run
method, which is then been executed within the EDT, causing the blockage.
Also, in your original example, it's pointless to extend from Thread
as you never actually start it.
For future reference, I would have a closer look at Concurrency in Java
You are correct (in your self answer), SwingWorker
is a much easier solution.
Upvotes: 0
Reputation: 6783
Ideally you would be implementing a SwingWorker to perform all your heavy non-GUI related tasks. You should take the advantage of what event-driven programming is all about.
What you need to do would be something like this:
Implement a PropertyChangeListner to your GUI class, listening to updates. Based on the property changes, update the labels. It is always a good practice to have a single GUI class handle all the GUI related update activities.
Create a SwingWorker where you would perform your background-intensive tasks. As and when there're updates available, fire a property change event and let the GUI class know that there's an update.
Here's a small SSCCE example of what you can do:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.SwingWorker;
/**
*
* @author Sujay
*/
public class SimpleWorkerUI extends javax.swing.JFrame implements PropertyChangeListener{
/**
* Creates new form SimpleWorkerUI
*/
public SimpleWorkerUI() {
initComponents();
Worker worker = new Worker();
worker.addPropertyChangeListener(this);
worker.execute();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jPanel1 = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jLabel1.setText("Current Status: ");
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel2)
.addContainerGap(327, Short.MAX_VALUE))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(20, 20, 20)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(jLabel2))
.addContainerGap(20, Short.MAX_VALUE))
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
);
pack();
}// </editor-fold>
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(SimpleWorkerUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(SimpleWorkerUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(SimpleWorkerUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(SimpleWorkerUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new SimpleWorkerUI().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JPanel jPanel1;
// End of variables declaration
@Override
public void propertyChange(PropertyChangeEvent evt) {
if("status".equalsIgnoreCase(evt.getPropertyName())){
String currentStatus = (String) evt.getNewValue();
jLabel2.setText(currentStatus);
}
}
}
class Worker extends SwingWorker<String, String>{
private static final int FINAL_VALUE = 1000;
@Override
protected String doInBackground() throws Exception {
int counter = 0;
while(counter < FINAL_VALUE){
firePropertyChange("status", "", "value is: "+counter);
try{
Thread.sleep(100);
}catch(InterruptedException ixe){
}
counter++;
}
return null;
}
}
Upvotes: 3
Reputation: 1039
A simple swing worker solved it. I guess I wasn't good enough on my google'ing
public class LoginPrepThread extends SwingWorker<String,String> {
private final UIMain parent;
public LoginPrepThread(UIMain w){
parent = w;
}
@Override
protected String doInBackground() throws Exception {
try{
publish("Fetching preferences...");
Thread.sleep(1000);
publish("Updating permissions...");
Thread.sleep(1000);
publish("Scanning workbench...");
Thread.sleep(1000);
publish("Finalizing...");
Thread.sleep(2000);
publish("Finished...Please wait");
Thread.sleep(1000);
parent.getLayout().show(parent.getMainFrame().getContentPane(), "view.main");
}catch(Exception e){
}
return null;
}
protected void process(List<String> item) {
parent.getLoadingLable().setText(item.get(0));
}
}
Upvotes: 4
Reputation: 3150
You should not perform any long-running operations on the event dispatch thread. You are performing an operation that takes 3 seconds to complete. During those 3 seconds, you are monopolizing the EDT and no other GUI updates can happen.
Upvotes: 1