I want to display different images in a same frame within a loop. String pathName[] contains the different paths of the images. When running this code, only last image i.e image at path pathname[last] is getting displayed on frame instead I want all images to be displayed in a continuous way (have given delay of 1sec ). Help is appreciated.
public void actionPerformed(ActionEvent event) {
int i=0;
while(i<5){
if(i>0){
Container labelParent = receiverImageLabel.getParent();
labelParent.remove(receiverImageLabel);
labelParent.validate();
labelParent.repaint();
}
try {
imageR = ImageIO.read(new File(pathName[i++])).getScaledInstance(512,512 , BufferedImage.SCALE_SMOOTH);
receivedImage = new ImageIcon(imageR);
}catch (IOException e) {
e.printStackTrace();
}
receiverImageLabel = new JLabel(receivedImage);
receiverFrame.getContentPane().add(BorderLayout.EAST,receiverImageLabel);
receiverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
receiverFrame.setSize(800,700);
receiverFrame.setVisible(true);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Your problem is a common one: you're calling Thread.sleep(...) in a Swing GUI on the event thread and are essentially thus putting the entire GUI to sleep.
Solution: Google the Swing Timer and use this in place of your while loop/Thread.sleep(...)
Also, if the images aren't too big, then consider reading them all in at once (in a background thread), putting them into ImageIcons, and then swapping out a JLabel's ImageIconsand in your Swing Timer.
For example, you could do something like:
ImageIcon[] icons = new ImageIcon[IMAGE_COUNT];
for (int i = 0; i < IMAGE_COUNT; i++) {
BufferedImage img = ImageIO.read(...); // read in the appropriate image
// ...... here manipulate the image if desired such as re-size it
icons[i] = new ImageIcon(img); // put it into an icon
}
elsewhere:
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener(){
int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (count < IMAGE_COUNT) {
someLabel.setIcon(icons[count]);
count++;
} else {
// stop the timer
((Timer)e.getSource()).stop();
}
}
}).start();
Note: code not compiled nor tested and is posted only as a general example of steps to consider.
Related
We are developing video streaming app using JavaFx and JavaCv.
We Have GridPane of 8X8 and each cell contains Pane layout and each pane
containing ImageView.
Every time i am calling play function whenever user drag url to particular
cell.
we have 64 Urls,So we are calling play function 64 times(all 64 url are playing
in background thread).
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
//grid08(8X8)
Pane p=new Pane();
ImageView im=new ImageView();
Label l=new Label("");
im.fitWidthProperty().bind(p.widthProperty());
im.fitHeightProperty().bind(p.heightProperty());
l.prefWidthProperty().bind(p.widthProperty());
l.prefHeightProperty().bind(p.heightProperty());
p.getChildren().addAll(im,l);
l.setVisible(false);
l.setStyle("-fx-text-fill: #fff; -fx-font-size: 12px");
l.setAlignment(Pos.CENTER);
p.setStyle("-fx-background-color: black;");
grid.add(p,j,i);//grid->instance of gridPane
p.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
if(!l.isVisible()){
event.acceptTransferModes(TransferMode.ANY);
}
}
});
p.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
play(im,event.getDragboard().getString());
}
});
}
}
play(ImageView im,String Url){
new Thread(new Runnable(){
frameGrabber = new FFmpegFrameGrabber(Url);
frameGrabber.setVideoOption("preset","ultrafast");
frameGrabber.setOption("rtsp_transport","tcp");
frameGrabber.setOption("stimeout" , "60000");
frameGrabber.setAudioChannels(0);
frameGrabber.setAudioCodec(0);
try {
frameGrabber.start();
start=true;
} catch (Exception e) {
}
JavaFXFrameConverter jconverter=new JavaFXFrameConverter();
while(true){
Frame frame = frameGrabber.grabImage();
Image image=jconverter.convert(frame);
Platform.runlater(new Runnable(){
im.setImage(image);
});
}
}).start();
}
Problem:
We are facing White Screen Problem,after increasing some threshold Width and
Height.
Here i am attaching Snap of my application with different screen size;
Screen1
Since We are facing this problem from long time.
Any Lead will be highly appreciable.
Thanks in advance.
Just a guess here, but when the image view is a different size to the image, the image view has quite a lot of work to do when you call setImage(...) (it has to resize the image). Since you have a lot of these happening, potentially every pulse, it's possible you are submitting more runnables to Platform.runLater(...) than it can handle (i.e. you submit another Platform.runLater(...) before the previous one submitted from the same thread can complete). This will cause rendering issues, because the rendering system can't keep up.
I can't test this, so I'm not sure if it will fix the issue, but it's probably a good idea to only submit a new call to Platform.runLater(...) when the previous one has completed (i.e. you should do this even if it's not the cause of the problem).
You can do this with
void play(ImageView im,String Url){
new Thread(() -> {
frameGrabber = new FFmpegFrameGrabber(Url);
frameGrabber.setVideoOption("preset","ultrafast");
frameGrabber.setOption("rtsp_transport","tcp");
frameGrabber.setOption("stimeout" , "60000");
frameGrabber.setAudioChannels(0);
frameGrabber.setAudioCodec(0);
try {
frameGrabber.start();
start=true;
} catch (Exception e) {
e.printStackTrace();
}
JavaFXFrameConverter jconverter=new JavaFXFrameConverter();
AtomicReference<Image> imageHolder = new AtomicReference<>(null);
while(true){
Frame frame = frameGrabber.grabImage();
Image image=jconverter.convert(frame);
if (imageHolder.getAndSet(image) == null) {
Platform.runLater(() -> im.setImage(imageHolder.getAndSet(null)));
}
}
}).start();
}
Note that in this code (and the original), all the images are being resized on the same thread (the FX Application Thread). If possible, it would probably be more efficient to resize each image in its own thread. You can't absolutely guarantee this (because there's no way to guarantee the image view doesn't change size between the image being resized in the background thread and the image view displaying the image), so you still need to bind the fitWidth and fitHeight properties, but this should reduce the amount of image resizing that happens on the UI thread, and consequently should improve performance. This relies on there being some mechanism in your frame grabber API (which I'm not familiar with) to resize images.
for(int i=0;i<8;i++){
for(int j=0;j<8;j++){
//grid08(8X8)
Pane p=new Pane();
AtomicReference<Bounds> bounds = new AtomicReference<>(p.getBoundsInLocal());
p.boundsInLocalProperty().addListener((obs, oldBounds, newBounds) -> bounds.set(newBounds));
ImageView im=new ImageView();
Label l=new Label("");
im.fitWidthProperty().bind(p.widthProperty());
im.fitHeightProperty().bind(p.heightProperty());
l.prefWidthProperty().bind(p.widthProperty());
l.prefHeightProperty().bind(p.heightProperty());
p.getChildren().addAll(im,l);
l.setVisible(false);
l.setStyle("-fx-text-fill: #fff; -fx-font-size: 12px");
l.setAlignment(Pos.CENTER);
p.setStyle("-fx-background-color: black;");
grid.add(p,j,i);//grid->instance of gridPane
p.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
if(!l.isVisible()){
event.acceptTransferModes(TransferMode.ANY);
}
}
});
p.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
play(im,bounds,event.getDragboard().getString());
}
});
}
}
and
void play(ImageView im, AtomicReference<Bounds> bounds, String Url){
new Thread(() -> {
frameGrabber = new FFmpegFrameGrabber(Url);
frameGrabber.setVideoOption("preset","ultrafast");
frameGrabber.setOption("rtsp_transport","tcp");
frameGrabber.setOption("stimeout" , "60000");
frameGrabber.setAudioChannels(0);
frameGrabber.setAudioCodec(0);
try {
frameGrabber.start();
start=true;
} catch (Exception e) {
e.printStackTrace();
}
JavaFXFrameConverter jconverter=new JavaFXFrameConverter();
AtomicReference<Image> imageHolder = new AtomicReference<>(null);
while(true){
Frame frame = frameGrabber.grabImage();
Bounds imgBounds = bounds.get();
double width = imgBounds.getWidth();
double height = imgBounds.getHeight();
// resize frame to width, height....
Image image=jconverter.convert(frame);
if (imageHolder.getAndSet(image) == null) {
Platform.runLater(() -> im.setImage(imageHolder.getAndSet(null)));
}
}
}).start();
}
I am working on a swing project. There is a map, I have raster images of a given data for different times. Normally I change the time via a JSlider and it requests server for raster image. Then I add response image to map. There is a Play JButton, when pressed it will add those images one by one to raster layer of the map. It will be seen as an animation. In that JButton's actionPerfomed method I change the JSlider's value in a for loop.
My problem is when I press Play JButton, I can't see the data is played but I know code block works because I record each image(from server). I found out that it is becuse of JButton does not release Focus until its actionPerformed method ends. Because JButton looked like it was pressed until the end. So I only see the last image in the map.
First, I tried JButton.setFocusable(false) etc. but to no good.
Second, I tried using SwingWorker. I added it like this:
class PlayMvgmLayerWorker extends SwingWorker<Void, Void> {
public PlayMvgmLayerWorker(String title) {
super(title);
}
#Override
protected void done(Void aVoid) {
}
#Override
protected Void doInBackground() {
try{
BufferedImage[] image = new BufferedImage[24];
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( value );
Thread.sleep(10000l);
}
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
}
private JButton animation = new JButton("");
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PlayMvgmLayerWorker("title").execute();
}
});
private JSlider timeSlider = new JSlider();
timeSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
// time consuming processes ( server call, add image to map, etc)
}
});
I tried to simplify it.
It is much better than before, but I still can not see the data played properly. Sometimes data is played after JSlider ticks. Could it be because my time consuming process is in the second components(JSlider) stateChanged event? Should I use a second SwingWorker in JSlider's event too? Any suggestions about what can I do?
Moreover, what would be the best way to disable all components before playing data, and enable them after playing data?
Thank you very much in advance
If you have two activities Activity A and Activity B which have to be run simultaneously, you need to create a thread for the second activity - the first activity will already be run in its own thread (the main program).
The scheme is as follows:
Program A:
create new Thread: Activity B
run allother tasks for Activity A
In more specific terms the following program will run your simulation and update the tick of the slider:
public P() {
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doInBackgroundImp();
}
});
setSize(500, 500);
setLayout(new FlowLayout());
add(animation);
add(timeSlider);
setVisible(true);
}
protected void doInBackgroundImp() {
Thread th=new Thread() {
public void run() {
try{
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( i );
System.out.println(timeSlider.getValue()+" "+value);
Thread.sleep(1000);
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
};
th.start();
}
private JButton animation = new JButton("");
private JSlider timeSlider = new JSlider();
}
public void playPanel() throws IOException{
for(int i = 0; i<listData.size(); i++){
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ascii.setText(listData.get(i));
}
}
What I'm trying to do is play through the listData ArrayList type, which was copied from the ascii JTextArea. Its supposed to be an animation, so when they hit play the function displays the first slide, waits a second, then the next slide, etc.
When I run this the only thing that happens is a pause with nothing on the screen changing until it displays only the final slide. I'm not sure what's wrong with it
You should never call Thread.sleep(...) on the Swing event thread unless your goal is to put the entire application to sleep, rendering it useless. Instead, get rid of the for loop, and "loop" using a Swing Timer. Something like this should work, or be close to a functioning solution (caveat: code has not been compiled nor tested):
int delay = 1000;
new Timer(delay, new ActionListener() {
private int i = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (i < listData.size()) {
ascii.setText(listData.get(i));
} else {
((Timer) e.getSource()).stop();
}
i++;
}
}).start();
i am trying to load image from specified path, and list of files are stored inside List<>.
at first time when i initialize image it display but when i am trying to display next image from List instance which contain list of files, it doesn't repaint the image.
what's wrong going is i am initializing image in constructor first time that overwrite
the new image, now where to initialize image first time outside constructor i don't know.
my code :
public void nextImage(int cnt)
{
System.out.println(cnt);
if (cnt < imageFiles.size())
{
System.out.println(imageFiles.size());
try
{
bg = ImageIO.read(new File((imageFiles.get(cnt)).toString()));
scaled = getScaledInstanceToFit(bg, new Dimension(600, 600));
setBackground(Color.BLACK);
}
catch(Exception e)
{
e.printStackTrace();
}
}
MouseHandler handler = new MouseHandler();
addMouseListener(handler);
addMouseMotionListener(handler);
System.out.println(cnt);
System.out.println(imageFiles.get(cnt).toString());
}
menu item click code :
JMenuItem mntmRestoreImage = new JMenuItem("Next Image");
final ImagePane st = new ImagePane();
mntmRestoreImage.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0)
{
st.nextImage(k);
k++;
}
});
mnEdit.add(mntmRestoreImage);
class code & constructor :
private BufferedImage bg;
private BufferedImage scaled;
java.util.List<Path> imageFiles= getFilesFromDirectory(FileSystems.getDefault().getPath("D:\\New folder"));
public ImagePane()
{
try
{
bg = ImageIO.read(getClass().getResource("/images/src11.jpg"));
scaled = getScaledInstanceToFit(bg, new Dimension(600, 600));
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
counter also increments, now the code inside ImagePane() constructor
overwrites the image of nextImage() function, so idea what happen out
in this code ??
any suggestion ?
I think I have the perfect solution for you! I wrote a little program for you so it is easier to understand.
First I have a method for you to check if the file is a picture:
public Stack<File> getFilesInFolder(String startPath) {
File startFolder = new File(startPath);
Stack<File> picturestack = new Stack<File>();
String extension;
int dotindex;
// Go through the folder
for (File file : startFolder.listFiles()) {
extension = "";
dotindex = file.getName().lastIndexOf('.'); // Get the index of the dot in the filename
if (dotindex > 0) {
extension = file.getName().substring(dotindex + 1);
// Iterate all valid file types and check it
for (String filetype : validpicturetypes) {
if (extension.equals(filetype)) {
picturestack.add(file);
}
}
}
}
return picturestack;
}
Very easy! Take the folder and iterate his files. Take the extension of the file and check if it is a valid file type. Define the file types in a array at the begining of your code.
String[] validpicturetypes = {"png", "jpg", "jpeg", "gif"};
At the end I push every file into a stack. Remember to fill the stack into a variable, do not read the files more than once because than you get the same problem as before:
Stack<File> pictures = getFilesInFolder("C:\\Users\\Admin\\Desktop");
After that use a Action for your JMenuItem! In my example I do not have much, you have to put your methods in!
Action nextpictureaction = new AbstractAction("Next Picture") {
private static final long serialVersionUID = 2421742449531785343L;
#Override
public void actionPerformed(ActionEvent e) {
if (!pictures.isEmpty()) {
System.out.println(pictures.pop().getName());
}
}
};
Add the Action at your JMenu and set the properties of your Frame.
/*
* Add Components to Frame
*/
setJMenuBar(menubar);
menubar.add(toolsmenu);
toolsmenu.add(nextpictureaction);
/*
* Frame Properties
*/
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationByPlatform(true);
setSize(1000, 700);
setTitle("PictureEditor");
setVisible(true);
At the end execute your program with the invokeLater method!
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new PictureEditor();
}
});
}
Summary
Basically you need a somthing to iterate through because values like integer are not saved the way you like. In my example I used a Stack and save at the beginning all pictures in it. Important is that, if you used or finished with the picture, you have to remove it (use stack.pop() for a Stack). I do not found a method where you check if the file is a picture (if it is the ImageIO catch it is bad). I wrote a method for that if you want you could use it.
This is not an answer, but I cannot paste that much code into a comment.
I would change your code to something along the lines of this piece of code. This seperates the image loading from the gui updating logic (like adding mousehandlers and the like, I pasted only image loading code).
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import javax.imageio.ImageIO;
public class ImageLoader
{
public static class ImageContainer
{
BufferedImage bg = null;
BufferedImage scaled;
}
Iterator<File> imageFiles = Arrays.asList(
new File("D:\\New folder").listFiles()).iterator();
public ImageContainer nextImage(Dimension dimensionToFit) throws Exception
{
ImageContainer c = new ImageContainer();
if (imageFiles.hasNext())
{
File file = imageFiles.next();
//you might not need this, if only images are in this directory
if(file.isDirectory())
return null;
c.bg = ImageIO.read(file);
c.scaled = getScaledInstanceToFit(c.bg, dimensionToFit);
return c;
}
else
return null;
}
private BufferedImage getScaledInstanceToFit(BufferedImage bg,
Dimension dimensionToFit)
{
//do the risizing
}
}
This is not yet optimized though.
This question already has an answer here:
SwingWorker, Thread.sleep(), or javax.swing.timer? I need to "insert a pause"
(1 answer)
Closed 9 years ago.
I am working on my first Swing application. It is a memory game using poker cards.
I simulate cards using JLabels and setting icons for front and back sides. Each card has a MouseListener and when the user clicks, I check if two cards are the same. If they aren't the same card, I want to show these two cards for one or two seconds and after this delay, change icon back.
I tried using sleep, wait, invokeLater, invokeAndWait... but nothing works.
This is my main class:
public class Main {
public static void main(String[] args) throws FontFormatException, IOException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
MyApp window = new MyApp();
} catch ( FontFormatException | IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
}
MyApp inherits from JFrame. Inside it, I add all my cards to one panel:
while ( cont < cardsInGame.size() ){
this.cardsInGame.get(cont).setBounds(x, y, 100, 140);
panelTablero.add(cardsInGame.get(cont));
cardsInGame.get(cont).addMouseListener(this);
x = x+108+5;
if ( (cont+1)%8 == 0 && cont != 0){
y = y+140+15;
x = 53;
}
cont++;
}
And this is the MouseListener:
public void mouseClicked(MouseEvent e) {
Card selectedCard = (Card)e.getSource();
if (selectedCard != activeCard){
selectedCard.setIcon(new ImageIcon("img/"+selectedCard.getSuit()+selectedCard.getValue()+".png"));
//JOptionPane.showMessageDialog(vp, "Wait");
if ( activeCard != null && !activeCard.getPaired()) {
int result = activeCard.isPair(selectedCard);
pairsTried++;
if ( result != 0 ){
// PAIR
}
else{
// I WANT TO WAIT HERE
// NO PAIR
selectedCard.setIcon(new ImageIcon(CARD_BACK));
activeCard.setIcon(new ImageIcon(CARD_BACK));
}
activeCard = null;
}
else{
activeCard = selectedCard;
}
}
}
If I put a call to JOptionPane.showMessageDialog(vp, "Wait") in my code, all works well. The icon is refreshed and after that wait for dialog OK. If not, the icon never is refreshed (or is ultra fast and is not showing).
How can I add this delay?
Have u tried to put a therad inside this else?
Runnable r = new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread asd = new Thread(r);
asd.start();