Accurately summing file sizes to use in a JProgressBar - java

I have a list of files that I'm going to copy over to another location and I want to use a JProgressBar to monitor the progress of the transfer.
The JProgressBar constructor only takes an int for the bounds and File.length returns a long. I know I can cast the long to an int and lose some accuracy, but is this the correct way to do it?
I didn't include any code because it's not really a syntax or coding issue, I'm just not sure if casting a long to an int for use in a progress bar is the correct or accurate enough for my purposes.

I don't really see a problem about progress bar bounds just because actual progress is a percentual value from 0 to 100. You should always be capable to calculate the proportion between processed bytes and the total amount of bytes and that's the actual progress.
I think the right question would be something like: how do I properly update the progress of my progress bar based on files length and total of bytes to be transferred?
Consider this example:
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Demo {
private void createAndShowGUI() {
final JProgressBar progressBar = new JProgressBar();
progressBar.setStringPainted(true);
progressBar.setString("");
final SwingWorker<Void,String> worker = new SwingWorker<Void, String>() {
#Override
protected Void doInBackground() throws Exception {
long totalSize = 0l;
File[] files = new File(System.getProperty("user.dir")).listFiles();
for (File file : files) {
totalSize += file.length();
}
long transferred = 0l;
for (File file : files) {
Thread.sleep(250); // Just for animation purpose
transferred += file.length();
int progress = (int) (transferred * 100l / totalSize);
setProgress(progress);
String text = String.format("%1s%%: %2s / %3s bytes", progress, transferred, totalSize);
publish(text);
}
return null;
}
#Override
protected void process(List<String> chunks) {
progressBar.setString(chunks.get(chunks.size() - 1));
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer)evt.getNewValue());
}
}
});
Action startAction = new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent e) {
worker.execute();
setEnabled(false);
}
};
JPanel content = new JPanel();
content.add(progressBar);
content.add(new JButton(startAction));
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(content);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().createAndShowGUI();
}
});
}
}
Because transferred * 100l / totalSize will be always a value between 0 and 100, you maybe will lose a decimal point accuracy but you definitely won't lose precision casting it to int in order to set progress bar value.

Related

Draw profile of the mic input

