JProgressBar not updating from SwingWorker, while debugs work fine - java
This has been driving me up the wall for 2 days now. I can't seem to make the JProgressBar update properly and I'm not sure why. I've read through the concurrency and SwingWorker tutorials, I've tested outside of my program and been able to get it working fine using simple code. I understand the concepts, and am just not seeing why it's not working as the code has been re-written in many different ways and still I can't make it work. I'll post a SSCE using as much code from my program as I can, and explain things best I can here as well.
Basically I have a auto-updater/Installer program to update and/or install World of Warcraft addons to our customers computers. The first thing the 'updater' does is check the server to see if it's version is the same as the version of the updater installer on the server. This works swimmingly! In fact the entire updater works great except for this one issue. If the updater sees that the server has a different version number than the updater it downloads the installer (in a zip) from the server, unzips it, starts the installation from the exe that was contained in the zip and closes the currently open updater so the install can complete properly.
So I have a progress bar on the card (using cardLayout to show the three different cards for the updater, SelfUpdate, Login, and AddonList) for the self updater. as well as a JTextArea below the JProgressBar and that's all that shows on the particular SelfUpdate card. It query's the server for the version number on the server and it compares that version to the version number on the updater currently installed. If the version is the same it changes to the Login card and moves along to the next bits of the program, which work great so far, if the versions are different it sends a query to the server and the server sends the zip file back to the updater for processing.
This is where it's not working. The zip is a decent size and of course I want to show the progress of this download. The zip file download works fine (it runs in the SwingWorker doInBackground() method), the updater then unzips the installer.exe file to a folder on the customers system drive, fires off using Runtime and starts the installer.exe which pops up the installer screen, then closes the current updater using System.exit(0). again, all this works but the JProgressBar itself will NOT update. it sits at 0% for the whole thing. Also, the taskOutput JTextArea doesn't get updated either, but the main problem is the progressbar not updating. I can live with that getting fixed and the JTextArea not updating. (tried a JLabel in place of a textarea as well and no joy)
So I know I'm doing something wrong, but I can't imagine what. Here's the bits where the magic happens. :) Forgive me if I explain as I go along here.
So Controller.java handles all the flow of the program here's the parts you need to see.
// Imports packages needed for use in this file.
import javax.swing.*;
import java.awt.CardLayout;
import java.io.*;
// class instantiation
public class Controller {
// Variables needed for use in this class
private static Updater util;
private static int loginReturnState = 0;
/**
* Single contructor for this class
*/
public Controller() {
}
/**
* Main method which runes everything in the updater program
*
*/
public static void main(String[] args) {
util = new Updater();
// Schedule a job for the event dispatch thread.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// setup a new GUI
util.createGUI();
JFrame rFrame = util.getRootFrame();
JPanel gFrame = util.getCardLayoutFrame();
CardLayout cards = (CardLayout) gFrame.getLayout();
cards.show(gFrame, "UpdateUpdaterPanel");
rFrame.pack();
rFrame.setLocationRelativeTo(null);
rFrame.setVisible(true);
SelfUpdater selfUp = new SelfUpdater();
int needUpdate = selfUp.checkForUpdate(); if (needUpdate == 2) {// Update available for download. Download it.
selfUp.downloadUpdate();
}
}
});
}
}
OK Next file is the Updater class. To put code in here that will hopefully compile and show the issue I need to put this class in there or the utility methods I have in the uPdater class will not be around to be called and I use them extensively.
// Packages needed for use in this file
import javax.swing.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.CardLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseAdapter;
import java.io.*;
// Class instantiation
public class Updater {
// These final variables are set to the path for each of the images used
public static final String BG_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\background.png";
public static final String APP_ICON_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\app-icon.png";
public static final String CLOSE_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\close.png";
public static final String FINOFF_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\FinishedOff.png";
public static final String FINON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\FinishedOn.png";
public static final String ICON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon.png";
public static final String ICON64_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon64x64.png";
public static final String ICON256_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon256x256.png";
public static final String INSTOFF_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\InstallUpdatesOFF.png";
public static final String INSTON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\InstallUpdatesON.png";
public static final String LOGO_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\logo.png";
public static final String MIN_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\minimize.png";
public static final String PROGBACK_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\ProgressBackground.png";
public static final String PROGBAR_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\ProgressBar.png";
public static final String INSTRUCTIONS_BUTTON = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\instructions.png";
public static final String PURCHASE_BUTTON = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\Purchase.png";
// These are various final variables for use throughout the updater
public static final String URL_CONNECT = "http://dynastyaddons.com/api/index2.php?";
public static final String TITLE_TEXT = "Dynasty Updater";
public static final int BUFFER_SIZE = 4096;
public static final double UPDATER_VERSION = 1.1;
// Public variables needed for this class workings
private static JFileChooser fc;
private static JFrame rootFrame;
// Private variables for this class use alone
private static String PHPSessionID;
private static boolean debugToggle = true;
private CardLayout cards;
private JPanel guiFrame;
private Point mouseDownCompCoords;
private Login login;
private Addons addons;
/**
* Sole contructor. (For invocation by subclass contructors, typically implicit.)
*/
public Updater() {
// First instantiate any variables needed
PHPSessionID = "notset";
guiFrame = new JPanel();
cards = new CardLayout();
guiFrame.setLayout(cards);
}
// Various methods follow
/**
* This will create the root gui for showing in the main method
*
*/
public void createGUI() {
rootFrame = new JFrame();
SpringLayout rootLayout = new SpringLayout();
// setup root frame
rootFrame.setUndecorated(true);
rootFrame.setBackground(new Color(0,0,0,0));
rootFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rootFrame.setPreferredSize(new Dimension(800,600));
rootFrame.setTitle(TITLE_TEXT);
// add the background image
JLabel bg = new JLabel(new ImageIcon(BG_IMAGE_PATH));
bg.setLayout(rootLayout);
rootFrame.add(bg);
// setup title bar
mouseDownCompCoords = null;
mouseDownCompCoords = null;
rootFrame.addMouseListener(new MouseListener() {
public void mouseReleased(MouseEvent e){ mouseDownCompCoords = null; }
public void mousePressed(MouseEvent e){ mouseDownCompCoords = e.getPoint(); }
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){ }
public void mouseClicked(MouseEvent e){ }
});
rootFrame.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e){}
public void mouseDragged(MouseEvent e){
Point currCoords = e.getLocationOnScreen();
rootFrame.setLocation(currCoords.x - mouseDownCompCoords.x, currCoords.y - mouseDownCompCoords.y);
}
});
// Display and place the logo
JLabel logo = new JLabel(new ImageIcon(LOGO_IMAGE_PATH));
bg.add(logo);
// add Close and Minimize buttons
JPanel cmButt = createCloseAndMinButtons();
bg.add(cmButt);
// create card frame
guiFrame.setPreferredSize(new Dimension(800, 435));
guiFrame.setOpaque(false);
bg.add(guiFrame);
// Constrain the parts of your BG
rootLayout.putConstraint(SpringLayout.WEST, logo, 30, SpringLayout.WEST, rootFrame);
rootLayout.putConstraint(SpringLayout.NORTH, logo, 70, SpringLayout.NORTH, rootFrame);
rootLayout.putConstraint(SpringLayout.NORTH, cmButt, 0, SpringLayout.NORTH, rootFrame);
rootLayout.putConstraint(SpringLayout.EAST, cmButt, 0, SpringLayout.EAST, rootFrame);
rootLayout.putConstraint(SpringLayout.SOUTH, guiFrame, 0, SpringLayout.SOUTH, rootFrame);
rootLayout.putConstraint(SpringLayout.WEST, guiFrame, 0, SpringLayout.WEST, rootFrame);
rootLayout.putConstraint(SpringLayout.EAST, guiFrame, 0, SpringLayout.EAST, rootFrame);
// Create self updater panel and get it ready to show
SelfUpdater selfUp = new SelfUpdater();
JPanel selfUpd = selfUp.getSelfUpdatePanel();
// get the cardlayout and add the three panels to it, then show the self-updater to begin.
guiFrame.add(selfUpd, "UpdateUpdaterPanel"); }
/**
* Creates a panel which has the close and minimize buttons on it.
*/
private static JPanel createCloseAndMinButtons() {
JPanel a = new JPanel();
SpringLayout closeLO = new SpringLayout();
a.setLayout(closeLO);
a.setPreferredSize(new Dimension(150,150));
a.setOpaque(false);
// Close Button
JButton cButt = new JButton(new ImageIcon(Updater.CLOSE_IMAGE_PATH));
cButt.setBorderPainted(false);
cButt.setContentAreaFilled(false);
cButt.setFocusPainted(false);
cButt.setOpaque(false);
cButt.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) { System.exit(0); }
});
a.add(cButt);
// Minimize Button
JButton mButt = new JButton(new ImageIcon(Updater.MIN_IMAGE_PATH));
mButt.setBorderPainted(false);
mButt.setContentAreaFilled(false);
mButt.setFocusPainted(false);
mButt.setOpaque(false);
mButt.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) { rootFrame.setState(JFrame.ICONIFIED); }
});
a.add(mButt);
// Constrain parts on panel
closeLO.putConstraint(SpringLayout.EAST, cButt, 12, SpringLayout.EAST, a);
closeLO.putConstraint(SpringLayout.NORTH, cButt, -2, SpringLayout.NORTH, a);
closeLO.putConstraint(SpringLayout.EAST, mButt, 30, SpringLayout.WEST, cButt);
closeLO.putConstraint(SpringLayout.NORTH, mButt, -2, SpringLayout.NORTH, a);
// Return the jframe
return a;
}
/**
* Returns the guiFrame which holds the cardlayout for the changing of the panels.
*
* #return JPanel Returns the frame which holds the cardlayout.
*/
public JPanel getCardLayoutFrame() {
return guiFrame;
}
/**
* This will return the cardlayout for use in the controller.
*
* #return CardLayout Returns the cardlayout for use in the Controller.
*/
public CardLayout getCardLayout() { return cards; }
/**
* Returns the current version of this updater as a double
*
* #return the version of this updater as a double.
*/
public double getUpdaterVersion() { return UPDATER_VERSION; }
/**
* Returns a string that tells if the customer is useing windows or mac
*
* #return the operating system in use by the customer
*/
public static String getOSType() {
String retString = "";
String oST = System.getProperty("os.name").toLowerCase();
int spacePos = oST.indexOf(" ");
if (spacePos > 0) {
retString = oST.substring(0, spacePos);
}
retString.trim();
return retString;
}
/**
* Returns the main root frame for display purposes in controller.java
*
* #return JFrame returns rootFrame.
*/
public JFrame getRootFrame() { return rootFrame; }
}
OK< Keep in mind this class really just does some utility stuff in the background and likely isn't involved in anything other than creatin the background gui and tellin the SelfUpdate card to show up.
Finally here's the SelfUpdater.java file which displays the SelfUpdater panel with the progress bar in it and does all the server querying which works fine again. Pay attention to the System.out.println debug statements I have in the downloadUpdate method (which is called in the controller file), as they work fine and print out in the console no problem. The selfUpdateProgress.setValue(int) and taskOutput.setText(txt) statements in that method do nothing .Also I've put a println in the doInBackground where it does setProgress(int) and that prints to the console fine but the bar doesn't update with setProgress. Here it is.
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.util.zip.*;
// Create and start class
public class SelfUpdater extends JPanel {
// These are the variables needed for use in this class
private JPanel selfUpdatePanel;
private JProgressBar selfUpdateProgress;
private Task task;
private JTextArea taskOutput;
private Updater util;
/**
* Default class constructor, instantiates variables and sets up GUI.
*
*
**/
public SelfUpdater() {
// Progress Bar
selfUpdateProgress = new JProgressBar(0, 100);
selfUpdateProgress.setValue(0);
selfUpdateProgress.setStringPainted(true);
selfUpdateProgress.setPreferredSize(new Dimension(470,29));
// Layout
SpringLayout selfUpdateLayout = new SpringLayout();
SpringLayout pBarLayout = new SpringLayout();
// Font and color setup for Task Output
Font myFont = new Font("Serif", Font.BOLD, 16);
Color myColor = new Color(255, 170, 0);
// Description of progress pane
taskOutput = new JTextArea("Checking server for updates to the updater.", 1, 39);
taskOutput.setFont(myFont);
taskOutput.setForeground(myColor);
taskOutput.setEditable(false);
taskOutput.setOpaque(false); // this is false after setting up the window.
//extras needed for the class workings
util = new Updater();
// Images for progress bar setup
JLabel pBar = new JLabel(new ImageIcon(util.PROGBACK_IMAGE_PATH));
pBar.setOpaque(false);
//pBar.setLayout(pBarLayout);
//pBar.add(taskOutput);
//pBar.add(selfUpdateProgress);
// Main panel
selfUpdatePanel = new JPanel();
selfUpdatePanel.setPreferredSize(new Dimension(800, 435));
selfUpdatePanel.setLayout(selfUpdateLayout);
selfUpdatePanel.setOpaque(false);
selfUpdatePanel.add(taskOutput);
selfUpdatePanel.add(selfUpdateProgress);
selfUpdatePanel.add(pBar);
// Constrain your bits.
// First constrain PBar to the self update panel
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, pBar, -40, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, pBar, 150, SpringLayout.WEST, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, taskOutput, -50, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, taskOutput, 175, SpringLayout.WEST, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, selfUpdateProgress, -84, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, selfUpdateProgress, 171, SpringLayout.WEST, selfUpdatePanel); }
/**
* Will return the jpanel that contains the gui elements for the Self-Update
*
* #return JPanel The Self-Update gui.
*
*/
public JPanel getSelfUpdatePanel() { return selfUpdatePanel; }
/**
* This will check the server to see if an update is available for the updater
*
* #return int Returns an int that will describe the answer from the server.
* Possible Return values:
* 0 = Problem checking for update: Server return NULL
* 1 = Second type of problem checking for update: Server returned empty string
* 2 = Update available.
* 3 = Update not needed.
*
*/
public int checkForUpdate() {
// ask the server what version it's updater is.
String serverUpdaterVersion = "";
try {
HttpConnect conn = new HttpConnect();
String urlRequest = util.URL_CONNECT + "action=get_updater&platform=" + util.getOSType();
serverUpdaterVersion = conn.textRequest(urlRequest, 1);
} catch (MalformedURLException e) {
JOptionPane.showMessageDialog(null, "We were unable to connect to the server while checking for an updater update, please check your internet connection.", "Attention!",JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "We were unable to open the connection to the server while checking for an updater update, please check your internet connection.", "Attention!",JOptionPane.ERROR_MESSAGE);
}
// check for errors and return proper int.
if (serverUpdaterVersion == null) { // There is an error if the server returns null, return 0
return 0;
} else if (serverUpdaterVersion.equals("")) { // if the server returns an empty string return 1 for an error
return 1;
} else { // not null or empty string. check versions
// Check version of this updater to servers version
double sVers = Double.parseDouble(serverUpdaterVersion);
if((double)sVers == util.getUpdaterVersion()) { // return 3 if no update needed here.
return 3;
} else { // otherwise return 2 so we can get the update
return 2;
}
}
}
/**
* This will download the update from the server.
*
* #return File The downloaded file from the server. A zip file usually.
*
*/
public void downloadUpdate() {
// turn on wait cursor
util.getRootFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// start a new task which does the download
// Instances of javax.swing.SwingWorker are not reusuable, so
// we create new instances as needed.
task = new Task();
// add a property listener to the task so it knows to update the progress meter.
task.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
switch(evt.getPropertyName()) {
case "progress":// set the progress value and the task output value.
selfUpdateProgress.setIndeterminate(false);
int prg = (Integer) evt.getNewValue();
selfUpdateProgress.setValue(prg);
taskOutput.setText("Downloading and unzipping update. Progress = " + prg + "%");
System.out.println("Percent progress: " + prg + "%");
break;
}
}
});
// execute or start the task.
task.execute();
}
/**
* This method will unzip the downloaded file, and start the installation.
* Then it will close this program while the installation completes.
*
*
*/
public void unzipNewUpdater() {
// create strings for naming the zip file to open up and where to put it.
String zipFile = "NewUpdater.zip";
String outputFolder = System.getenv("SystemDrive") + "\\DynastyAddons";
// Try the unzipping and catch any errors.
try {
// Setup the input stream and get the entry ready to unzip.
ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry ze = zis.getNextEntry();
// loop to open each entry and output it to the output folder and file.
while(ze!=null) {
// Get the name
String entryName = ze.getName();
// get the file to unzip
File f = new File(outputFolder + File.separator + entryName);
// Create folders needed to hold the unzipped stuff
f.getParentFile().mkdirs();
// get output stream setup to copy the file to from the zip
FileOutputStream fos = new FileOutputStream(f);
// get length of file to make sure we get the whole thing moved
int len;
byte buffer[] = new byte[1024];
// loop thru the entry writing the whole file to the output directory
while((len = zis.read(buffer)) > 0){
fos.write(buffer, 0, len);
}
// close the output stream and get the next entry ready for the next iteration of the loop.
fos.close();
ze = zis.getNextEntry();
}
// close the entry and input stream since we're done.
zis.closeEntry();
zis.close();
} catch (FileNotFoundException ex) {
// throw error
} catch (IOException ex) {
// throw error
}
}
// This private class handles the download and unzip of the new updater if needed.
class Task extends SwingWorker<Void, Void> {
#Override
public Void doInBackground() {
try {
HttpConnect conn = new HttpConnect();
String downloadURL = util.URL_CONNECT + "action=download_updater&platform=" + util.getOSType();
conn.downloadFile(downloadURL);
InputStream inputStream = conn.getInputStream();
FileOutputStream outputStream = new FileOutputStream(System.getenv("SystemDrive") + "\\DynastyAddons\\NewUpdater.zip");
byte[] buffer = new byte[util.BUFFER_SIZE];
int bytesRead = -1;
long totalBytesRead = 0;
int percentCompleted = 0;
long fileSize = conn.getContentLength();
taskOutput.setText("Downloading Newest Updater.");
//setProgress(0);
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
percentCompleted = (int) (totalBytesRead * 100 / fileSize);
setProgress(percentCompleted);
}
outputStream.close();
conn.disconnect();
} catch (IOException ex) {
JOptionPane.showMessageDialog(util.getRootFrame(), "Error downloading Updater file. Please send this entire error to support#dynastyaddons.com. \n Error: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
setProgress(0);
}
return null;
}
#Override
public void done() {
// Create msg window saying update downloaded.
JOptionPane.showMessageDialog(util.getRootFrame(), "The new updater has been downloaded. \nClick OK to close this updater and start the installation of the new version.", "Attention", JOptionPane.PLAIN_MESSAGE);
// Unzip the updater
unzipNewUpdater();
// Tell the cursor we're not busy anymore
setProgress(100);
selfUpdateProgress.setValue(100);
taskOutput.setText("Done Downloading New Updater. Installing...");
util.getRootFrame().setCursor(null);
// Create command used to open the udpater installer.
String cmd = "cmd /c " + System.getenv("SystemDrive") + "\\DynastyAddons\\InstallDynastyAddonsUpdater.exe";
// Here is where we run the exe that will install the new updater.
try{
Runtime.getRuntime().exec(cmd);
}catch (FileNotFoundException ex){
// handle this error
}catch (IOException ex) {
// handle this error
}
// Now close this updater so the new one can install.
System.exit(0);
}
}
}
I'm hopin it'll all compile properly if you need to see it working. If not let me know and i'll update stuff to make it work on a compile. So I've tried a number of things, making the SelfUpdater class implement PropertyChangeListener and implementing PropertyChange(event), I've tried feeding the JProgressBar to a constructor in the Task class. I've tried outputting an Integer in the SwingWorker and using Publish and Process to update the taskOutput and JProgressBar, I've tried re-writing the whole damn class to fix up the code and make it eaiser to pick out issues. I've done tests using the tutorials and some of my code to make it work (they worked great) but every time I come back to my updater code here, the darn progressbar wont update. I'm really prayin someone can give me a bit of insight on this problem so I can wrap up this project and start on something new! This is the very last thing I have to fix (unless there's problems on the Mac) and I'm done it and can go back to testing and getting it distributed to our customers.
Feel more than free to comment on any part of the code as well (anything I maybe did stupid or something) I'm always lookin for critisim on my code especially if I'm not doing something according to good design and.. ugh brain froze forgot the word here. and definitely if more info is needed I can easily just paste in all the code in the 7 classes that make up this updater.
It all compiles, and works fine, except this jprogressbar wont display the progress, even tho I can get it to pring out in the console. Thank you in advance, sorry for the mini-novel here, Haven't had to ask a question yet, and have been able to figure everything eout from other peoples questions and answers, just stumped now and ready to be done with this! :D
I apologize in advance if I've screwed something up in the post here, remember it's my first post and while I've read hundreds of them, I'm sure my noobness has messed something up so feel free to castigate me for my mistakes so I can fix them. :)
EDIT Updated the selfUpdater.java to reflect using of only one spring layout.
What you have there are layout problems, not anything SwingWorker related. The JProgressBar is updating nicely, you just can't see it.
Check your main method. You are creating two instances of SelfUpdater, adding one to your GUI and updating the other.
public static void main(String[] args) {
util = new Updater(); // <-- don't do this, it calls Swing code from a thread that is not EDT
// Schedule a job for the event dispatch thread.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// setup a new GUI
util.createGUI(); // <-- creates an instance of SelfUpdater and adds it to GUI
JFrame rFrame = util.getRootFrame();
JPanel gFrame = util.getCardLayoutFrame();
CardLayout cards = (CardLayout) gFrame.getLayout();
cards.show(gFrame, "UpdateUpdaterPanel");
rFrame.pack();
rFrame.setLocationRelativeTo(null);
rFrame.setVisible(true);
SelfUpdater selfUp = new SelfUpdater(); // <-- creates another instance
int needUpdate = selfUp.checkForUpdate();
if (needUpdate == 2) {// Update available for download. Download it.
selfUp.downloadUpdate();
}
}
});
}
You can't expect components to be shown, if they are not added anywhere. :)
Have you tried to put the updating code of the jProgressBar in an extra thread? http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
I had a similar problem once, the progressbar didn't progress and when it was all done, the progressbar showed 100%.
Related
I need help closing a DialogDisplayer window in a Netbeans Platform application
I am adding to an existing codebase using the Netbeans Platform (14) and its GUI builder to display user selected data to create an output file. The user selects the inputs, then selects to generate the file using a default file name. I want to interrupt the process with a dialog presenting the user with a summary of what they entered, a TextField containing the default file name and the OK - Cancel buttons. I created a DialogDisplayer configured by a DialogDescriptor containing a JPanel which contains the summary info and file name JTextField. This all works, I see the summary data, am able to modify the file name but selecting the OK or Cancel doesn't close the window. Only the X in the upper right will close it. My actionPerformed() method gets called and exercises the code appropriate to the selected button, but just can't figure out how to close the window from there. I tried setting the closing options to null (dd.setClosingOptions(null);) which the API says causes all action to close the window. No dice. I don't see a method to call to close the DialogDisplayer window in the API. I originally thought of using a JDialog but it requires a Frame, which I can't figure out how to get from a org.netbeans.spi.project.ActionProvider, the enclosing class that initiates the request. I have used Swing for more years than I care to admit (since java 1.1) but the Netbeans Platform framework is new to me. Here is my code: private class FileNameDialog extends JPanel implements ActionListener { private final JTextField fileNameField = new JTextField(50); private final JLabel fileNameLabel = new JLabel("File Name"); private final JLabel infoLabel = new JLabel(); private final JPanel entryPanel = new JPanel(new FlowLayout()); public FileNameDialog(String fileName, String info) { infoLabel.setText(info); fileNameField.setText(fileName); setLayout(new BorderLayout()); entryPanel.add(fileNameLabel); entryPanel.add(fileNameField); add(BorderLayout.CENTER, infoLabel); add(BorderLayout.PAGE_END, entryPanel); } #Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals(OK_BUTTON)) { //Replace the file name with what was entered and move on abort = false; //Global field in enclosing class logger.log(Level.INFO, "Setting abort to FALSE for {0}", fileNameField.getText()); } else if (e.getActionCommand().equals(CANCEL_BUTTON)) { abort = true; //Global field in enclosing class logger.log(Level.INFO, "Setting abort to TRUE"); } //Close the Dialog Window Here (somehow) } } /** * Request text entry from the user. Currently used for displaying the * file name and allowing the user to update it. This is the entry point * for all this code. * #param info summary text * #param title window title * #return the user entered String */ private String userRequestDialog(String info, String title, String fileName) { FileNameDialog fileNameDialog = new FileNameDialog(fileName, info); Object [] options = { new JButton ("OK"), new JButton ("Cancel")}; DialogDescriptor dd = new DialogDescriptor (fileNameDialog, title, true, options, null, DialogDescriptor.DEFAULT_ALIGN, null, fileNameDialog); DialogDisplayer.getDefault().notify(dd); //Display the window dd.setClosingOptions(null); //Doesn't seem to have any effect return fileNameDialog.fileNameField.getText(); //FileName to use as process continues } Just for giggles, I tried Object frame = lookup.lookup(JFrame.class); but that comes back as null.
#jjazzboss - You had the answer that solved my problem and you should get the credit. Though it technically didn't answer the question, it allowed me to replace the Netbeans DialogDisplayer with a JOptionPane as in below. I also tried a CustomDialog modeled after the one in https://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html, but the OK and Cancel buttons still didn't close it. I suspect that something in Netbeans is stealing those events because a breakpoint in the listener never got hit (or I screwed up the code). boolean cancelled = false; Frame frame = WindowManager.getDefault().getMainWindow(); while (!cancelled) { String newFileName = JOptionPane.showInputDialog(frame, info, fileName); if (newFileName == null) //OK was not selected { return null; } else if (isValidFileName(newFileName)) { return newFileName; } else { JOptionPane.showMessageDialog( frame, "Sorry, \"" + newFileName + "\" " + "isn't a valid file name.\n" + "Please Try again", "Bad File Name", JOptionPane.ERROR_MESSAGE); } }
How to preserve window state when using keybindings
I'm currently working on a game that's based on a client-server architecture. What basically happens is that one player can create a lobby that others can connect to by providing IP address and port number (so, actually basic Java Sockets). Before the actual game is started, all players are first placed inside a lobby which consists of a JLabel displaying the player count and a JTextArea providing information about people joining or leaving the lobby. Everytime a new player connects or someone leaves the lobby, the host pushes out notifications to the clients for updating their UI correspondingly. The problem is now the following: I want the lobby to be displayable in fullscreen if the user likes (which is also implemented already using keybindings/Input-/Actionmaps). However, when a user changes from window to fullscreen or back the current frame has to be disposed and as a consequence reset to the initial lobby state. I originally wanted to solve this using the class LobbyState which stores all relevant data to be restored. However, somehow this is not working as the frame still displays the initial state of the lobby when changing "window-mode". To give you an insight into the code, this is the relevant code of the Lobby class: private int playerCount = 1; private boolean isHost; public Lobby(Integer windowState, Image image, Boolean isHost, LobbyState lobbyState) { this.isHost = isHost; setDefaultCloseOperation(EXIT_ON_CLOSE); setTitle("Lobby"); setExtendedState(MAXIMIZED_BOTH); setSize(screenDim); setMinimumSize(new Dimension(800, 400)); setIconImage(image); if (windowState == 1) { setUndecorated(true); } else { setUndecorated(false); } setResizable(true); contentPane.setLayout(null); setLayout(null); contentPane.getInputMap(KeyBindings.AFC).put(KeyStroke.getKeyStroke("F11"), "lobby_fullscreen"); contentPane.getActionMap().put("lobby_fullscreen", new EnterFullscreen(this, this.isHost, new LobbyState(playerCount, getLobbyHistory()))); contentPane.setBackground(new Color(253, 205, 136)); setContentPane(contentPane); loadComponents(); loadBounds(); addActionListeners(); if (lobbyState != null) { lblPlayerCount.setText("" + lobbyState.getPlayerCount()); txtLobbyHistory.setText(lobbyState.getLobbyHistory()); } setVisible(true); } My first assumption why F11 triggers the creation of a Lobby with the initial state instead of the current state is, that the ActionMap is created when playerCount is still 1 and getLobbyHistory() doesn't return anything because there actually still is no lobby history. If this is really the problem, is there any possibility to tell the ActionMap to re-read the values of playerCount and getLobbyHistory() whenever F11 is pressed? Would I have to remove the current ActionMap and add a new one or is there a more elegant way to solve this? In case this is of any interest, this is the code that currently performs the toggle between window and fullscreen application: public class EnterFullscreen extends AbstractAction{ private static final long serialVersionUID = 1773898497666165938L; private JFrame frame; private boolean isHost; private LobbyState lobbyState; private Class<?> frameType; private Constructor<?> c; public EnterFullscreen(JFrame frame, boolean isHost, LobbyState lobbyState) { this.frame = frame; this.isHost = isHost; this.lobbyState = lobbyState; try { frameType = frame.getClass(); if (frameType.equals(Lobby.class)) { c = frameType.getConstructor(Integer.class, Image.class, Boolean.class, LobbyState.class); } else { c = frameType.getConstructor(Integer.class, Image.class, Boolean.class); } } catch (SecurityException | NoSuchMethodException e) { e.printStackTrace(); } } #Override public void actionPerformed(ActionEvent e) { try { frame.setVisible(false); if (frame.isUndecorated()) { if (frameType.equals(Lobby.class)) { c.newInstance(0, frame.getIconImage(), isHost, lobbyState); } else { c.newInstance(0, frame.getIconImage(), isHost); } } else { if (frameType.equals(Lobby.class)) { c.newInstance(1, frame.getIconImage(), isHost, lobbyState); } else { c.newInstance(1, frame.getIconImage(), isHost); } } frame.dispose(); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) { e1.printStackTrace(); } } } I'd really appreciate any input on how to solve this and how one would achieve something like this properly. If you feel like there's any missing information or code to solve this, please let me know and I'll try to provide that as soon as possible.
JFrame progressbar doesn't update when downloading [duplicate]
OK so I have the uploader uploading files using the Java FTP, I would like to update the label and the progress bar. Label with the percent text, bar with the percent int value. Right now with the current code only get the 100 and full bar at the end of the upload. During the upload none of them change. here it is: OutputStream output = new BufferedOutputStream(ftpOut); CopyStreamListener listener = new CopyStreamListener() { public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) { System.out.printf("\r%-30S: %d / %d", "Sent", totalBytesTransferred, streamSize); ftpup.this.upd(totalBytesTransferred,streamSize); } public void bytesTransferred(CopyStreamEvent arg0) { } }; Util.copyStream(input, output, ftp.getBufferSize(), f.length(), listener); } public void upd(long num, long size){ int k = (int) ((num*100)/size); System.out.println(String.valueOf(k)); this.d.setText(String.valueOf(k)); //d.setText(String.valueOf(k)); progressBar.setValue(k); }
From the sounds of it (and lacking any evidence to the contree) it sounds like your processing a time consuming action in the Event Dispatching Thread You might like to read Concurrency in Swing for some further insight I'd suggest using a SwingWorker to perform the actual transfer & take advantage of its built in progress support UPDATE after seeing source code Don't mix heavy weight components with light weight components. Change Applet to JApplet, change TextField to JTextField, don't use Canvas use a JPanel or JComponent If you expect other people to read your code, please use proper names for your variables, I have no idea what p is. Your Thread is useless. Rather then starting the thread and using it's run method you simply make your download call within it's constructor. This will do nothing for you... Remove your implementation of MyThread and replace it with public class MyWorker extends SwingWorker<Object, Object> { private URL host; private File outputFile; public MyWorker(URL host, File f) { this.host = host; outputFile = f; } #Override protected Object doInBackground() throws Exception { // You're ignoring the host you past in to the constructor String hostName = "localhost"; String username = "un"; String password = "pass"; String location = f.toString(); //FTPClient ftp = null; ftp.connect(hostName, 2121); ftp.login(username, password); ftp.setFileType(FTP.BINARY_FILE_TYPE); ftp.setKeepAlive(true); ftp.setControlKeepAliveTimeout(3000); ftp.setDataTimeout(3000); // 100 minutes ftp.setConnectTimeout(3000); // 100 minutes ftp.changeWorkingDirectory("/SSL"); int reply = ftp.getReplyCode(); System.out.println("Received Reply from FTP Connection:" + reply); if (FTPReply.isPositiveCompletion(reply)) { System.out.println("Connected Success"); } System.out.println(f.getName().toString()); File f1 = new File(location); in = new FileInputStream(f1); FileInputStream input = new FileInputStream(f1); // ftp.storeFile(f.getName().toString(),in); //ProgressMonitorInputStream is= new ProgressMonitorInputStream(getParent(), "st", in); OutputStream ftpOut = ftp.storeFileStream(f.getName().toString()); System.out.println(ftpOut.toString()); //newname hereSystem.out.println(ftp.remoteRetrieve(f.toString())); OutputStream output = new BufferedOutputStream(ftpOut); CopyStreamListener listener = new CopyStreamListener() { public void bytesTransferred(final long totalBytesTransferred, final int bytesTransferred, final long streamSize) { setProgress((int) Math.round(((double) totalBytesTransferred / (double) streamSize) * 100d)); } #Override public void bytesTransferred(CopyStreamEvent arg0) { // TODO Auto-generated method stub } }; Util.copyStream(input, output, ftp.getBufferSize(), f.length(), listener); return null; } } In your ActionListener of o (??) replace the thread execution code with try { MyWorker worker = new MyWorker(new URL("http://localhost"), file); worker.addPropertyChangeListener(new PropertyChangeListener() { #Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("progress")) { Integer progress = (Integer) evt.getNewValue(); progressBar.setValue(progress); } } }); worker.execute(); } catch (MalformedURLException ex) { ex.printStackTrace(); } Note. You are ignoring the URL you pass to the constructor. http:// is not ftp:// so I doubt this will work...
During the upload you don't see changes to the GUI, because you run the upload and the GUI changes in the same thread. You should start one threayd that does the upload and another one in EDT (Event-Dispatch-Thread) that does the GUI updates. For more info see: The Event Dispatch Thread
You should implement the transfer logic in a SwingWorker, that way the UI will have the chance to present the progress.
Problems cancelling a SwingWorker
Im making a program that is designed to be a backup. i have SwingWorker doing the backup and posting what it's doing to a JTextArea. I want a button to cancel the worker, (there are a unknown amount of them initiallized at one time) so this is what i have for the calling of the swing workers, and the cancel method: package main; import java.io.File; import java.util.ArrayList; import javax.swing.SwingWorker; public class test2 { SwingWorker bw; static ArrayList bgWorker = new ArrayList(); ArrayList al = new ArrayList(); // this is the list of files selected static boolean bwInitiallized = false; public void startBackup() throws Exception { Panel.txtArea.append("Starting Backup...\n"); for (int i = 0; i < al.size(); i++) { /** * THIS IS WHERE I NEED TO CREATE THE FOLDER THAT EACH BACKUP FILE * WILL GO INTO EX: SC2 GOES INTO A FOLDER CALLED SC2 AND RIOT GOES * TO RIOT, ALL WITHIN THE DIRECTORY CHOSEN */ File file = new File((String) al.get(i)); File directory = new File(dir); // dir is gotten by a JFileChooser. bw = new BackgroundWorker(Panel.txtArea, file, directory); bgWorker.add(bw); bwInitiallized = true; bw.execute(); /** * follows to the bottom of the txtarea */ int x; Panel.txtArea.selectAll(); x = Panel.txtArea.getSelectionEnd(); Panel.txtArea.select(1, x); } } public static void cancel() { BackgroundWorker bg; if (bwInitiallized) { bwInitiallized = false; Panel.txtArea.append("Cancelling...\n"); for (int i = 0; i < bgWorker.size(); i++) { bg = (BackgroundWorker) bgWorker.get(i); bg.cancel(true); } Panel.txtArea.append("Canceled backUp!\n"); } else { Panel.txtArea.append("Cannot Cancel! Not Initiallized!\n"); } } } Well, for reasons that i cannot figure out, this does not cancel any of them (as far as i'm aware). the only thing that i can think of is when i do bg = (BackgroundWorker) bgWorker.get(i); bg.cancel(true); it doesnt do what i think it does, but idk. what am i doing wrong? thanks in advance!
I'd create an instance of class FileWorker extends SwingWorker<File, File> for each file and a single instance of class Supervisor extends SwingWorker<Queue<File>, File> to manage them, as suggested in this example. You could allow the user to cancel an individual FileWorker or let the Supervisor cancel them all. An example using cancel() is shown here. In any case, use the appropriate SwingWorker type parameters for safety and critically examine your design as suggested in comments by #Hovercraft.
Java - Console-like web applet [closed]
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers. We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations. Closed 4 years ago. Improve this question Hey, I've been developing an application in the windows console with Java, and want to put it online in all of its console-graphics-glory. Is there a simple web applet API I can use to port my app over? I'm just using basic System.out and System.in functionality, but I'm happy to rebuild my I/O wrappers. I think something along these lines would be a great asset to any beginning Java developers who want to put their work online.
Sure, just make into an applet, put a small swing UI on it with a JFrame with two components - one for writing output to, and one for entering inputs from. Embed the applet in the page.
I did as Lars suggested and wrote my own. import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.awt.Font; public class Applet extends JFrame { static final long serialVersionUID = 1; /** Text area for console output. */ protected JTextArea textArea; /** Text box for user input. */ protected JTextField textBox; /** "GO" button, in case they don't know to hit enter. */ protected JButton goButton; protected PrintStream printStream; protected BufferedReader bufferedReader; /** * This function is called when they hit ENTER or click GO. */ ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { goButton.setEnabled(false); SwingUtilities.invokeLater( new Thread() { public void run() { String userInput = textBox.getText(); printStream.println("> "+userInput); Input.inString = userInput; textBox.setText(""); goButton.setEnabled(true); } } ); } }; public void println(final String string) { SwingUtilities.invokeLater( new Thread() { public void run() { printStream.println(string); } } ); } public void printmsg(final String string) { SwingUtilities.invokeLater( new Thread() { public void run() { printStream.print(string); } } ); } public Applet() throws IOException { super("My Applet Title"); Container contentPane = getContentPane(); textArea = new JTextArea(30, 60); JScrollPane jScrollPane = new JScrollPane(textArea); final JScrollBar jScrollBar = jScrollPane.getVerticalScrollBar(); contentPane.add(BorderLayout.NORTH, jScrollPane); textArea.setFocusable(false); textArea.setAutoscrolls(true); textArea.setFont(new Font("Comic Sans MS", Font.TRUETYPE_FONT, 14)); // TODO This might be overkill new Thread() { public void run() { while(true) { jScrollBar.setValue(jScrollBar.getMaximum()); try{ Thread.sleep(100); } catch (Exception e) {} } } }.start(); JPanel panel; contentPane.add(BorderLayout.CENTER, panel = new JPanel()); panel.add(textBox = new JTextField(55)); textBox.addActionListener(actionListener); panel.add(goButton = new JButton("GO")); goButton.addActionListener(actionListener); pack(); // End of GUI stuff PipedInputStream inputStream; PipedOutputStream outputStream; inputStream = new PipedInputStream(); outputStream = new PipedOutputStream(inputStream); bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "ISO8859_1")); printStream = new PrintStream(outputStream); new Thread() { public void run() { try { String line; while ((line = bufferedReader.readLine()) != null) { textArea.append(line+"\n"); } } catch (IOException ioException) { textArea.append("ERROR"); } } }.start(); } } This below code was in a separate class, "Input", which has a static "inString" string. public static String getString() { inString = ""; // Wait for input while (inString == "") { try{ Thread.sleep(100); } catch (Exception e) {} } return inString; } Through-out the lifespan of the project I will probably maintain this code some more, but at this point - it works :)
As a premier example of a glorious and incredibly useful cnsole-like webapp, please see goosh, the Google Shell. I cannot imagine browsing the Net without it anymore. Granted, there's no source code, but you might get out a bit of its magic by using Firebug or so. Using a TextArea might be a bug-prone approach. Remember that you'll need to do both input and output to this TextArea and that you must thus keep track of cursor position. I would suggest that, if you really do this approach, you abstract away over a plain TextArea (inheritance, maybe?) and use a component that has, e.g. a prompt() to show the prompt and enable input and a also follows the usual shell abstraction of having stdin (an InputStream, that reads from the prompt, but can be bound to, let's say files or so) and stdout and possibly stderr, OutputStreams, bound to the TextArea's text. It's not an easy task, and I don't know of any library to do it.
I remember seenig telnet client applet implementationa around years ago (back when people used telnet). Maybe you could dig them out and modify them.
System.out and System.in are statics and therefore evil. You'll need to go through your program replacing them with non-statics ("parameterise from above"). From an applet you can't use System.setOut/setErr/setIn. Then you're pretty much sorted. An applet. Add a TextArea (or equivalent). Append output to the text area. Write key strokes to the input. Job done.