I have a Java-Gnome GUI that is initialised like this:
public static void main(String[] args) {
Gtk.init(args);
new GTK();
Gtk.main();
}
public GTK() {
initUI(this);
//Exit GUI cleanly if close pressed
connect(this);
showAll();
}
public void initUI(final GTK gtk) {
//Add various panes, boxes, buttons etc...
//All my UI updates made at runtime here work correctly
start.connect(new Button.Clicked(){
@Override
public void onClicked(Button start){
worker = new worker(fileList, gtk);
Thread workerThread = new Thread(worker);
workerThread.start();
Glib.idleAdd(new Handler(){
public boolean run(){
progress = worker.getProgress();
if(progress != 1){
progressBar.setFraction(progress);
return true;
}
else{
progressBar.setFraction(progress);
return false;
}
}
});
}
});
}
Where progressBar is a component in my GUI (org.gnome.gtk.ProgressBar) but the progressBar does not update until everything has finished running, it seems like it gets put on the back of the queue though and simply executes at the end as opposed to immediately as I was hoping.
http://java-gnome.sourceforge.net/NEWS.html
Edit.
My worker thread spans two classes the first:
public class auCDtect implements Runnable {
private String processingLog;
private String output;
private String summary;
private Collection<String> fileList;
private double progress = 0.0;
private GTK gtk;
auCDtect(Collection<String> fileList, GTK gtk){this.fileList = fileList; this.gtk = gtk;}
public void run () {
List<String> command = new ArrayList<String>();
command.add("./auCDtect");
command.add("-d");
command.add("-m10");
//Add each song passed to this class to the auCDtect command
for(String file:fileList){
command.add(file);
}
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = null;
try {
process = processBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}
//Set up error stream thread
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", this);
//Set up output stream thread
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", this);
// Start error and input stream threads
new Thread(errorGobbler).start();
new Thread(outputGobbler).start();
}
public void update(double progress, String processingLog, String output, String summary){
this.processingLog = processingLog;
this.output = output;
this.summary = summary;
this.progress = progress/(fileList.size());
gtk.setOutputUpdated(true);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public double getProgress(){
return progress;
}
public String getProcessingLog(){
return processingLog;
}
public String getOutput(){
return output;
}
public String getSummary(){
return summary;
}
}
and the second spawned by the 1st:
public class StreamGobbler implements Runnable {
InputStream inputStream;
String type;
private String processingLog = null;
private String output = null;
private String summary = null;
protected boolean finished = false;
private auCDtect auCDtect;
private int progress = 0;
StreamGobbler(InputStream inputStream, String type, auCDtect auCDtect){this.inputStream = inputStream; this.type = type; this.auCDtect = auCDtect;}
public void run(){
Scanner scanner = new Scanner(inputStream);
while (((scanner.hasNextLine())&&(type == "OUTPUT"))){
String line = scanner.nextLine();
if(line.contains("Processing file:")){
processingLog = line.substring(line.indexOf("P"), (line.indexOf("]")+1));
}
if(line.contains("This track looks like")){
output = line.substring(line.indexOf("This track"), (line.indexOf("%")+1));
progress = progress + 1;
}
if(line.contains("These")){
summary = line.substring(line.indexOf("These tracks"));
}
if((type == "OUTPUT")&&(progress > 0)){
auCDtect.update(progress, processingLog, output, summary);
processingLog = null;
output = null;
summary = null;
}
}
}
}
Not sure this applies to the java version, I’m more used to the C version, but anyway…
It should not be the worker thread that tries to update the main thread, but the reverse. From the callback handling the
clickedsignal, you should callidleAdd, and create and run your worker thread. In the worker thread, you update a variable to which the main thread will have access, and where you will put the progress value. Then, the main loop will run the idle handler once in a while, and will read that value. It will then update the progress bar, but what is important is that this call is done from the main thread.GTK is not thread-safe, as most toolkits (the java version may be close to be thread safe, but I’m not 100% sure, and subsequent changes done in GTK may break this in the future, so I would not rely on it). Therefore, your worker thread should not mess with user-interface components.
Take this with a grain of salt, I know a bit GTK in C, not java-gnome, so I may be completely wrong, which the
idleAdddocumentation seems to confirm anyway.