This time I got a really hard nut to crack. I was able to implement a slideshow-program which is able to display a number of random pictures after each other in a given time. The program also reacts on button presses.
Now I got the task to also make it able to display video files and it's wrecking my head. The tasks that need to be solved are the following:
The resolution of the file should be dependent on the actualy size of the screen. If an image or video has a greater resolution than the screen it is supposed to be scaled down (see the example code).
Images and videos are supposed to be implemented as JComponents in a JFrame which itself should be composed out of several elements like an area for the image/video, an area for text and for buttons etc - (I solved this for pictures).
After a certain amount of time, the slideshow is supposed to show the next picture/video. With pictures the time is fixed but when showing a video, the time should be dependent on the duration of the video itself (we wouldn't want the slideshow to jump to the next slide in the middle of the video)
For easier explanation let me first show how I solved the implementation of the pictures into the slideshow:
'''
public class DisplayImage extends JComponent {
private static final long serialVersionUID = 2613775805584208452L;
private static Image image;
public static Image displayImage(File f, Dimension screenSize) throws IOException {
//This method loads a file from the computer and resizes it in comparison to the size of the computer screen. The image is then returned for further processing.
BufferedImage img = ImageIO.read(f);
Image dimg;
double width = screenSize.getWidth()*0.75;
double z1 = (img.getWidth()/width);
double z2 = (img.getHeight()/screenSize.getHeight());
if (img.getHeight()/z1 <= width && img.getHeight()/z1 < screenSize.getHeight()) {
dimg = img.getScaledInstance((int)(img.getWidth()/z1), (int) (img.getHeight()/z1),Image.SCALE_SMOOTH);
} else {
dimg = img.getScaledInstance((int)(img.getWidth()/z2), (int)(img.getHeight()/z2),Image.SCALE_SMOOTH);
}
return dimg;
}
public void setImage(Image image1) {
//When an image is resized, it is given to this method.
//It replaces the global variable "image" with the new loaded image so the JFrame in the slideshow is actually reset and will display the new image.
image = image1;
repaint();
invalidate();
}}
'''
As you can see, I am completely fine for loading a new image and rewriting the image as well as the JComponent of the class with it.
Coming to a video file it get's messy instead. I was able to get video files to be loaded by another code taken from somewhere here using Maven but I didn't succeed in implementing it as a JComponent (I have been browsing stackoverflow as well as google already for days but couldn't find the solution for my problem). So far the only thing I can do is starting an extra player besides the slideshow as if they have nothing in common:
'''
public void playVideo(File f, Dimension screenSize) throws IOException, JCodecException {
Picture img = FrameGrab.getFrameAtSec(f, 1);
double width = screenSize.getWidth()*0.75;
double z1 = (img.getWidth()/width);
double z2 = (img.getHeight()/screenSize.getHeight());
NativeLibrary.addSearchPath(RuntimeUtil.getLibVlcLibraryName(), "C:\\Program Files\\VideoLAN\\VLC");
Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
JFrame frame = new JFrame("vlcj Tutorial");
MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory();
Canvas c = new Canvas();
c.setBackground(Color.black);
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
p.add(c, BorderLayout.CENTER);
frame.add(p, BorderLayout.CENTER);
EmbeddedMediaPlayer mediaPlayer = mediaPlayerFactory.newEmbeddedMediaPlayer();
mediaPlayer.setVideoSurface(mediaPlayerFactory.newVideoSurface(c));
if (img.getHeight()/z1 <= width && img.getHeight()/z1 < screenSize.getHeight()) {
frame.setSize((int)(img.getWidth()/z1), (int)(img.getHeight()/z1));
} else {
frame.setSize((int)(img.getWidth()/z2), (int)(img.getHeight()/z2));
}
frame.setSize(screenSize);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
mediaPlayer.playMedia(f.getPath());
}
'''
The mess starts already with me not being able to actually get the measurements of the video file itself (meaning width and height). I have been crushing my head over implementing different frameworks like JavaCV, Xuggle, MarvinFramework and much more but it was no good at all. The only thing I can do is to get a frame from the video as a Picture-type as shown in this example. But this doesn't work for me to give back either a JComponent or a BufferedImage (as with the pictures seen in the first method). Even worse: I have found no possible way to make the JFrame actually be reset when a video file is loaded leading for it to freeze dead as soon as a video is started in a new player. After that there is only the kill switch left.
So I'm lost here. Any help is greatly appreciated.
Related
I'm learning java through university and I've been taught the basics of making a java program and designing GUIs. Maximizing a window after running my program makes all the JFrame components stay in place while grey fills the rest of the space. Here's an example of how it looks like:
JFrame window normally, Maximized window before "fix".
After failing to find a solution I came up with a band-aid solution which is to get the component locations and just move them with hard-coded values when the jframe is maximized. This was not an elegant solution and every jframe in my java course project increased in the number of elements on screen. Is there any piece of code to make my components move and resize automatically and dynamically?
Here's what I've tried so far:
First I obtained the positions of components through 2D points:
Point managementLoginBtnLocation, empLogLocation, logoLocation, customerBtnLocation, welcomeLblLocation, contactBtnLocation, aboutBtnLocation, mainMenuBtnLocation;
//Constructor and rest of code...
public final void getOriginalComponentLocations()
{
managementLoginBtnLocation = managementLoginBtn.getLocation();
empLogLocation = empLoginBtn.getLocation();
logoLocation = shopLogo.getLocation();
customerBtnLocation = customerBtn.getLocation();
welcomeLblLocation = welcomeLbl.getLocation();
contactBtnLocation = contactBtn.getLocation();
aboutBtnLocation = aboutBtn.getLocation();
mainMenuBtnLocation = mainMenuBtn.getLocation();
}
//This method is called within the constructor.
I implemented the ComponentListener Interface and added a component listener to my jframe. Then I made it so when the jframe's size changes, it changes the size of the jlabel used for background art. And if the label's width is greater than 800 (the default I used while designing) it moves the components and doubles their size and font size. When the jframe is minimized the label will go back to the default size so I made a method to revert the font sizes, because I found the component sizes and locations reset automatically.
public void componentResized(ComponentEvent e)
{
//Resizing the background label and setting its icon to a resized version of its current icon.
backgroundMainArt.setSize(this.getWidth() - 16, this.getHeight() - 21);
ImageIcon icon = new ImageIcon("C:\\Program Files\\OMOClothingStore\\Resources\\Main menu\\main menu background art.jpg");
Image img = icon.getImage();
Image newImage = img.getScaledInstance(backgroundMainArt.getWidth(), backgroundMainArt.getHeight(), Image.SCALE_FAST);
icon = new ImageIcon(newImage);
backgroundMainArt.setIcon(icon);
if(backgroundMainArt.getWidth() > 800) //When the size of the label is greater than default
{
//I move the components, enlarge the buttons and zoom the font size
moveComponents();
enlargeBtns();
zoomBtnsFontSize();
}
else //When the label is back to its original size
{
//I revert the font sizes as button sizes and positions reset automatically
revertBtnsFontSize();
setLogoIconAndBackgroundArtAndWelcomeLbl();
}
}
public void moveComponents()
{
moveLogo();
moveManagementLoginBtn();
moveEmployeeLoginBtn();
moveCustomerBtn();
moveWelcomeLbl();
moveContactInfoBtn();
moveAboutBtn();
moveMainMenuBtn();
}
public void moveLogo()
{
ImageIcon logoIcon = new ImageIcon("C:\\Program Files\\OMOClothingStore\\Resources\\Shared resources\\OMO Clothing Store logo.png");
Image logoImg = logoIcon.getImage();
Image newLogoImage = logoImg.getScaledInstance(250, 250, Image.SCALE_DEFAULT);
logoIcon = new ImageIcon(newLogoImage);
shopLogo.setIcon(logoIcon);
Point newLogoLocation = new Point();
newLogoLocation.x = (logoLocation.x * 2) + 200;
newLogoLocation.y = (logoLocation.y * 2) + 30;
shopLogo.setLocation(newLogoLocation);
}
//The rest of the "moveX" methods follow the same pattern as moveLogo()
public void enlargeBtns()
{
managementLoginBtn.setSize(410, 94);
empLoginBtn.setSize(410, 94);
customerBtn.setSize(410, 94);
}
public void zoomBtnsFontSize()
{
customerBtn.setFont(sizeBtn.getFont());
//sizeBtn is a JButton that has a font size of 24. I found that just creating a new Font object with bigger size here made the font way larger for some reason.
empLoginBtn.setFont(sizeBtn.getFont());
managementLoginBtn.setFont(sizeBtn.getFont());
}
public void revertBtnsFontSize()
{
empLoginBtn.setFont(new Font("Segoe UI", Font.PLAIN, 14));
managementLoginBtn.setFont(new Font("Segoe UI", Font.PLAIN, 14));
customerBtn.setFont(new Font("Segoe UI", Font.PLAIN, 14));
}
I split the moving of the components into many methods inside other methods because I found it easier to keep up with.
This worked. Here's how it looks like when running the JFrame: Maximized window after "fix". But moving on to other JFrames, they are more intricate and have many more components - extra buttons, panels with other components in them, menu bars, etc.
Is there a better approach to fixing this? Or do I just remove the ability to resize and move on?
I am trying to make a Slot Machine with a JFrame.
How do I move my JLabel with a smooth animation?
What I have tried is that I am increasing the delay in the loop with every round, but that is to slow.
It should move at the beginning very fast and getting at the end very slow.
Maybe anyone can help me to calculate the smooth better?
Here is my code of my Animation class extending Thread:
JLabel label; // thre Jlabel
int labelPosY, maxHeight; // label Y position and all Y positions in summary
Animate(JLabel label, int labelPosY, int maxHeight)
{
this.label = label;
this.labelPosY = labelPosY;
this.maxHeight = maxHeight; // for example: -6500
}
public void run()
{
int smooth; // smooth
for (int i = this.maxHeight; i <= 0; i++)
{
try
{
smooth = 100 - (Math.abs(i) / (Math.abs(this.maxHeight) / 100)); // getting percentage of whole moving process
Layout.setLabelPosY(this.label, this.labelPosY++); // changes Y position of JLabel
Thread.sleep(smooth); // waits the always changing process (1ms - 100ms)
}
catch (InterruptedException e) {e.printStackTrace();}
}
}
Image
Here's one way to create a slot machine GUI in Java Swing.
Download slot machine images, like this one. You can find many other images with Google.
Read the image file from a properties folder in your Java project into a BufferedImage.
Divide the BufferedImage into individual BufferedImages. With this image, that would be 9 individual BufferedImages.
Create a BufferedImage strip that's one image wide by nine images high.
Create the rotating image of a slot machine by animating the rotation of three or four copies of the BufferedImage strip. You draw this image on a JPanel in the JFrame.
Keep track of the money spent and winnings on a control JPanel in the JFrame. The spin JButton would also be placed on the control JPanel.
I'm writing a plugin for a miscropy program and have problems with the repaint() method.
short question:
Is there any way to get informed as soon as the repaint of a JPanel was done or synchronize the code with it?
detailed version:
My program can plot a set of data in a xy-chart to a JPanel and show it using jfree.chart; In another part of the programm I have many datasets (~100) that I want to plot and save as images. I've also found a solution, but I really don't like it. The Problem can be reduced to a notification about the paint status of a JPanel.
In the part that shall save all images I have this solution:
PlotSpectrum spectrumWindow = getTheWindow(); //pseudo code...
// some stuff
ti = storage.getImage(channel, slice, frame, position);
spectrumWindow.plotData(false, andor.captureSpectrum(ti.pix), wave,
centerWave, fineGrating, exposureTime,
slitWidth, substractBackground);
spectrumWindow.repaint(); // probably not necessary
sleep(100); // this annoys me...
spectrumWindow.savePlot(path, true, config, null);
spectrumWindow is a JPanel that is also displayed in another window and it all works fine.
BUT I really don't like that sleep(100) in there... without it I'm asking for a repaint but it isn't done till I try to save a "snapshot" of (thats what savePlot is doing...). I know, other Thread and these damn synchronization problems...
With the sleeping I'm just making it unnecessary slow and if I wait not long enough the images are not completly drawn (eg lower half missing)
Is there any way to get informed as soon as the repaint was done? I probably would be also fine with a Listener, better would be a solution with a monitor or sth comparable or a method that is repainting NOW (doesn't exists as far I know?)
The main GUI (include the JPanel spectrumWindow) and the earlier pasted code are running in different Threads.
The probably also important parts of my code are following here. Please excuse if some brackets aren't matching or some variables aren't declared, I removed very much code.
thanks
schetefan24
class PlotSpectrum extends ApplicationFrame // that extends JFrame
{
public void plotData(boolean backgroundGiven, int[] spect, double[] wave_,
double centerWave, boolean fineGrating_, double exposureTime,
double slitWidth, boolean substractBackground)
{
//process data and store internally
replot();
}
private void replot()
{
XYSeries series = new XYSeries("Spectrum");
//add data to series
XYSeriesCollection collection = new XYSeriesCollection(series);
//setting up some labels and such stuff...
JFreeChart chart = ChartFactory.createXYLineChart(
title,
"Wavelength [nm]",
yLabel,
collection,
PlotOrientation.VERTICAL,
false,
false,
false
);
dataPanel.add(new ChartPanel(chart)); // this is contained in a Frame
}
public void savePlot(String path, boolean overWriteAll, HashMap<String,String> config, int[][] addData)
{
File output = new File(path);
//some more stuff, ask overwrite etc
if(image)
{
BufferedImage im = createImage();
String extension = path.substring(path.lastIndexOf(".")+1, path.length());
ImageIO.write(im, extension, output);
} else {
//that is an textexport, works fine
}
}
public BufferedImage createImage()
{
JPanel panel = (JPanel) flipChart.getSelectedComponent();
int w = panel.getWidth();
int h = panel.getHeight();
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
panel.paint(g);
return bi;
}
}
that I want to plot and save as images.
add the data to a non visible panel.
create a BufferedImage of the panel
create an ImageIcon using the Image from above
update a JLabel (that has already been added to the frame) using the setIcon(...) method
the above step should generate a PropertyChange event when the Icon changes. You can use a ProperChangeListener to listen for this event. When you receive the event you can repeat steps 1 - 4.
Check out Screen Image. It will help you create an image of a non-visible component.
Note, you don't really need steps 4-5. I just added them so you have a visual of the plots as they are being processed. If you don't want the visual then maybe you just display text on a JLabel indicating which plot is currently being converted.
I think I've just pinpointed the problem, but I'm still not sure what to do about it.
I've created various animated gifs using Photoshop and I wanted to display them in my java application. Using ImageIcon, some of them display as they should. Those are the ones with frames that have the same frame rate (such as each frame is 1 second long). However, the gifs that have varying frame rates (ex. one frame is 1 second, the other is .5 seconds, the next is .2) doesn't seem to be showing correctly. Once the gif reaches the frames that vary in seconds the image messes up. Some examples include the background briefly changing color and half of the image disappearing, or most of the image disappearing with the gif still animating.
I'm having a difficult time articulating, but do I need to use something else other than ImageIcon to correctly load the gif with varying frame rates? Like something with BufferedImage?
Edit: Added complete code below. Again, it works for the image with equal frame rates, but not for one with varying frame rates.
here is the image that works fine
and here is the image that messes up
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
public class Creature extends JWindow {
public void initCreature() {
JPanel contentPane = (JPanel) getContentPane();
ImageIcon idleImage = new ImageIcon(getClass().getResource("img.gif"));
// Set window properties
getRootPane().putClientProperty("Window.shadow", false);
setBackground(new Color(0,0,0,0));
setAlwaysOnTop(true);
contentPane.setOpaque(false);
// Get size of user's screen
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice defaultScreen = ge.getDefaultScreenDevice();
Rectangle screen = defaultScreen.getDefaultConfiguration().getBounds();
int taskbarheight = (int) (Toolkit.getDefaultToolkit().getScreenSize().height
- GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().getHeight());
int x = (int) screen.getMaxX() - idleImage.getIconWidth();
int y = (int) screen.getMaxY() - idleImage.getIconHeight() - taskbarheight;
JLabel imageLabel = new JLabel(idleImage);
setSize(idleImage.getIconWidth(), idleImage.getIconHeight());
contentPane.add(imageLabel);
setLocation(x, y);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
Creature cr = new Creature();
cr.initCreature();
}
});
}
}
The frame rates seem the same in both versions (one seen in a browser, the other in a JLabel). The actual problem is that the rendering in Java becomes 'clipped' in the areas of the animation that do not change.
At a guess I'd say the problem is that PhotoShop is using an advanced encoding to ensure that only the parts of the frame that change, are updated. Note that although Java supports a particular file type like GIF, PNG or JPEG, does not mean it correctly understands every encoding type for each file type.
A simpler image that, for every change, changes the entire frame should work better, but also be larger in bytes. In fact, you can see that working in the other image. Because the little dragon is hunching down then rising back up, all parts of the visible image need to change, so the animation of that cannot be optimized in the same way as the first image can.
The imageLabel doesn't know to repaint when the GIF's frame changes. You need to tell the ImageIcon to notify the JLabel of frame changes, by passing the JLabel to setImageObserver:
idleImage.setImageObserver(imageLabel);
From that method's documentation:
Set this property if the ImageIcon contains an animated GIF, so the observer is notified to update its display.
I am attempting to write a program that will loop through lots of images and apply various operations to them and then store use the result to train a self organizing map, i wish to write a front end to this program that for each image i am processing it will display the original and it will display the resultant image after i have applied the operations to it, so far i have written a GUI and from what i have researched it should display the image ( which i have scaled because some can be rather large ) on the left of the screen but nothing is displayed apart from the text labels i was hoping for some incite into what is going wrong as i am new to programming GUI's.
This is what i get when i run the program, what i would like is a image displayed below the "Origonal Image" label
This portion of code just handles the initialization as you can see an panel is added to the frame all the program is inside here. When the panel is initialized the following code is run, note that this is inside the ImageComparatorPanel class
public ImageComparatorPanel() throws FileNotFoundException, UnsupportedEncodingException
{
setLayout(new BorderLayout());
origonalImage = new JLabel( new ImageIcon() );
leftTitle = new JLabel("Origonal Image");
rightTitle = new JLabel("Shrunken Image");
ButtonListener listener = new ButtonListener();
start = new JButton("Start!");
start.addActionListener(listener);
JPanel left = new JPanel();
left.add(leftTitle);
left.add(origonalImage);
//add(leftTitle, BorderLayout.WEST);
//add(origonalImage, BorderLayout.WEST);
add(left, BorderLayout.WEST);
add(rightTitle, BorderLayout.EAST);
add(start, BorderLayout.NORTH);
setPreferredSize(new Dimension(800,800));
}
in the main part of my program ( the bit that is executed when the start button is pressed ) the bit of code that is supposed to update the image is as follows
origonalImage.setIcon(getImage(imagePath));
the getImage function opens the image and shrinks it so that it will fit on the panel the code for this is ( thought i should include this just in case...
public ImageIcon getImage(String URL) throws IOException
{
double scale = 0.5;
File f = new File(URL);
Image image = ImageIO.read(f);
ImageIcon icon = new ImageIcon(image);
int h = icon.getIconHeight();
int w = icon.getIconWidth();
Image newImg = icon.getImage();
Image scaled = newImg.getScaledInstance((int)(w * scale), (int)(h * scale), Image.SCALE_SMOOTH);
ImageIcon newIcon = new ImageIcon(scaled);
return newIcon;
}
How can i change this so on each iteration of the loop in the RUN function the image displayed in the GUI will be updated?
You state:
How can i change this so on each iteration of the loop in the RUN function the image displayed in the GUI will be updated?
To change a GUI's visible state every x msecs, you need to either use a Swing Timer or a background thread such as a SwingWorker.
The Timer would be used if your code being called intermittently is not long running and does not tie up the Swing event thread (the Event Dispatch Thread or EDT) inordinately. If the code being called periodically does take time to run, then this technique will tie up the EDT making your GUI completely unresponsive. In this case, use the SwingWorker when you do in fact need to do heavy processing between image changes, and then use the SwingWorker's publish/process method pair to obtain and display updated images. A properly created SwingWorker will run the heavy lifting code in a thread background to the EDT, but will allow you to make Swing calls on the EDT when necessary.
If your main problem is just that no images are showing, then you're going about solving this wrong: you shouldn't be trying to solve this in a huge complex program but instead create a small program that just tries to show an image and nothing else. Solve each small sub-problem in a step-wise fashion, and only when solved, add it to the greater whole, the large program.