Search code examples
javaswingconcurrencyjdialogjprogressbar

JProgressBar in dialog frame not working properly


I have a java program that load a text file as input, read its content, modify some strings and then prints the result to a textarea. Due to several seconds required by this operation i would like to show a JProgressBar during this activity in order to inform the user that the execution is in progress and when the activity is completed close the dialog containing the JprogressBar and print the results.

Here is the code:

JButton btnCaricaFile = new JButton("Load text file");
        panel.add(btnCaricaFile);
        btnCaricaFile.setIcon(UIManager.getIcon("FileView.directoryIcon"));
        btnCaricaFile.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                //JFileChooser choice = null;
                final JFileChooser choice = new JFileChooser(userDir +"/Desktop");
                int option = choice.showOpenDialog(GUI.this);
                if (option == JFileChooser.APPROVE_OPTION) {
                    final JDialog dialog = new JDialog(GUI.this, "In progress", true);
                    JProgressBar progressBar = new JProgressBar(0, 100);
                    progressBar.setIndeterminate(true);
                    dialog.getContentPane().add(BorderLayout.CENTER, progressBar);
                    dialog.getContentPane().add(BorderLayout.NORTH, new JLabel("Elaborating strings..."));
                    dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
                    dialog.setSize(300, 75);
                    dialog.setLocationRelativeTo(GUI.this);
                    Thread t = new Thread(new Runnable() {
                        public void run() {
                            dialog.setVisible(true);
                            File file = choice.getSelectedFile();
                            lista.clear();
                            textArea.setText("");
                            lista = loadFile.openFile(file);
                            for(int i=0; i<lista.size(); i++) {
                                textArea.append(lista.get(i)+"\n");
                            }
                            dialog.setVisible(false);
                        }
                    });
                    t.start();
                }
            }
        });

For this purpose i'm using a JDialog as container for the JProgressBar executed by an appropriate thread. The problem is that the progress bar is shown for an infinite time and is not printed anything to the textarea.

Could you help me to solve this? Thanks


Solution

  • Yes, you're creating a background thread for your file reading, good, but you're also making Swing calls from within this same background thread, not good, and this is likely tying up the Swing event thread inappropriately. The key is to keep your threading separate -- background work goes in the background thread, and Swing work goes only in the Swing thread. Please read Lesson: Concurrency in Swing fore more on this.

    Myself, I would create and use a SwingWorker<Void, String>, and use the worker's publish/process method pair to send Strings to the JTextArea safely.

    For example, something like...

    final JDialog dialog = new JDialog(GUI.this, "In progress", true);
    JProgressBar progressBar = new JProgressBar(0, 100);
    progressBar.setIndeterminate(true);
    dialog.getContentPane().add(BorderLayout.CENTER, progressBar);
    dialog.getContentPane().add(BorderLayout.NORTH, new JLabel("Elaborating strings..."));
    dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
    dialog.setSize(300, 75);
    dialog.setLocationRelativeTo(GUI.this);
    lista.clear();
    SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
    
        @Override
        public Void doInBackground() throws Exception {
            // all called *off* the event thread
            lista = loadFile.openFile(file);
            for (int i = 0; i < lista.size(); i++) {
                publish(lista.get(i));
            }
            return null;
        }
    
        @Override
        protected void process(List<String> chunks) {
            // called on the event thread
            for (String chunk : chunks) {
                textArea.append(chunk + "\n");
            }
        }
    
        // called on the event thread
        public void done() {
            dialog.setVisible(false);
            // should call get() here to catch and handle
            // any exceptions that the worker might have thrown
        }
    };
    worker.execute();
    dialog.setVisible(true); // call this last since dialog is modal
    

    Note: code not tested nor compiled