I don't know much about sounds digitalization. I am trying to represent the instant profile of the mic input. I know how to get the bits from the mic, but I don't know how to interpret it into a profile. Can anyone help me filling the blank?
package test;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.OutputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
*
* #author François Billioud
*/
public class SoundRecorder extends JFrame {
/** JFrame for the GUI **/
public SoundRecorder() {
super("Sound Recorder");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = getContentPane();
pane.setLayout(new BorderLayout());
pane.add(wavePane = new WavePane(), BorderLayout.CENTER);
pane.add(new JButton(new AbstractAction("ok") {
#Override
public void actionPerformed(ActionEvent e) {
dispose();
}
}), BorderLayout.SOUTH);
setSize(300,300);
setLocationRelativeTo(null);
}
/** Just displays the frame and starts listening **/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SoundRecorder rec = new SoundRecorder();
rec.setVisible(true);
rec.listenToMic();
}
});
}
/** Draws the sound read from the mic **/
private static class WavePane extends JPanel {
private final int[] x = new int[0];
private int[] y = new int[0];
private WavePane() {
setOpaque(true);
setBackground(Color.WHITE);
}
/** updates the data to be displayed **/
public void setData(int[] y) {
this.y = y;
int n = y.length;
this.x = new int[n];
float pas = getWidth()/(float)(n-1);
float xCurrent = 0;
for(int i=0; i<n; i++) {
this.x[i] = Math.round(xCurrent);
xCurrent+=pas;
}
repaint();
}
/** Draws a line that represent the mic profile **/
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2D = (Graphics2D) g;
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2D.drawPolyline(x, y, x.length);
}
}
/** Defines the audio format to be used.
* I know nothing about that and am open to suggestions if needed
*/
private static final AudioFormat format = new AudioFormat(
16000, //Sample rate
16, //SampleSizeInBits
2, //Channels
true,//Signed
true //BigEndian
);
/** Creates a thread that will read data from
* the mic and send it to the WavePane
* in order to be painted.
* We should be using a SwingWorker, but it will do
* for the sake of this demo.
**/
private void listenToMic() {
new Thread(new Runnable() {
#Override
public void run() {
try {
//Open the line and read
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
//checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.err.print("Line not supported");
}
//starts listening
TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();
//sends the stream to the interpreter
AudioInputStream ais = new AudioInputStream(line);
AudioSystem.write(ais, AudioFileFormat.Type.AU, new Interpreter());
} catch (LineUnavailableException | IOException ex) {
System.err.println(ex.getLocalizedMessage());
}
}
}).start();
}
private final WavePane wavePane;
private class Interpreter extends OutputStream {
private int[] y;
#Override
public void write(int b) throws IOException {
//TBD
//Fill y array
}
#Override
public void flush() throws IOException {
//Sends the values found to the panel for drawing
wavePane.setData(y);
}
}
}
I found this link but it didn't help me...
Edit: Ok, from what I understand, each 16 bits is the amplitude for one frequency. I have 2 channels, so I have to read 16 bits every 32 to get the first channel. Now I need to know how many frequencies I am going to read for each frame. Then I think I can draw the profile. Any hint?
If you want to draw the spectrum (energy per frequency over time) of the signal that is coming from the microphone then you might want to read this. Maybe a bit more that you wanted to know but it has the maths you need.
If you want to draw the amplitude (the pressure over time) then check, for instance, this.
The contents of the audio stream, based on your specification of the audio format, will be a PCM stream. PCM stream means that every frame is a value of sound pressure at that moment of time. Each frame will consist of four bytes, two bytes per channel. The first two bytes will be channel 0, the other two -- channel 1. The two bytes will be in big endian format (the more significant byte will come before the less significant byte). The fact that you specified signed as True means that you should interpret the values as being in the range from -32768 to 32767.

running two frames concurrently at the same time

I just want to run two frames concurrently, one contains drag and drop feature which i am using to write file to ftp server and other one to display the progress bar to show how much file is written(uploaded you can say).
But the problem is my progress bar frame remains white i.e, GUI is not done on it.why???
please give me a solution.
here is my first class which is a homePage frame which calls another class called ProgressSample which writes data to the output stream and also updates the progress bar simultaneously.
FTPConnect.java
SwingWorker<Void , Integer> worker = new SwingWorker<Void , Integer>(){
#Override
protected Void doInBackground() throws Exception {
ProgressSample.main(secondLocalFile , bytesIn , inputStream , outputStream);
return null;
}
protected void done(){
ProgressSample.frame.dispose();
}
};
worker.execute();
this is the second class ...
when i call its main(......) method just a frame with white color on it appears nothing is added on it plz plz plz help me with ur suggestions as soon as possible...
ProgressSample.java
package encdec;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
public class ProgressSample
{
static JFrame frame;
JProgressBar progressBar;
public static void main(File file , byte[] bytesIn, InputStream inputStream , OutputStream outputStream)throws IOException
{
ProgressSample obj=new ProgressSample();
obj.redfile(file , bytesIn , inputStream , outputStream);
}
public ProgressSample()
{
frame = new JFrame();
frame.setBounds(100, 100, 400, 200);
frame.getContentPane().setLayout(null);
progressBar = new JProgressBar();
progressBar.setBounds(102, 40, 150, 16);
frame.getContentPane().add(progressBar);
progressBar.setStringPainted(true);
frame.setVisible(true);
}
public void redfile(File f , byte[] bytesIn, InputStream inputStream , OutputStream outputStream)
{
try {
long totalLength = f.length();
double lengthPerPercent = 100.0 / totalLength;
long readLength = 0;
int read;
System.out.println(totalLength);
while ((read = inputStream.read(bytesIn)) != -1) {
readLength += read;
progressBar.setValue((int) Math.round(lengthPerPercent * readLength));
outputStream.write(bytesIn, 0, read);
}
progressBar.setValue(100);
frame.dispose();
JOptionPane.showMessageDialog(null, "Upload Successful");
} catch (Exception e) {
}
}
}

