Tracking the progress of wget (bash) with SwingWorker / Swing - java

I am trying to make a gui, whereby users are able to download files. Currently I am able to call the wget command through a process, but I am struggling to use it along with swingworker.
How would I go about tracking the progress of the downloading and updating a gui simultaneously?
Currently I have tried using this method:
ShellProcess.command("wget --progress=dot "+_url);
Where command is the method that creates the process:
InputStream stdout = _process.getInputStream();
BufferedReader stdoutBuffered =new BufferedReader(new InputStreamReader(stdout));
String line = null;
String output ="";
try {
while ((line = _stdoutBuffered.readLine()) != null ) {
// System.out.println(line);
output+=(line+" ");
System.out.println(line +" SHELL");
_progress++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
_progress = 0;
return output;
}
I am trying to count the amount of lines outputted as "wget --progress=dot" should output a line for every percent of progress. But this does not seem to work.
My doInBackground method inside the swingworker looks like this:
#Override
protected Integer doInBackground() throws Exception {
// Start
download .command("wget "+_url);
ShellProcess.command("wget --progress=dot "+_url);
int progress = 0;
while (progress<101){
progress = ShellProcess.getProgress() %100 ;
publish(ShellProcess.getOutput());
setProgress(progress);
}
return 1;
}
Any help would be appreciated.

In this complete example, the background method of a SwingWorker starts a ProcessBuilder. Standard output and error streams are combined for display in a text component. Substitute your wget command to see the effect. Experiment with --progress=bar and reading a character at a time.
ProcessBuilder pb = new ProcessBuilder("wget", "--progress=dot", url);

You don't really need SwingWorker for this. Just download in a separate Thread (don't do it in the EDT), and every time you encounter a new dot line output from wget, update a GUI component (a progress bar for example), but do this update in the EDT e.g. with SwingUtilities.invokeLater():
JProgressBar progressBar = ...; // Initialize and add progress bar to your GUI
...
// In your separate download thread:
final AtomicInteger percent = new AtomicInteger();
while ((line = _stdoutBuffered.readLine()) != null ) {
if (".".equals(line)) {
// A new percent was completed, update the progressbar:
percent.incrementAndGet();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
progressBar.setValue(percent.get());
}
});
}
}

Related

Why does my Swing worker thread interrupt work?

