Java JFrame update in loop - java

Before coming here, I have searched all over the web and read dozens of topic talking about this but I can't fix my problem.
I want to show the progress of an upload. In the following code, everything works, except that my JFrame does not update. I am using a technique I found on another topic, but it doesn't seem to work. I think it will be more simple if you take a look at my code (I erased the instructions that aren't related to the problem).
/*
* Correct imports have been done
*/
class GUI extends JFrame {
public JPanel pan;
public GUI(JPanel panel) {
super("Uploading...");
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setPreferredSize(new Dimension(600, 500));
setMinimumSize(new Dimension(600, 500));
setMaximumSize(new Dimension(600, 500));
setDefaultCloseOperation(this.DO_NOTHING_ON_CLOSE);
setLocationRelativeTo(null);
pan = panel;
pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS));
setContentPane(pan);
setVisible(true);
}
}
public class GUIUpload {
private static GUI ui;
public static void main(String args[]) {
JPanel main = new JPanel();
ui = new GUI(main); // create and display GUI
uploadLoop(args, main); // start the upload loop
/*
* After upload is finished
*/
JButton jb = new JButton("Ok");
jb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
ui.setVisible(false);
}
});
ui.getContentPane().add(jb);
ui.getContentPane().repaint();
}
private static void uploadLoop(String[] paths, JPanel monitor) {
/*
* Upload starts here
*/
long transfered;
long size;
InputStream inputStream;
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect("xxxxxx", 21);
boolean success = ftpClient.login("xxxxxx", "xxxxxx");
/*
* Sending
*/
for (int i = 0; i < 4; i++){
if (paths[i] != null){
File localFile = new File(paths[i]);
String remoteFile = "/public_html/papers/" + i + ".pdf";
JLabel label = new JLabel("Uploading...");
ui.getContentPane().add(label);
ui.repaint();
inputStream = new FileInputStream(localFile);
// Monitoring misc
size = localFile.length();
transfered = 0;
int percentage = 0;
// Progress bar
JProgressBar pgb = new JProgressBar();
pgb.setValue(0);
ui.getContentPane().add(pgb);
ui.repaint();
// Upload routine
OutputStream outputStream = ftpClient.storeFileStream(remoteFile);;
byte[] bytesIn = new byte[4096];
int read = 0;
while ((read = inputStream.read(bytesIn)) != -1) {
outputStream.write(bytesIn, 0, read);
transfered += read;
percentage = (int)(transfered * 100.0 / size + 0.5);
System.out.println(percentage);
pgb.setValue(percentage);
ui.repaint();
}
inputStream.close();
outputStream.close();
boolean completed = ftpClient.completePendingCommand();
/*
* End of upload
*/
}
}
} // end try
catch (Exception e){
// Do nothing}
} // end catch
} // end upload method
}
The percentage works fine. The file transfer works fine. The GUI frame only updates after when I repaint it in the main method of the GUIUpload class. When it repaints, I can see that all the labels and progressbars have been correctly added and updated (the progress bars are showing the maximum value.
So.. it's been quite a while that I'm searching how to do this, and I've tried using threads, I've tried a lot of things, but none worked (or I did something wrong when trying them).
Thanks a lot to anyone who will be able to help me out.
Best regards.

Swing is single-threaded. When you perform resource heavy tasks such as file download, you prevent Swing from repainting.
It's unsurprising that raw Threads didn't work as Swing has it's own concurrency features that provide a means of dealing with time-consuming background tasks. Threads were not designed to interact with swing components.
Use a SwingWorker.

Related

Java graphics add method

I'm trying to create a networked poker server client program, I'm currently writing the client side which includes the graphics part, However when I try to add a component to a JPanel in my code when a certain condition is met in the run method, add method doesn't seem to work, however other methods that manipulates the JPanel under the same condition works.
public class PokerClient {
BufferedReader in;
PrintWriter out;
JFrame frame = new JFrame("Poker");
JPanel playerHandPanel;
String serverAddress = "localhost";
String playerName;
Card playerHand1, playerHand2;
public PokerClient() {
// Layout GUI
frame.setSize(1100, 700);
frame.setResizable(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
playerHandPanel = new JPanel(new GridLayout(1, 2));
playerHandPanel.setPreferredSize(new Dimension(600, 300));
playerHandPanel.add(new CardComponent(new Card(3, Suit.CLUB))); //it works here
playerHandPanel.setVisible(true);
frame.add(playerHandPanel, BorderLayout.NORTH);
frame.setVisible(true);
}
/**
* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
private Card constructCard(String line){
int seperator = line.indexOf('/');
int cardNum = Integer.parseInt(line.substring(0, seperator));
Card card;
if(line.substring(seperator+1).startsWith("S")){
card = new Card(cardNum, Suit.SPADE);
} else if(line.substring(seperator+1).startsWith("C")){
card = new Card(cardNum, Suit.CLUB);
} else if(line.substring(seperator+1).startsWith("D")){
card = new Card(cardNum, Suit.DIAMOND);
} else{
card = new Card(cardNum, Suit.HEART);
}
System.out.println(card.toString());
return card;
}
/**
* Connects to the server then enters the processing loop.
*/
private void run() throws IOException {
Socket socket = new Socket(serverAddress, 9050);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
System.out.println(line);
if (line.startsWith("SUBMITNAME")) {
// String name = getName();
// playerName = name;
// out.println(name);
} else if (line.startsWith("p1")) {
playerHandPanel.add(new CardComponent(new Card(4, Suit.SPADE)));//this doesn't work i can't figure out why
playerHandPanel.setBackground(Color.WHITE);//this worked
playerHandPanel.add(new JLabel("is this added"));//this doesn't work either
playerHandPanel.repaint();
}
}
}
public static void main(String[] args) throws Exception {
PokerClient client = new PokerClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}
Several problems jump out:
You're not calling revalidate() on the playerHandPanel after adding or removing a component -- probably a major contributor to your problem.
You're constraining the playerHandPanel's size artificially
And not putting it into a JScrollPane
Your code flaunts Swing threading rules by making major Swing component state changes off of the Swing event thread or EDT
You're using a constraining layout, new GridLayout(1, 2)
Possible solutions:
Yes, do call revalidate() on the playerHandPanel after adding or removing a component. This will tell its layout managers to do their thing.
If you want to use GridLayout, do so in a more flexible way, such as, new GridLayout(1, 0) or new GridLayout(1, 0), depending on if you want to specify the number of columns or rows (the 0 meaning a variable number of columns or rows)
Consider using a JList or JTable, two components that are much easier to add things to.
Do learn and follow Swing threading rules, including only making Swing state changes (such as adding or removing components, changing background color...) on the Swing event thread.

Java: access user Generated Code from Library

I am making a Library for my pupils to create a GUI easy in Java,
but if they press a button in the Interface it should run a method created by them.
Is there any way to do this?
I am not really into Java, but the curriculum wants me to:(
You can use reflection to run their class without requiring they implement an interface although you would still have to provide instructions to limit the argument types. This works only for methods with no arguments.
java.awt.EventQueue.invokeLater(() -> {
JFrame frm = new JFrame();
JPanel pnl = new JPanel();
frm.add(pnl);
pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS));
pnl.add(new JLabel("Class"));
JTextField classField = new JTextField();
pnl.add(classField);
pnl.add(new JLabel("Method"));
JTextField methodField = new JTextField();
pnl.add(methodField);
JTextArea area = new JTextArea();
area.setPreferredSize(new Dimension(300, 300));
JButton btn = new JButton("Run");
pnl.add(btn);
pnl.add(area);
System.setOut(new PrintStream(new OutputStream() {
#Override
public void write(int b) throws IOException {
area.append(new String(new byte[]{(byte) b}));
}
}));
btn.addActionListener(e -> {
try {
Class cls = Class.forName(classField.getText());
Method m = null;
Method ma[] = cls.getDeclaredMethods();
String methodName = methodField.getText().trim();
m = cls.getMethod(methodName,new Class[]{});
Object o = cls.newInstance();
Object mr = m.invoke(o);
if(null != mr) {
area.append("\nreturned "+mr.toString()+"\n");
}
} catch (Exception ex) {
ex.printStackTrace();
area.append("\nException "+ex.getMessage()+"\n");
}
});
frm.pack();
frm.setVisible(true);
});
given a class like:
public class StudentClass {
public void print10() {
int sum= 0;
for(int i = 0; i< 10; i++ ) {
System.out.println("i = "+i);
sum+=i;
}
}
}
The fields would be need to be filled with StudentClass and print10 and the compiled class needs to be on your classpath.
Perhaps a better option would be to teach Processing (https://processing.org/). This is essentially java since the Processing code gets pasted into a java class behind the scenes but is much more oriented to get beginners drawing graphical sketches. I guess you'd have to ask your administration if they would go for it and at some point the students would need to be able to write the code the processing tools are generating for them.

How to set the jProgressBar working according to the time taken for a task in java

I m developing application that encrypt decrypt files, i want to use a jProgressBar according to the real time taken to the process.
To make the jProgressBar work according to the time taken for a task, you should use SwingWorker to perform the task in a separate thread.
How SwingWorker works?
SwingWorker is designed for situations where you need to have a long running task run in a background thread and provide updates to the UI either when done, or while processing. Subclasses of SwingWorker must implement the doInBackground() method to perform the background computation.
Workflow
There are three threads involved in the life cycle of a SwingWorker :
Current thread: The execute() method is called on this thread. It schedules SwingWorker for the execution on a worker thread and returns immediately. One can wait for the SwingWorker to complete using the get methods.
Worker thread: The doInBackground() method is called on this thread. This is where all background activities should happen. To notify PropertyChangeListeners about bound properties changes use the firePropertyChange and getPropertyChangeSupport() methods. By default there are two bound properties available: state and progress.
Event Dispatch Thread: All Swing related activities occur on this thread. SwingWorker invokes the process and done() methods and notifies any PropertyChangeListeners on this thread.
Often, the Current thread is the Event Dispatch Thread.
So, to develop an application that encrypts and decrypts files, you can use a SwingWorker to perform the encryption/decryption and jProgressBar according to the real time taken to the process.
Sample output:
Code:
public class Test extends JPanel implements ActionListener,
PropertyChangeListener {
private JProgressBar progressBar;
private JButton startButton;
private JTextArea encryptedOutput;
private JTextArea decryptedOutput;
private Task task;
Key aesKey;
Cipher cipher;
String key = "Code1234Code1234"; // 128 bit key
class Task extends SwingWorker<Void, Void> {
#Override
public Void doInBackground() {
int progress = 0;
// Initialize progress property.
setProgress(0);
try {
JFileChooser fileopen = new JFileChooser();
FileFilter filter = new FileNameExtensionFilter("txt files",
"txt");
fileopen.addChoosableFileFilter(filter);
File file = null;
String fileName = "";
int ret = fileopen.showDialog(null, "Open file");
if (ret == JFileChooser.APPROVE_OPTION) {
file = fileopen.getSelectedFile();
fileName = file.getName();
encryptedOutput.setText(String.format(
"Encrypted Output : %s\t\n\n", fileName));
decryptedOutput.setText(String.format(
"Decrypted Output : %s\t\n\n", fileName));
BufferedReader in = new BufferedReader(new FileReader(file));
String line = in.readLine();
long readLength = 0;
long totalLength = file.length();
double lengthPerPercent = 100.0 / totalLength;
// added dummy delay because encryption takes very less time
// in MVCE
Thread.sleep(1000);
while (line != null) {
try {
Thread.sleep(100);
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(line.getBytes());
encryptedOutput.append(String.format("%s\n",
new String(encrypted)));
// decrypt the text
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(
cipher.doFinal(encrypted));
decryptedOutput.append(String.format("%s\n",
decrypted));
// calculate progress.
readLength += line.length();
progress = (int) (lengthPerPercent * readLength);
setProgress(Math.min(progress, 100));
line = in.readLine();
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/*
* Executed in event dispatching thread
*/
#Override
public void done() {
setProgress(100);
Toolkit.getDefaultToolkit().beep();
startButton.setEnabled(true);
// turn off the wait cursor
setCursor(null);
}
}
public Test() {
super(new BorderLayout());
// Create key and cipher
aesKey = new SecretKeySpec(key.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES");
} catch (Exception e) {
e.printStackTrace();
}
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
encryptedOutput = new JTextArea(20, 50);
encryptedOutput.setMargin(new Insets(5, 5, 5, 5));
encryptedOutput.setEditable(false);
encryptedOutput.setLineWrap(true);
decryptedOutput = new JTextArea(20, 50);
decryptedOutput.setMargin(new Insets(5, 5, 5, 5));
decryptedOutput.setEditable(false);
decryptedOutput.setLineWrap(true);
startButton = new JButton("Start Encoding");
startButton.setActionCommand("start");
startButton.addActionListener(this);
JPanel panel = new JPanel();
add(panel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.LINE_START);
add(new JScrollPane(encryptedOutput), BorderLayout.LINE_START);
add(new JScrollPane(decryptedOutput), BorderLayout.LINE_END);
panel.add(startButton, BorderLayout.PAGE_END);
setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));
}
public void actionPerformed(ActionEvent evt) {
startButton.setEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
}
/**
* Invoked when task's progress property changes.
*/
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("progress")) {
int progress = (Integer) evt.getNewValue();
progressBar.setValue(progress);
encryptedOutput.append("\n\n");
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new Test();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Related Documentation:
Worker Threads and SwingWorker

ProgressMonitor or ProgressBar for monitoring multiple xml files upload

I am trying to find a way if possible, of displaying a progressBar or progressMonitor while reading a huge number of xml files for making a spreadsheet of files to modify without the user having to open every single xml file and modify the content which might seem complex the user, that I have done and its working fine now I m stuck with show a progressBar to user unlike making the user wait with nothing indicating that something is in progress, when uploading many files, it takes close to ten seconds or more. Your shedding of some light is highly appreciated, thank you in advance.
public Vector<Vector<SpreadSheetCell>> returnDynamicTrees(int h,int w)
{
//Display thread shows the JProgress Bar for this time consuming method
new Thread(new display()).start();
long ready = System.currentTimeMillis();
System.out.println("Taking time ?");
Vector<Vector<SpreadSheetCell>> dynamicTrees = new Vector<Vector<SpreadSheetCell>>();
Vector<String> columns= columnHeaders(0,validDocs.size(),rootName);
//Data reading here.
//This where too much delay is happening
//I thought of putting this for loop in a thread
for(int j=0;j<fileNames.length;j++)
{
dynamicTrees.add(new Vector<SpreadSheetCell>());
for(int i=0;i<columns.size();i++)
{
dynamicTrees.elementAt(j).add(new SpreadSheetCell(directoryPath+"/"+fileNames[j],columns.get(i),i,j,h,w));
}
}
long done= System.currentTimeMillis();
System.out.println(set);
System.out.println("Execution time for ReturnDynamicTree "+(done-ready)+" ms.");
return dynamicTrees;
}
//////Method for display JProgressBar
public static void displayProgressBar()
{
frame.setSize(300, 100); //Window size 300x100 pixels
pane = frame.getContentPane();//Pane is a Container
pane.add(bar);
bar.setBounds(20, 20, 20, 20); //Bar is JProgressBar
frame.setResizable(false);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
new Thread(new Repaint()).start();
}
static class Repaint implements Runnable{
public void run(){
for (int i=0; i<=100; i++){ //Progressively increment variable i
bar.setValue(i); //Set value
bar.repaint(); //Refresh graphics
try{Thread.sleep(10);} //Sleep 50 milliseconds
catch (InterruptedException err){}
System.out.println();
}
}
}
//Thread for running displayProgressBar in a thread to avoid more delay
static class display implements Runnable{
public void run(){
displayProgressBar();
frame.repaint();
}
}
Use a JProgressBar
progressBar = new JProgressBar(0, numberOfFiles);
do a progressBar.setValue when each file upload completes.
Assuming you are doing an upload one by one.
Use a javax.swing.ProgressMonitorInputStream. Encapsulates a JProgressBar with a FilterInputStream.

Progress bar in Swing (Java) for command tools

I have several C/C++ command line tools that I'm wrapping with Java.Swing as GUI. The command line tools can take minutes to hours. Progress bar seems like a good idea to keep users sane. I'm also thinking it might be nice to wrap a GUI for the progress bar, instead of just using system out. But how?
I'm thinking the command line tools can write percents to stderr and I can somehow read it in java. Not exactly sure what the mechanics for this would be. I'm also not clear on asynchronous display (learned a bit about invokeLater() ). New to Java, and would appreciate general suggestions as well. Thanks.
--- update ---
Thanks everyone for your suggestions. Here's the resulting code.
private void redirectSystemStreams() {
OutputStream out_stderr = new OutputStream() {
#Override
public void write(final int b) throws IOException {
update(String.valueOf((char) b));
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
update(new String(b, off, len));
}
#Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
};
System.setErr(new PrintStream(out_stderr, true));
}
private void update(final String inputText) {
int value = 20; //parse inputText; make sure your executable calls fflush(stderr) after each fprintf().
jProgressBar.setValue(value);
/* Also one can redirect to a textpane
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//update jTextPane with inputText
}
});
*/
}
That's seems very fragile, better would be to communicate via sockets in a well established protocol or with some sort of RCP ( perhaps Google's protobuf ) or even webservices.
If you still insists you can launch a process in Java with ProcessBuilder that will give you a Process reference of which you can get the InputStream to read the standard output, but again, that seems very fragile to me.
I hope this helps.
For the progress bar part of your problem you can do something like the following. Note that this is just an example to illustrate the point.
Basically, a thread is created to do the work. Presumably this Runner thread will be interacting with your C/C++ code to get its progress. It then calls update on the Progress Bars Dialog class.
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class Main {
private int value;
private Progress pbar;
public static void main(String args[]) {
new Main();
}
public Main() {
pbar = new Progress();
Thread t = new Thread(new Runner());
t.start();
}
class Progress extends JDialog {
JProgressBar pb;
JLabel label;
public Progress() {
super((JFrame) null, "Task In Progress");
pb = new JProgressBar(0, 100);
pb.setPreferredSize(new Dimension(175, 20));
pb.setString("Working");
pb.setStringPainted(true);
pb.setValue(0);
label = new JLabel("Progress: ");
JPanel panel = new JPanel();
panel.add(label);
panel.add(pb);
add(panel, BorderLayout.CENTER);
pack();
setVisible(true);
}
public void update(){
pb.setValue(value);
if(value >= 100){
this.setVisible(false);
this.dispose();
}
}
}
class Runner implements Runnable {
public void run() {
for (int i = 0; i <= 100; i++) {
value++;
pbar.update();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
}
}
// Create a window
JFrame frame = new JFrame("Progress");
// Creates a progress bar and add it to the window
JProgressBar prog = new JProgressBar();
frame.add(prog);
// Run C/C++ application
try {
Process p = Runtime.getRuntime().exec(new String[]{"filename","arg1","arg2","..."});
// Get InputStream
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
// Update the progress when recieving output from C/C++
new java.util.Timer().schedule(new TimerTask(){
public void run(){
String str = "";
while ((str=br.readLine()!=null) {
prog.setValue(new Integer(str)); // Set Value of Progress Bar
prog.setString(str+"%"); // Set Value to display (in text) on Progress Bar
}
}
},0,100); // Check every 100 milliseconds
// Fit the window to its contents and display it
frame.pack();
frame.setVisible(true);
} catch (Exception e) {
System.out.println("Failed To Launch Program or Failed To Get Input Stream");
}

Categories