Controlando ProgressBar da SWT com uma Thread responsável pelo processo do negócio

4 respostas
jProbst

Olá colegas,
estou quebrando a cabeça para resolver uma situação.

Tenho uma aplicação em SWT e permito executar um processamento em que eu não tenho controle do tempo, por isso resolvi colocar um ProgressBar e jogar o processo de negócio em uma Thread.
Até aí tudo bem, parece ser simples, e funciona mas somente uma vez, na segunda gera a seguinte exceção:

Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Unknown Source)
	at swt.progress.MainPanel.runProcess(MainPanel.java:56)
	at swt.progress.MainPanel.access$0(MainPanel.java:52)
	at swt.progress.MainPanel$1.handleEvent(MainPanel.java:31)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:928)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3348)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2968)
	at swt.progress.MainPanel.run(MainPanel.java:64)
	at swt.progress.StartMain.main(StartMain.java:7)

Criei uma situação semelhante do meu caso no código abaixo.
Alguém se aventura em solucionar esse mistério?

Classe main:

public class StartMain {

	public static void main(String[] args) {
		MainPanel mainPanel = new MainPanel();
		mainPanel.run();
		mainPanel.done();
	}
}

Painel principal

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

public class MainPanel {

	private Display display;
	private Shell shell;
	private Process process;
	
	public MainPanel() {		
		this.display = new Display();
	    this.shell = new Shell(this.display);
	    this.process = new Process();
	    this.initGUI();   
	}

	private void initGUI() {
		shell.setLayout(new GridLayout(3, true));

		Button button = new Button(shell, SWT.NONE);
		button.setText("3s");
		button.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				runProcess(3000);
			}
		});
		
		button = new Button(shell, SWT.NONE);
		button.setText("5s");
		button.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				runProcess(5000);
			}
		});
		
		button = new Button(shell, SWT.NONE);
		button.setText("10s");
		button.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				runProcess(10000);
			}
		});
	}
		
	private void runProcess(long time){
		this.process.setTimeProcess(time);		
		Progress progress = new Progress(this.shell);
		this.process.registerInterest(progress);
		this.process.start();
		progress.run();
	}
	
	public void run() {
		shell.setSize(500, 300);	    
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
	}

	public void done() {
		display.dispose();
	}	
}

Classe que tem o progressbar

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;

public class Progress implements Observer {

	private Shell shell;	
	
	public Progress(Shell shell) {
		this.shell = new Shell(shell, SWT.TITLE | SWT.APPLICATION_MODAL);
	    this.initGUI();
	}	
	
	private void initGUI() {
		this.shell.setText("Aguarde...");
		this.shell.setLayout(new GridLayout());

		ProgressBar  progressBar = new ProgressBar(this.shell, SWT.HORIZONTAL | SWT.INDETERMINATE);
		GridData data = new GridData (SWT.CENTER, SWT.CENTER, false, false);
		progressBar.setLayoutData (data);		
	}
	
	public void closeProgress(){
		this.shell.dispose();
	}
	
	public void run() {
		Rectangle displayBounds = this.shell.getDisplay().getBounds();
        int minWidth = (displayBounds.width / 2) - 300;
        int minHeight = (displayBounds.height / 2) - 250;
        int left = (displayBounds.width - minWidth) / 2;
        int top = (displayBounds.height - minHeight) / 2;
        this.shell.setBounds(left, top, minWidth, minHeight);        
        this.shell.open();
	}

	@Override
	public void sendNotify() {
		this.shell.getDisplay().syncExec(new Runnable(){
			@Override
			public void run() {
				shell.dispose();
			}
		});
	}
}

Classe Thread que executa o processo

import java.util.ArrayList;
import java.util.List;

public class Process extends Thread {
	
	private long timeProcess;
	private List<Observer> observers = new ArrayList<Observer>();
		
	public void setTimeProcess(long time){
		this.timeProcess = time;
	}
		
	public void run(){
		try {
			int contador = 0;
			System.out.println("iniciando processo..." + (this.timeProcess/1000) + "s");
			while(contador < (this.timeProcess/1000)){
				contador++;
				System.out.println("contador " + contador);
				Thread.sleep(1000);
			}
			System.out.println("fim do processo.");
			this.notifyObservers();
		} catch (Exception e) {
			System.err.println("ocorreu um erro aqui: " + this.getClass());
			e.printStackTrace();
		}
	}	
	
	public void registerInterest(Observer obs) {
		this.observers.add(obs);
	}
	
	private void notifyObservers(){
		for (final Observer obs : this.observers) {
			obs.sendNotify();
		}
	}
}

Ah, a interface Observer

public interface Observer {

	public void sendNotify();
}

4 Respostas

davidtiagoconceicao

Não tive tempo de ler todo o seu código cara, mas a exceção IllegalThreadStateException ocorre quando você tenta iniciar uma thread que está em um estado não iniciável. Geralmente ocorre quando você tenta iniciar novamente uma thread que já morreu. Uma thread pode ser iniciada apenas uma vez, para iniciá-la novamente é necessário instanciar um novo objeto.

davidtiagoconceicao

Dá uma olhada na documentação:

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html#start()

jProbst

davidtiagoconceicao,
você tinha razão, não estava atento a esse detalhe,
só foi eu remover o atributo processo, que é a Thread, da classe do Principal e instânciá-la sempre que o processo for executado.

Aí deu certo.

Valeu

davidtiagoconceicao

Beleza, qualquer coisa poste ai!

Criado 16 de dezembro de 2008
Ultima resposta 17 de dez. de 2008
Respostas 4
Participantes 2