Reputation: 41
I am working on a school project on limited time, leading to obfuscated code and much confusion. I am trying to have two jFrames open, one displaying a chat interface, the other displaying an image. Upon a call to actionPerformed()
from the first window, I would like to call a method which changes the image that is on the second window, several times, with some delays in between. The image, however, does not change.
I think my issue has arose due to using a piece of example code for the text window, and trying to incorporate my own modifications, while not entirely understanding the former. In trying to look up this question, I only found people updating their jFrame based off of a timer and in actionPerformed()
, which is not really the behavior I desire.
Here is the offending code:
package main;
import java.awt.event.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
public class Runner extends JPanel implements ActionListener {
JFrame fFrame = new JFrame("Leonardo");
protected JTextField textField;
protected JTextPane textArea;
private final static String newline = "\n";
Dictionary dict = new Dictionary();
StyleContext uContext = new StyleContext();
StyleContext rContext = new StyleContext();
Style uStyle;
Style rStyle;
JLabel lbl=new JLabel();
public Runner() {
super(new GridBagLayout());
styleInit();
textField = new JTextField(20);
textField.addActionListener(this);
textArea = new JTextPane();
textArea.setEditable(false);
textArea.setBackground(Color.BLACK);
JScrollPane scrollPane = new JScrollPane(textArea);
//Add Components to this panel.
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
add(textField, c);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(scrollPane, c);
}
public void actionPerformed(ActionEvent evt) {
String text = textField.getText();
StyledDocument doc = textArea.getStyledDocument();
try {
doc.insertString(doc.getLength(), "You: " + text + newline, uStyle);
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
textArea.setStyledDocument(doc);
try { //this is the section which is not working properly
changeLeonardo("Leonardo_Thinking.jpg");
repaint();
TimeUnit.SECONDS.sleep(1);
for(int i = 0; i<3; i++){
changeLeonardo("Leonardo_Talking1.jpg");
TimeUnit.SECONDS.sleep(1);
changeLeonardo("Leonardo_Talking2.jpg");
TimeUnit.SECONDS.sleep(1);
}
} catch (InterruptedException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
doc.insertString(doc.getLength(), "Leonardo: " + Logic.respondIntelligent(text, dict)+ newline, rStyle);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
textArea.setStyledDocument(doc);
textField.selectAll();
textArea.setCaretPosition(textArea.getDocument().getLength());
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event dispatch thread.
*/
private void styleInit(){
uStyle = uContext.addStyle("User", null);
uStyle.addAttribute(StyleConstants.Foreground, Color.WHITE);
uStyle.addAttribute(StyleConstants.FontFamily, Font.SANS_SERIF);
uStyle.addAttribute(StyleConstants.FontSize, new Integer(16));
rStyle = rContext.addStyle("Robot", null);
rStyle.addAttribute(StyleConstants.Foreground, Color.GREEN);
rStyle.addAttribute(StyleConstants.FontFamily, Font.MONOSPACED);
rStyle.addAttribute(StyleConstants.FontSize, new Integer(20));
}
private void changeLeonardo(String imgName){
BufferedImage img = null;
try {
img = ImageIO.read(new File("C:\\resources\\" + imgName));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("C:\\resources\\" + imgName);
ImageIcon icon=new ImageIcon(img);
lbl.setIcon(icon);
revalidate();
repaint();
}
private void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Leonardo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fFrame.setLayout(new FlowLayout());
changeLeonardo("Leonardo_Idle.jpg");
frame.add(new Runner());
frame.pack();
frame.setVisible(true);
fFrame.pack();
fFrame.setVisible(true);
fFrame.add(lbl);
}
public static void main(String[] args) {
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Runner runner = new Runner ();
runner.createAndShowGUI();
}
});
}
}
Some things to note:
Please excuse any odd names, such as fFrame
this is done to make the code reflect the school project better. All classes outside this, such as Logic or Dictionary work fine. The code throws no errors. I edited the filenames of the images for privacy, but they are full in the code.
I'm sure there are other issues abundant in this code, as I am typically not a graphics expert, but if you could please focus on the update issue, I would greatly appreciate it.
Thank you.
Upvotes: 1
Views: 1752
Reputation: 347332
So, two basic concepts you want to try and get your head around...
The first, in order to call a method on some other class, you need a reference to it. You don't always want to create that reference yourself, sometimes, you just want to be handed a pre-created reference, this allows you to modify the actual underlying implementation of the referenced object without needing to modify the code that relies on it...
This is typically best achieved through the use of a interface
, as it reduces the exposure of the implementation and prevents the rest of the API doing things with the reference they shouldn't, but that's another discussion point...
Basically, you need to pass a reference of the image frame to the graph frame, so that the graph frame can call the image frame when it needs to. In the example below, this is done by the BrowserPane
requiring an instance of the SlideShowPane
to pass to it via it's constructor...
SlideShowPane slideShowPane = new SlideShowPane();
//...
browser.add(new BrowserPane(slideShowPane));
The BrowserPane
can the use this reference to call methods on the SlideShowPane
, in this case in particular, the setPath
method...
slideShowPane.setPath(path);
This will cause the SlideShowPane
to start a new slide show based on the contents of the directory/path past to it.
The second concept is the fact that Swing is a single threaded environment, it is also an event driven environment.
The first part means you can't block the UI thread, otherwise it can't process, amongst other things, repaint requests. The second part highlights a means by which you might be able to work around this.
In this example, I've simply used a javax.swing.Timer
to set up a 1 second delay between images. The timer waits in the background (in it's own thread) and when triggered, will call the registered ActionListener
's actionPerformed
method within the context of the UI thread, making it safe to update the UI from within.
This ensures that the UI thread is not blocked and can continue to process new events, while providing us with a convenient mechanism for performing delayed callbacks
Take a look at Concurrency in Swing and How to Use Swing Timers for more details
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SlideShow {
public static void main(String[] args) {
new SlideShow();
}
public SlideShow() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
Rectangle bounds = gd.getDefaultConfiguration().getBounds();
System.out.println(bounds);
SlideShowPane slideShowPane = new SlideShowPane();
JFrame frame = new JFrame("Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(slideShowPane);
frame.pack();
int x = (bounds.x + (bounds.width / 2)) - frame.getWidth();
int y = (bounds.y + (bounds.height - frame.getHeight()) / 2);
frame.setLocation(x, y);
frame.setVisible(true);
JFrame browser = new JFrame("Browser");
browser.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
browser.add(new BrowserPane(slideShowPane));
browser.pack();
x = (bounds.x + (bounds.width / 2)) + browser.getWidth();
y = (bounds.y + (bounds.height - browser.getHeight()) / 2);
browser.setLocation(x, y);
browser.setVisible(true);
}
});
}
public class BrowserPane extends JPanel {
private SlideShowPane slideShowPane;
private JFileChooser chooser;
private BrowserPane(SlideShowPane pane) {
this.slideShowPane = pane;
setLayout(new GridBagLayout());
JButton browse = new JButton("...");
add(browse);
browse.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (chooser == null) {
chooser = new JFileChooser();
chooser.setMultiSelectionEnabled(false);
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
switch (chooser.showOpenDialog(BrowserPane.this)) {
case JFileChooser.APPROVE_OPTION:
File path = chooser.getSelectedFile();
slideShowPane.setPath(path);
break;
}
}
});
}
}
public class SlideShowPane extends JPanel {
private File path;
private Timer timer;
private List<File> imageList;
private int nextImage;
private Image currentImage;
public SlideShowPane() {
imageList = new ArrayList<>(25);
timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (imageList != null && !imageList.isEmpty()) {
nextImage++;
if (nextImage >= imageList.size()) {
nextImage = 0;
}
System.out.println("NextImage = " + nextImage);
do {
try {
File file = imageList.get(nextImage);
System.out.println("Load " + file);
currentImage = ImageIO.read(file);
} catch (IOException ex) {
currentImage = null;
nextImage++;
ex.printStackTrace();
}
} while (currentImage == null && nextImage < imageList.size());
repaint();
}
}
});
timer.setInitialDelay(0);
}
public void setPath(File path) {
System.out.println("SetPath");
this.path = path;
timer.stop();
imageList.clear();
currentImage = null;
if (path.isDirectory()) {
File files[] = path.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
String name = pathname.getName().toLowerCase();
return name.endsWith(".jpg")
|| name.endsWith(".jpeg")
|| name.endsWith(".png")
|| name.endsWith(".bmp")
|| name.endsWith(".gif");
}
});
if (files != null) {
System.out.println("Found " + files.length + " matches");
imageList.addAll(Arrays.asList(files));
}
}
if (imageList.isEmpty()) {
repaint();
} else {
System.out.println("Start timer...");
nextImage = -1;
timer.restart();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (currentImage != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - currentImage.getWidth(this)) / 2;
int y = (getHeight() - currentImage.getHeight(this)) / 2;
g2d.drawImage(currentImage, x, y, this);
g2d.dispose();
}
}
}
}
Upvotes: 2