I'm trying to display a GIF file to a JLabel. No problem I load the GIF and set it as the icon of the label. But now i really need to know when this ends, because it has to function as a cutscene. What i already tried is this (GameScene is a custom object but i know setIcon works, trust me) :
private GameScene scene;
private ImageIcon _temp;
private ImageIcon toplay;
private Thread anim;
public Cutscene(ImageIcon gif, GameScene scene, int length){
this._temp = scene.getIcon();
this.toplay = gif;
this.scene = scene;
anim = new Thread(){
#Override
public void run(){
scene.setIcon(gif);
scene.repaint();
try {
Thread.sleep(length);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
private void _s(){
scene.setSolidColor(false);
scene.setIcon(toplay);
scene.repaint();
}
public void play(){
_s();
anim.start();
}
public void await(){
try {
anim.join();
scene.setIcon(_temp);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Now, Is there anyway I can achieve this without, just letting the whole program sleep for the given amount of time and then continue with the rest of the code and completely not drawing the GIF file?
This is a really simply example, it will only work with non-looping gifs, if your gif repeats you'll have to test the time between requests for FRAMEBITS and try and determine if you can detect the point at which loops.
public class MonitoringLabel extends JLabel {
public MonitoringLabel(Icon image) {
super(image);
}
public void addActionListener(ActionListener listener) {
listenerList.add(ActionListener.class, listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(ActionListener.class, listener);
}
protected void fireActionPerformed() {
ActionListener[] listeners = listenerList.getListeners(ActionListener.class);
if (listeners.length > 0) {
ActionEvent evt = new ActionEvent(this, 0, "stopped");
for (ActionListener listener : listeners) {
listener.actionPerformed(evt);
}
}
}
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, int h) {
boolean finished = super.imageUpdate(img, infoflags, x, y, w, h);
if (!finished) {
fireActionPerformed();
}
return finished;
}
}
The above class provides an ActionListener which is triggered when the image "finishes" loading (this is triggered when their are no new frames in the animation to be played - repeating gifs never return false)
You can use a different listener interface if you want, I just threw it together as an example.
The reason I choose to override imageUpdate is because the JLabel is already acting as the ImageObserver to the image when it paints it, this way we can use the power of the JLabel without fussing about with manually painting a gif ... yes I've done it, it's pain
Would it be possible to make the GIF non-repeating with the program used to make it? Most photo editors that make GIF images give you the option of making it repeating or not.
If I'm understanding your problem, it seems like you just want the gif to play once?
If it's for a cut scene, why not make the program sleep during that time?
Related
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();
}
Not sure how well I will explain this; I'm quite new to programming...
So I'm trying to make a desktop application that draws musical notes of a given type on some sheet music when the user selects the button corresponding to that type of note. Currently, if the user selects the "Whole Note" button, the user can then start clicking on the screen and the note will be drawn where the click occurred. It will also make a "ding" sound and write some info about that note to a text file.
That's all well and good, but unfortunately when the user selects a new button, say the "Quarter Note" button, for each mouse click there will be two notes drawn (one whole, one quarter), two dings, and two packets of info written to the file. I have no idea how to make this work! Currently, I'm trying to use threads, such that each button creates a new thread and the thread currently in use is interrupted when a new button is pressed, but that doesn't resolve the issue.
Initially, an empty linked list of threads ("NoteThreads") is constructed. Also, a private class known as SheetMusicPane (given the variable name "smp") is constructed in order to draw the sheet music. The buttons are added in the main constructor (public CompositionStudio), whereas the method containing the mouse listener (see what follows) is contained in the SheetMusicPane private class. Not sure whether that is part of the problem.
I have a button action listener:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!threads.isEmpty()) {
NoteThread oldThread = threads.remove();
oldThread.interrupt();
}
NoteThread newThread = new NoteThread(e.getActionCommand());
threads.add(newThread);
newThread.run();
}
});
that produces a thread:
private class NoteThread extends Thread {
private String note;
public NoteThread(String note) {
this.note = note;
}
public void run() {
smp.getShape(smp.getGraphics(), note);
}
}
that when, on running, calls this method with graphics and a mouse listener:
public void getShape(final Graphics g, final String note) {
this.addMouseListener(new MouseListener() {
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
addShape(g, p.x, p.y, note);
int pitch = 12;
piano.playNote(pitch);
advance(1.0, piano);
try { addToFile(pitch, note);}
catch(FileNotFoundException fnfe) {}
catch(IOException ioe) {}
}
});
}
The above method is responsible for drawing the note ("addShape()"), making the ding sound, and writing to the file.
Thanks in advance for any help you can give!
what you're trying to do does not require multithreading. This is the approach that I'd take:
set up a set of toggle buttons or radio buttons to select the note to paint. this way, only one note will be selected at a time. add action listeners to those that store in an adequately scoped variable what note is selected, or infer that every time a note should be drawn. this way, you don't even add action listeners to the buttons. in any case, don't spawn new threads.
in your mouse listener, find out what note to draw, and do that - only one note.
if you can, stay away from multithreading, especially as a beginner. also, I think you confuse adding and running listeners here. each call to getShape() adds a new listener, meaning they accumulate over time, which might be the cause of your problems.
PS: welcome to stackoverflow! your question contained the important information and I could infer that you tried solving the problem yourself. It's pleasant to answer such questions!
One solution would be to simply fetch all the listeners (which should be 1) and remove them before adding the new listener:
public void getShape(final Graphics g, final String note) {
MouseListener[] listeners = this.getMouseListeners();
for (MouseListener ml : listeners) {
this.removeMouseListener(ml);
}
this.addMouseListener(new MouseListener()...);
}
An alternative, since you have a finite number of buttons, would be to create a finite set of listeners, eg:
private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;
Create a reference to the "current" listener (private MouseListener current;), and have a means of deciding which listener to use whenever getShape is called (a series of if conditions on the note String would work, although I would prefer some refactoring to use an enum personally). Then you could do something along the lines of:
private MouseListener wholeNote = new MouseListener()...;
private MouseListener quarterNote = new MouseListener()...;
private MouseListener current;
...
public void getShape(final Graphics g, final String note) {
if (current != null) {
this.removeMouseListener(current);
}
if (...) { // note is Whole Note
current = wholeNote;
} else if (...) { // note is Quarter Note
current = quarterNote;
} // etc.
this.addMouseListener(current);
}
Another alternative would be to change your listener so that you only ever need the one, but clicking a button changes a variable which the listener has access to. For example:
// In the listener
public void mouseClicked(MouseEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
addShape(g, p.x, p.y, currentNote);
int pitch = 12;
piano.playNote(pitch);
advance(1.0, piano);
try { addToFile(pitch, currentNote);}
catch(FileNotFoundException fnfe) {}
catch(IOException ioe) {}
}
// In the same class
protected String currentNote;
...
public void getShape(final Graphics g, final String note) {
currentNote = note;
}
I want to move a image across the screen by 16 to the right when the arrow key is pressed. I want to move it with a speed(1px/10ms) until reaches the point. The image is created inside a class that is child of JPanel.
I wrote the next code but the image changes the position instatly instead making a movement:
public class Test extends JFrame implements KeyListener {
private int x=0;
private int y=0;
BufferedImage img;
...
...
public void paint(Graphics g){
g.drawImage(img,x,y,null);
}
// Move to a point 16 pixels to right
public void moveRight(){
for(int i=0;i<16;i++){
x++;
repaint();
try {
Thread.sleep(10); // Sleep 10 milliseconds until next position change
}catch (InterruptedException e) {}
}
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
moveRight();
}
}
}
The problem is your sleep inside the EDT (Event-Dispatching-Thread). repaint() triggers an event that will be dispatched by the EDT and will in turn perform the actual repainting of your component. Since you are blocking the EDT, the repaint does not perform directly (but after the end of all your code then a single repaint event occurs (because repaint events are grouped whenever possible). You will probably need to use SwingWorker to fix this issue.
What if you call moveRight() in another thread?
try this:
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
new Thread(new Runnable(){
public void run(){
moveRight();
}
}).start();
}
}
I've not tested and I even don't know if this is a good aproach
I have a script that cycles (almost like a slideshow) through a Vector object (flipBook), using a Thread (animationThread), and adds them to a JPanel. However, the added image is only 2x2 pixels large.
I've verified the images are 50x50, but they don't appear to be properly showing.
Here's some of the code going on behind the Thread instance. I'm not entirely sure which code would be beneficial for finding the source for.
public void startThread() {
if (flipWidth != 0 && flipHeight != 0) {
System.out.println("[ AnimationAsset ] " + "We're starting the thread");
Runnable r = new Runnable() {
#Override
public void run() {
runWork();
}
};
animationThread = new Thread(r, "AnimationThread");
animationThread.start();
going = true;
}
}
private void runWork() {
try {
while (going) {
repaint();
flipIndex = (flipIndex + 1) % numFlips;
System.out.println("[ AnimationAsset ] flipIndex: " + flipIndex);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("[ AnimationAsset ] " + "Interrupted");
}
}
public void paint(Graphics g) {
update(g);
}
public void update(Graphics g) {
System.out.println("[ AnimationAsset ] " + flipIndex);
((Graphics2D) g).drawImage(flipBook.get(flipIndex), null, 5, 5);
}
and adds them to a JPanel
This is not a Swing code. This is AWT code.
You would never override the update() and paint() methods this way when using Swing. Get rid of this code and start over.
To do this in Swing I would use a JLabel with an Icon and add the label to the frame.
Then, to do animation in SWing your should use a Swing Timer.
When the timer fires you simply use the setIcon(...) method of the label to replace the old icon with your new icon.
Emm you have a strange drawing code as
((Graphics2D) g).drawImage(flipBook.get(flipIndex), null, 5, 5);
You can see the docs here... to use Graphic2D drawImage() method right
the most common way of image drawing is to paint image right on JComponent, as a rule, the JLabel. Here is a component example
public class MyLabel extends JLabel
{
private Image image;
public MyLabel(Image image)
{
this.image=image;
}
public void paintComponent(Graphics g)
{
g.drawImage(this.image,x,y,width,height,null);
}
}
so here you can use the component as a common swing object
public class MyPanel extends JPanel
{
public MyPanel()
{
Image image=null;
try{
image=ImageIO.read(new File("image.png"));
}
catch (IOException e) {
}
this.add(new MyLabel(image));
}
}
Good luck
I got a problem I couldn't get to work after about 2 Hours of trying. I want to have a loop that do 2 Methods (Draw and update) but also listen to Mouse/Keyboard events. I have a loop that Draws and Updates, but does nothing outside of the loop ( Listening to events ) I tried alot of things but nothing worked. Help Please?
I tried using the Runnable Thread, using different orders, using wait() and notify(), I've tried alot of things. But basicly I want to know how to run a loop and still check for User Input
Also when I try to quit the program clicking the red "X", it won't quit but still work
Here's the Code:
import java.applet.Applet;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class main extends Applet implements MouseListener, Runnable {
public main() {
super();
init();
}
Thread t;
Screen screen = new Screen();
String Text = "Hello";
boolean Running = true;
boolean Click = false;
int R = 0x00;
int G = 0x00;
int B = 0x00;
int xpoints[] = {25, 40, 40, 25, 25};
int ypoints[] = {40, 40, 25, 25, 25};
int npoints = 5;
public void run() {
while (Running) {
GameLoop();
}
}
public void init() {
this.addMouseListener(this);
this.setSize(400, 300); //manually set your Frame's size
t = new Thread(this);
t.start();
}
public void paint(Graphics g) {
g.setColor(new Color(R, B, G));
g.fillPolygon(xpoints, ypoints, npoints);
Running = true;
t.run();
}
public void mousePressed(MouseEvent e) { //On Mouse Click
System.exit(0);
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
System.exit(0);
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public boolean keyDown(Event e, int key) {
return true;
}
public void GameLoop() {
if (Running) {
if (R != 0xff) {
R++;
} else {
if (G != 0xff) {
G++;
} else {
if (B != 0xff) {
B++;
} else {
System.exit(0);
}
}
}
try {
sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
paint(getGraphics());
}
}
public void sleep(int time) throws InterruptedException {
Thread.sleep(time, 0);
}
}
This tutorial should provide some insight as to how your program should be structured. And this one is helpful for the mouse listener.
Issues you should address:
1) You're doing something fishy with the paint method. Why are you calling t.run() in there? The thread t is already running and looping constantly calling the paint() method to redraw the screen. Remove this call and see what you get.
1) The destruction of your thread/applciation is poor. The first example above provides a better way for that to occur
2) You have your System.Exit(0) on mousePressed() with the comment //on mouse click but nothing in mouseClicked()... it works but its bad convention
3)Having your class named main is extremely poor convention that is both confusing and impractical. Rename your class to something like "Game" or similar.
4) Why declare Screen if you don't use it?
I see that you define a Running variable to be true upon initialization. This variable is used to determine whether or not the game should stop. I, however, don't see any place where you modify the value of this variable to false. This would explain why your game never exits.
As for the the game not working, try debugging the application in an IDE. You should then pay attention to what, if any, Exception are being thrown and the values of any variables you are questioning. Hopefully this will give you insight into the behavior of your app.
Don't forget to update us with any new info you discover so we can help you out along the way.