Reputation: 1123
i'm new to this community!
I want to ask you something about SwingWorker and its relationship with a GUI.
I know there are some answered questions about SwingWorker, and i've already read a lot of them, taking some helpful advices.
Now i'd like to post some code i wrote for a basic application which counts the number of files and folders from a specified directory.
Since the search could take a lot of time, i want a progress bar to be displayed during the process. Also, i would like users to have the possibility of stopping count process by clicking a button or simply closing the frame which contains the progress bar.
Here there are some questions on the code posted below:
And here's the code:
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import javax.swing.*;
import javax.swing.border.*;
public class CountFiles
{
public static void main(String[] args)throws Exception
{
SwingUtilities.invokeLater(new Runnable(){
public void run(){
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new CountFilesFrame().setVisible(true);
}
catch(Exception ex){
ex.printStackTrace();
}
}
});
}
}
class CountFilesFrame extends JFrame
{
private JTextField field;
public CountFilesFrame()
{
super("Conta File e Cartelle");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
JPanel pane=(JPanel)getContentPane();
pane.setBackground(Color.WHITE);
pane.setBorder(new EmptyBorder(5,20,5,20));
JPanel center=new StyledPanel(pane,BorderLayout.CENTER,new FlowLayout(FlowLayout.LEFT,5,10)),bottom=new StyledPanel(pane,BorderLayout.SOUTH,new FlowLayout(FlowLayout.LEFT,20,0));
// Center panel
center.add(new JLabel("Cartella :"));
String text="";
try{
File folder=new File("../");
text=folder.exists()?folder.getCanonicalPath():"";
}
catch(Exception ex){}
field=new JTextField(text,25);
center.add(field);
// JTextArea
String newLine=System.getProperty("line.separator"),message="Scegliere la cartella da cui far partire la ricerca."+newLine+
"Sara' contato il numero di file e di cartelle presenti "+newLine+"nella directory inserita e in tutte le sottocartelle";
JTextArea area=new JTextArea(message);
area.setEditable(false);
area.setFont(field.getFont());
pane.add(area,BorderLayout.NORTH);
// Bottom panel
bottom.add(new JButton(new AbstractAction("Cambia Cartella"){
public void actionPerformed(ActionEvent e){
changeDirectory();
}
}));
bottom.add(new JButton(new AbstractAction("Inizia ricerca"){
public void actionPerformed(ActionEvent e){
new WaitingFrame(CountFilesFrame.this);
}
}));
pack();
setLocationRelativeTo(null);
}
public void changeDirectory()
{
JFileChooser chooser=new JFileChooser(field.getText());
chooser.setDialogTitle("Cambia Cartella");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if(chooser.showDialog(this,"Scegli")==JFileChooser.APPROVE_OPTION)
{
try
{
File selected=chooser.getSelectedFile();
if(selected.exists())field.setText(selected.getCanonicalPath());
}
catch(Exception ex){}
}
}
private class WaitingFrame extends JFrame
{
private Counter counter;
public WaitingFrame(CountFilesFrame f)
{
super("Ricerca File");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
stopCounter();
}
});
setResizable(false);
JPanel pane=(JPanel)getContentPane(),buttonPanel=new StyledPanel(pane,BorderLayout.SOUTH,new FlowLayout(FlowLayout.CENTER,0,10));
JLabel label=new JLabel("Conteggio in corso...",JLabel.CENTER);
label.setBorder(new EmptyBorder(0,0,10,0));
pane.add(label,BorderLayout.NORTH);
pane.setBackground(Color.WHITE);
pane.setBorder(new EmptyBorder(10,40,0,40));
JProgressBar progressBar=new JProgressBar(0,100);
progressBar.setBorderPainted(false);
progressBar.setIndeterminate(true);
pane.add(progressBar,BorderLayout.CENTER);
buttonPanel.add(new JButton(new AbstractAction("Annulla"){
public void actionPerformed(ActionEvent e){
stopCounter();
}
}));
while(pane.getSize().width!=pane.getPreferredSize().width)pack();
setLocationRelativeTo(null);
setVisible(true);
(counter=new Counter()).execute();
}
public void stopCounter()
{
counter.interrupt();
counter.cancel(true);
}
private class Counter extends SwingWorker<Void,Void>
{
private boolean valid=true,interrupted=false;
private int filesNumber=0,foldersNumber=0;
protected Void doInBackground()
{
File folder=new File(field.getText());
if(!folder.exists()||!folder.isDirectory())valid=false;
else countFiles(folder);
return null;
}
protected void done()
{
dispose();
if(interrupted)return;
else if(!valid)JOptionPane.showMessageDialog(CountFilesFrame.this,"Inserire una cartella valida","Percorso specificato errato",JOptionPane.ERROR_MESSAGE);
else JOptionPane.showMessageDialog(CountFilesFrame.this,"Sono stati trovati "+(foldersNumber-1)+" cartelle e "+filesNumber+" file","Ricerca completata",JOptionPane.INFORMATION_MESSAGE);
}
private void countFiles(File file)
{
if(file.isDirectory())
{
foldersNumber++;
for(File nested:file.listFiles())countFiles(nested);
}
else filesNumber++;
}
public void interrupt()
{
interrupted=true;
}
}
}
}
class StyledPanel extends JPanel
{
public StyledPanel(JPanel parent,String position,LayoutManager layout)
{
super(layout);
setBackground(Color.WHITE);
parent.add(this,position);
}
}
I posted all the application code, so you can try to compile and run it.
Thanks in advance for your help!
PS: i didn't change interface language, i'm sorry for that. Also, i'm sorry for my bad english...
Upvotes: 0
Views: 773
Reputation: 5928
The call to execute() method for SwingWorker is the last instruction of WaitingFrame constructor: is there a better place for it?
There is nothing wrong with how you are calling it.
The dispose() method for WaitingFrame is called from SwingWorker's done() method, is it correct? If a count process is very fast, could the dispose method be called before the waiting frame is actually visible? As a result, i would have two open frames...
It is correct and the situation you describe cannot happen. SwingWorker.done()
is called on EDT via a delayed SwingUtilities.invokeLater
call and you already called JFrame.setVisible(true)
before constructing the SwingWorker
(on EDT).
Instead of multiple frames, consider using a dialog, perhaps a modal one if you are attempting to block user input (like here).
Is there a better method to interrupt the process and to manage the message dialogs shown to users? I used two boolean variables, valid and interrupted, to achieve my purpose...
There certainly is a better way of doing this, considering that your current code is dangerously close to not being thread safe. Consider using an AtomicBoolean
instead of boolean
if both EDT and swing worker need to access those two members.
You should also check the interrupted
flag somewhere and exit your file listing loop upon it changing.
You can also wrap any swing code into a SwingUtilities.invokeLater
call anywhere inside SwingWorker.doInBackground()
for fine grained GUI updates. Doing this essentially has the same effect as if done()
had been invoked but you control when and how many times it is called. Check here for a code example. Your own code does this to pass execution from the main thread to EDT inside your main()
method (the reason for this code pattern is that all swing code must execute on EDT).
Upvotes: 1