I have a small java program, written in Eclipse using WindowBuilder, which works on reading data from UTF-8 text files and writing them into a database. To maintain the GUI's responsiveness, I use a swing worker thread, executed when clicking on a button.
btnex.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
worker = new SwingWorker<Void, Void>() {
public Void doInBackground() {
String[] content = Reader.getContent(file);
//do something with the content, if something goes wrong, set error to true.
return null;
}
public void done() {
if (!error) {
//handle error
}
};
worker.execute();
The function getContent in the class Reader extracts data from the file into the string array.
public static String[] getContent (String dbfile) {
try {
String[] lines = null;
String[] linesplit = null;
String store = "";
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(dbfile), "UTF8"));
String line = "";
line = reader.readLine();
linesplit = line.split(";");
while (linesplit.length > 1 && !line.equals(null)) {
for (int i = 0; i < linesplit.size(); i++) {
store += StringFormer.decrypt(linesplit[i]) + " ";
} //StringFormer is another class written by me, just for decrypting the string
store += "\n";
line = reader.readLine();
if (line.equals(null)) break;
linesplit = line.split(";");
}
reader.close();
lines = store.split("\n");
return lines;
} catch (Exception ex) { //...
}
}
When I try to run my program and click the button, the program does not work correctly. So I run the program in debug mode, and as a result, the thead does not finish but somehow exits before finishing all work. This happens in getContent, after leaving the while loop but before working on reader.close(). Before leaving the while loop, the call stack in the debug view contains the calls of the button click and of getContent, next to others, but once I leave the loop, the two mentioned above are dropped and the next top stack member names the base swing worker class: SwingWorker$2(FutureTask).run.
Does anyone know, why the program does not finish the written work flow? I work with multiple threads in the program, but never two background threads are running at the same time.
I found the answer myself:
The line if(line.equals(null) throws a NullPointerException. line is null when nothing more can be read out of the file, but the function equals requires that the compared variable is defined. Since line was null, equals threw the exception without me noticing it. The line has to be if(line==null).

Redirecting stdout to swt.

Display disp = Display.getCurrent();
disp.asyncExec(new Runnable() {
public void run(){
try {
PipedOutputStream pos = new PipedOutputStream();
System.setErr( new PrintStream(pos, true) );
System.setOut( new PrintStream(pos, true) );
PipedInputStream pis = new PipedInputStream( pos );
BufferedReader reader = new BufferedReader( new InputStreamReader(pis) );
String line = null;
while (true){
line = reader.readLine(); // != null)
console.append(line);
System.out.println("moo" + line);
parent.layout(true,true);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
I'm trying to capture the contents of System.err to print them out in a debug window of my application. I can run above code but the content from System.err is not being displayed in my application, it's just being printed to console, any ideas why ?
i'm not familiar with swt programming. but i remember the way to redirect stdout is to set the output stream, as you did in calling System.setErr, to a customized object that implements PrintStream. This object's print method will know how to update the GUI
The problem is that you are echoing infinitely to System.out. What you probably want to do is to store the old System.out at the start of run(), and echo there rather than to the new System.out in your read loop.
PrintStream oldOut = System.out;
// ... set out and err
while(true) {
...
oldOut.println("moo" + line);
}
Edit:
Also, you want to start a new thread rather than calling asyncExec(), which runs on the EDT and hence will cause your GUI to hang.

Unable to perform any action before Process.Runtime.exec statement line

my very first post here after long time of searching but yet get an answer regarding this issue, please help me in this issue.
I am using Netbean 6.9.1 to build a Java application which massive call to few different external program, therefore I used process and runtime function to call for external program.
The whole application process is separated into few stages and I wish to inform the user till which stage the application is currently running by updating the GUI textarea, the code is showed as below:
public void executeCommand (String cmd, File path)
{
try
{
****areaOutput.setText("Executing audio decoding, please wait till process is done\n");****
btnTranscribe.setEnabled(false);
areaOutput.setEditable(false);
areaOutput.setEnabled(false);
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd , null, path);
InputStream stderr = proc.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<ERROR>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</ERROR>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
areaOutput.append("\n\nConversion is done, processing with features extraction....");
} catch (Throwable t)
{
t.printStackTrace();
}
}
As showed in the code above, I wish to set the Textarea and disable some button before executing the command, but when the application ran, all of these line seems unable to work and nothing is changed at the application itself till the command is finish executed, any solution to run the pre-command code execute first before the .exec() start to run?
I appreciate your great help and advice regarding this issue.
Best regards,
Striky
P/S:
hi there, I have make a Thread class for this CmdExec in order to execute cmd in different thread:
public class CmdExec extends Thread
{
private String cmd;
private File path;
public CmdExec() {
}
public CmdExec(String cmd, File path) {
this.cmd = cmd;
this.path = path;
}
public void run(){
try
{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd , null, path);
InputStream stderr = proc.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<ERROR>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</ERROR>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
and in order to call to this class,
CmdExec tryDemo = new CmdExec();
tryDemo = new CmdExec(strSegment, fSegment);
tryDemo.run();
is used to start the thread, but I failed to put SwingUtilities.invokeLater in any part of these process, it simply won't run the tryDemo.run() because it is void...
Also, may I know so far am I doing right?? Very thank you for your kind help regarding this issue
P/S 2: I have just added another runnable code (so threads for process executing, runnable to GUI update) for GUI update command as below:
Runnable doWorkRunnable = new Runnable() {
public void run() {
System.out.println("hello world");
btnTranscribe.setEnabled(false);
areaOutput.setEditable(false);
areaOutput.setEnabled(false);
areaOutput.setText("Performing segmentation, please wait till process is done\n"); }
};
and I used SwingUtilies.invokeLater before the execution of process as below:
SwingUtilities.invokeLater(doWorkRunnable);
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd , null, path);
But all these failed, am I get the wrong sequence for the GUI and process thread coordination?
you are executing this work on the EDT (the thread which updates the gui). so, the gui cannot update until all this work finishes. what you want to do is run a separate thread which does all the work and periodically calls SwingUtilities.invokeLater with a status update.
Try to put sleep before execute method. To verify what is happening.

What is the best way, if possible, to send information from a Java PrintStream to a JTextPane?

In Java, I have a package that translates XML metadata from one standard to another. This package is ultimately accessed through a single function and sends all of its output through a PrintStream object. The output sent is just a status of each file and whether or not it was translated.
This is pretty fine and dandy if I'm just printing to System.out, but I'm actually wanting to print this to a JTextPane while it translates (kind of like a progress text box). It wouldn't be a big deal to just print the status after it was done translating the XML, but since there may be thousands of XML files, that's just not feasible.
One thing that I've tried is to use a thread that takes all of the information from the PrintStream (which is attached to a ByteArrayOutputStream) and let it send any new information to the text pane. Unfortunately, this still sends the information all at once at the end of the translation. This does work correctly for System.out.
Here's the code that does the translation and tries to show the output:
public class ConverterGUI extends javax.swing.JFrame {
boolean printToResultsBox = false;
PrintStream printStream = null;
ByteArrayOutputStream baos = null;
private class ResultsPrinter implements Runnable {
public ResultsPrinter() {
baos = new ByteArrayOutputStream();
printStream = new PrintStream(baos);
}
public void run() {
String tempString = "";
while (printToResultsBox) {
try {
if (!baos.toString().equals(tempString)) {
tempString = baos.toString();
resultsBox.setText(tempString);
}
} catch (Exception ex) {
}
}
}
}
...
ResultsPrinter rp = new ResultsPrinter();
Thread thread = new Thread(rp);
thread.start();
// Do the translation.
try {
printToResultsBox = true;
boolean success = false;
TranslationEngine te = new TranslationEngine();
// fileOrFolderToConvert is a text box in the GUI.
// linkNeeded and destinationFile are just parameters for the translation process.
success = te.translate(fileOrFolderToConvert.getText(), linkNeeded, destinationFile, printStream);
if (success) {
printStream.println("File/folder translation was a success.");
}
resultsBox.setText(baos.toString());
} catch (Exception ex) {
printStream.println("File translation failed.");
} finally {
printToResultsBox = false;
}
...
}
Ultimately, this code prints out to the JTextPane just fine after all the translation is done but not during. Any suggestions? Do I need to change the PrintStream to something else?
The problem with the way your thread works is you are not on the UI event thread when updating your results box. Take a look at the SwingWorker class. Or you could even use the SwingUtilities.invokeAndWait

Display CMD output in my GUI (java)

How might I get the output from a CMD process to display in my GUI? This is the code I'm using to run the process:
try {
String line;
Process p = Runtime.getRuntime().exec("cmd /c \"e:\\folder\\someCommands.cmd\"");
BufferedReader input =
new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
} catch (Exception err) {
err.printStackTrace();
}
I've tried doing this:
jLabel1.setText(line);
...but the GUI is completely locked up while the process is running, so nothing updates until the very end, which isn't very useful. Other than that the CMD works fine. I just want to display the output in real-time.
Did you repaint() after setting the text of the label?
Anyway, you should generally be hesitant to execute a long operation on the GUI event thread. Look into using a SwingWorker instead.
You'll need to start a separate thread to run the process. The code you're using to run it can mostly just be inserted into the thread's (or Runnable's) run() method as is, but to set the text in the JLabel, you should use something like this:
...
while ((line = input.readLine()) != null) {
SwingUtilities.invokeLater(new SetTextRunnable(jLabel1, line));
}
...
class SetTextRunnable implements Runnable {
private String line;
private JLabel jLabel1
public SetTextRunnable(JLabel jLabel1, String line) {
this.jLabel1 = jLabel1;
this.line = line;
}
public void run() {
jLabel1.setText(line);
}
}
EDIT: just noticed something: apparently the class SwingWorker was designed for this sort of thing, so that's another option for you (if it exists in your version of Java).
EDIT to the EDIT: so silly me, I didn't notice SwingWorker was already mentioned in another answer.
In addition to what others have said about multithreading this - you'll also want to read the child process error stream. I believe that (in some instances) if you don't drain the error stream for the process it could cause it to hang.

Categories

Resources