Why does a fireContentsChanged call of a JList freezes the entire GUI?

I had some problems with freezing SWING GUIs when re-rendering a JTable with a custom cell renderer in Java. So I asked the question "Why does a JTable view update block the entire GUI?". The answers pointed to the fact, that a JList without modifying JTable and overwriting doLayout might be a better choice. So I implemented the example with a JList and ran into the same problem: while generating data, everything works fine and the progress bar moves. But when the view is updated, the program freezes and the progress bar stops moving.
Please note, that the sleep statement is there only to let the generation take a longer, more realistic time (reading thousands of data sets via JDBC and create objects out of them takes a lot time). One could remove it and increment the number of generated items. But you can clearly see, that the HTML rendering is quite slow. But I need this colors and the two lines (if not necessarily so many different colors).
So could you please tell me, where my mistake is? I think, that EDT and other work is separated through separate threads and I cannot see any mistke.
Update: I looked around at SO and found this question "https://stackoverflow.com/a/20813122/2429611". There is said:
The more interesting question would be how to avoid that UI blocking, but I don't think that's possible with just Swing, you'll have to implement some lazy loading, or rendering in batches.
This would mean, that I cannot solve my problem. Is this correct?
package example;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
public class ListExample extends AbstractListModel {
static List<DemoObject> internalList = new ArrayList<>();
#Override
public int getSize() {
return internalList.size();
}
#Override
public DemoObject getElementAt(int index) {
return internalList.get(index);
}
public void fireContentsChanged() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
fireContentsChanged(this, 0, -1);
}
});
}
static class MyCellRenderer extends JLabel implements ListCellRenderer<ListExample.DemoObject> {
public MyCellRenderer() {
setOpaque(true);
}
#Override
public Component getListCellRendererComponent(JList<? extends ListExample.DemoObject> list,
ListExample.DemoObject value,
int index,
boolean isSelected,
boolean cellHasFocus) {
setText("<html>" + value.toString()
+ "<br/>"
+ "<span bgcolor=\"#ff0000\">Line 2; Color = " + value.c + "</span>");
Color background;
Color foreground;
// check if this cell represents the current DnD drop location
JList.DropLocation dropLocation = list.getDropLocation();
if (dropLocation != null
&& !dropLocation.isInsert()
&& dropLocation.getIndex() == index) {
background = Color.BLUE;
foreground = Color.WHITE;
// check if this cell is selected
} else if (isSelected) {
background = Color.RED;
foreground = Color.WHITE;
// unselected, and not the DnD drop location
} else {
background = value.c; //Color.WHITE;
foreground = Color.BLACK;
};
setBackground(background);
setForeground(foreground);
return this;
}
}
static class DemoObject {
String str;
Color c;
public DemoObject(String str, int color) {
this.str = str;
this.c = new Color(color);
}
#Override
public String toString() {
return str;
}
}
static JPanel overlay;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Example");
frame.setLayout(new BorderLayout(4, 4));
// Add JTable
final ListExample model = new ListExample();
JList list = new JList(model);
list.setCellRenderer(new MyCellRenderer());
frame.add(new JScrollPane(list), BorderLayout.CENTER);
// Add button
Box hBox = Box.createHorizontalBox();
hBox.add(new JButton(new AbstractAction("Load data") {
#Override
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
#Override
public void run() {
overlay.setVisible(true);
internalList.clear();
System.out.println("Generating data ...");
SecureRandom sr = new SecureRandom();
for (int i = 0; i < 10000; i++) {
internalList.add(
new DemoObject(
"String: " + i + " (" + sr.nextFloat() + ")",
sr.nextInt(0xffffff)
)
);
// To create the illusion, that data are
// fetched via JDBC (which takes a little
// while), this sleep statement is embedded
// here. In a real world scenario, this wait
// time is caused by talking to the database
// via network
if (i%10 == 0) {
try {
Thread.sleep(1);
} catch (Exception e) {
}
}
}
System.out.println("Updating view ...");
model.fireContentsChanged();
overlay.setVisible(false);
System.out.println("Finished.");
}
}).start();
}
}));
hBox.add(Box.createHorizontalGlue());
frame.add(hBox, BorderLayout.NORTH);
// Create loading overlay
overlay = new JPanel(new FlowLayout(FlowLayout.CENTER)) {
#Override
protected void paintComponent(Graphics g) {
g.setColor(new Color(0, 0, 0, 125));
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
overlay.setOpaque(false);
overlay.setBackground(new Color(0, 0, 0, 125));
JProgressBar bar = new JProgressBar();
bar.setIndeterminate(true);
overlay.add(bar);
frame.setGlassPane(overlay);
frame.getGlassPane().setVisible(false);
// Create frame
frame.setSize(600, 400);
frame.setVisible(true);
}
});
}
}
there are three problems (recreating, reseting the model, and custom Renderer stoped to works)
JList (JComboBox hasn't) has an issue by removing more than 999 items, you have to set a new model to JList
see important for ComboBoxModel extends AbstractListModel implements MutableComboBoxModel for setElementAt(to hold current selection)
usage of public void fireContentsChanged() { is wrong, don't see reason to use this way, again is about to replace current, reset the model
. e.g. with success atr runtime and by recrusive testing for/if event (fired)
setModel(new DefaultListModel(list.toArray()) {
protected void fireContentsChanged(Object obj, int i, int j) {
if (!isFired)
super.fireContentsChanged(obj, i, j);
}
});

Different behavior of JDialog when is created by program and by user action

I have 3 clasess : Loader, MyDialog and TEST(with main method). (for code see below)
Everything I want to achieve is create simple dialog with JLabel and JProgressBar, which will notify user about how much time remains to show MyDialog. MyDialog is Jdialog with time consuming operation in constructor (loading data from database etc.).
In code below is model situation. When "MyDialog" is created by main (constant BY_USER is false), everything working exactly i want to. But when i make dialog with button , and instance of MyDialog is created after button press (constant BY_USER is true), Loader is blank white form. It looks like is not completed.
Loader is extending Thread, so i suppose that problem will be in threading (event dispatch thread)? I dont know, what is wrong and how fix it. Please help.
Thanks and sorry for my English.
CLASS TEST :
package test;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class TEST {
public static final boolean BY_USER = false;
public static void main(String[] args) {
if (BY_USER) {
JFrame mainDialog = new JFrame("Main");
JButton show = new JButton("Show MyDialog");
show.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
MyDialog dialog = new MyDialog();
}
});
mainDialog.add(show);
mainDialog.setLocationRelativeTo(null);
mainDialog.setMinimumSize(new Dimension(160, 80));
mainDialog.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainDialog.setVisible(true);
} else {
MyDialog dialog = new MyDialog();
}
}
}
CLASS MyDialog :
package test;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class MyDialog extends JFrame{
public MyDialog() {
super();
// making loader with title, first message and count of steps of operation
Loader loader = new Loader("Loader", "First showed message", 100);
loader.ShowLoader();
// time-consuming operation (loading data from database etc.).
// for clarity replaced with for statement
int j=0;
for(int i=0; i<Integer.MAX_VALUE; i++)
{
j++;
if(j==Integer.MAX_VALUE/100){
// updating loader message and progress bar value
loader.NewAction(Integer.MAX_VALUE - i+"");
j=0;
}
}
// closing loader
loader.DestroyLoader();
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setSize(300, 300);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
CLASS Loader:
package test;
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
public class Loader extends Thread{
private JDialog dialog;
private JLabel message = new JLabel("", SwingConstants.CENTER);
private JProgressBar progressBar = new JProgressBar(0, 100);
private String newMessage;
private double percentForStep;
private int remainingSteps;
public Loader(String taskName, String firstMessage, int steps) {
this.remainingSteps = steps-1;
dialog = new JDialog((Dialog) null, taskName);
dialog.setLayout(new BorderLayout(15, 15));
dialog.add(message, BorderLayout.CENTER);
dialog.add(progressBar, BorderLayout.SOUTH);
message.setText(firstMessage);
percentForStep = 100 / steps;
}
public void ShowLoader()
{
dialog.setMinimumSize(new Dimension(400,120));
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
this.start();
}
public void DestroyLoader(){
dialog.dispose();
this.interrupt();
}
public void NewAction(String newMessage){
this.newMessage = newMessage;
this.remainingSteps--;
Lock.changed = true;
}
public int RemainingStepsCount()
{
return remainingSteps;
}
#Override
#SuppressWarnings({"CallToThreadYield", "SleepWhileInLoop"})
public void run() {
do{
synchronized (Lock.class) {
if (Lock.changed) {
Lock.changed = false;
this.message.setText(newMessage);
this.progressBar.setValue((int)(100-(remainingSteps*percentForStep)));
dialog.repaint();
}
dialog.repaint();
}
}while(true);
}
}
class Lock{
static boolean changed = false;
}
Look to SwingWorker and his use; I think it can help you to solve the problem.

Dynamically Update Tooltip Currently Displayed

I'm trying to got a tooltip which displays the current progress of a task. So I want that the tooltip text change while the tooltip is displayed. But, when I call setToolTipText() the displayed text remains the same until I exit the mouse from the tooltip component and enter again. And call setToolTipText(null) before doesn't change anything.
Indeed it does not update itself, even when resetting the tooltip to null between calls.
So far, the only trick I found was to simulate a mouse-move event and forward it on the TooltipManager. It makes him think that the mouse has moved and that the tooltip must be relocated. Not pretty, but quite efficient.
Have a look at this demo code which displays a progress in % from 0 to 100:
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
public class TestTooltips {
protected static void initUI() {
JFrame frame = new JFrame("test");
final JLabel label = new JLabel("Label text");
frame.add(label);
frame.pack();
frame.setVisible(true);
Timer t = new Timer(1000, new ActionListener() {
int progress = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (progress > 100) {
progress = 0;
}
label.setToolTipText("Progress: " + progress + " %");
Point locationOnScreen = MouseInfo.getPointerInfo().getLocation();
Point locationOnComponent = new Point(locationOnScreen);
SwingUtilities.convertPointFromScreen(locationOnComponent, label);
if (label.contains(locationOnComponent)) {
ToolTipManager.sharedInstance().mouseMoved(
new MouseEvent(label, -1, System.currentTimeMillis(), 0, locationOnComponent.x, locationOnComponent.y,
locationOnScreen.x, locationOnScreen.y, 0, false, 0));
}
progress++;
}
});
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initUI();
}
});
}
}
Here's a simplified version of Guillaume Polet's answer which is self-contained in a single method. This code assumes one has called component.setToolTip("..."); previously. This code does not show how to periodically update the tooltip to show the progress.
public static void showToolTip(JComponent component)
{
ToolTipManager manager;
MouseEvent event;
Point point;
String message;
JComponent component;
long time;
manager = ToolTipManager.sharedInstance();
time = System.currentTimeMillis() - manager.getInitialDelay() + 1; // So that the tooltip will trigger immediately
point = component.getLocationOnScreen();
event = new MouseEvent(component, -1, time, 0, 0, 0, point.x, point.y, 1, false, 0);
ToolTipManager.
sharedInstance().
mouseMoved(event);
}

Categories