I Have written a listener for an IMediaReader from Xuggler.
It should show a video in a JPanel what i can add to a JFrame.
I have created this JFrame in the class main:
class Window extends JFrame {
static IMediaReader reader;
static Window main;
public static void main(String[] args) {
new Thread() {
public void run() {
reader = ToolFactory.makeReader("C:/Users/André/Desktop/Detail.wmv");
reader.addListener(new Player(IMediaViewer.Mode.AUDIO_VIDEO, main));
while (reader.readPacket() == null)
do {} while(false);
}
}.start();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
main = new Window();
}
});
}
private Window() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setVisible(true);
setSize(700, 700);
}
});
}
// invoked by Player with the video panel
public void add(final JPanel videoPanel) {
add(videoPanel, BorderLayout.CENTER);
}
}
It shows the video but it only works when i resize the window manually and that is my problem. Else, it shows a small black square.
Using pack() instead of setSize() or invoke repaint doesn't help.
The Code of class Player isn't just from me. I've just changed some things:
public class Player extends MediaListenerAdapter implements IMediaListener, IMediaViewer {
private static Main main;
Player(Mode mode, Main main) {
setMode(mode);
Player.main = main;
}
#Override
public void onAddStream(IAddStreamEvent event)
{
[...]
MediaFrame frame = new MediaFrame(stream, this, main);
[...]
}
#Override
public void onVideoPicture(IVideoPictureEvent event)
{
MediaFrame frame = mFrames.get(event.getStreamIndex());
frame.setVideoImage(event.getPicture(), event.getImage());
}
static class PositionFrame extends JPanel
{
public PositionFrame(Player viewer, Main main)
{
main.add(this);
mFrames.add(this);
}
protected void adjustSize()
{
invalidate();
}
}
private class MediaFrame extends PositionFrame
{
// the video image
private BufferedImage mImage;
// the video panel
private final JPanel mVideoPanel;
// the stream
private final IStream mStream;
// the index of the stream (incase it's closed)
private final int mStreamIndex;
/**
* Construct a media frame.
*
* #param defaultCloseOperation what should Swing do if the window
* is closed. See the {#link javax.swing.WindowConstants}
* documentation for valid values.
* #param stream the stream which will appear in this frame
* #param viewer containing media viewer
*/
public MediaFrame(IStream stream,
Player viewer, Main main)
{
super(viewer, main);
// get stream and set title based it, establish a copy of the
// stream since it lives in a separate thread
mStream = stream.copyReference();
mStreamIndex = mStream.getIndex();
// the panel which shows the video image
mVideoPanel = new JPanel()
{
public void paint(Graphics graphics)
{
paintPanel((Graphics2D) graphics);
}
};
// add the videoPanel
add(mVideoPanel);
// show the frame
setVisible(true);
}
// set the video image
protected void setVideoImage(IVideoPicture picture, BufferedImage image)
{
[...]
}
protected void paintPanel(Graphics2D graphics)
{
if (mImage != null)
graphics.drawImage(mImage, 0, 0, null);
}
}
}
It is made up from the class MediaViewer
http://code.google.com/p/xuggle/source/browse/trunk/java/xuggle-xuggler/src/com/xuggle/mediatool/MediaViewer.java?r=644
EDIT: If I do it like this, it doesn't work without resizing manually.
I'm not shure if I have unterstood you right.
You have this code in two places.
pack();
setSize(700, 700);
The pack() class is wasteful since you are setting the size immediately afterwards. Also, the first time you call in your constructor you haven't even added anything yet.
Try setting the size or calling pack after you have added the video component and after you have made the frame visible.
Based on your edits, I believe the issue is that you are setting the frame visible before the component is in place. Move your setVisible and pack() calls to the end of your constructor for the Main class instead of the add() method.
Based on more edits to your question: As I mentioned in the comments, make sure you separate off the GUI code in a separate call to invokeLater to get things happening on the EDT. Therefore you need to move your IMediaReader creation and thread starting into your main() method, then after that create a new call to SwingUtilities.invokeLater that creates a new Main, class. By the way, Main is a confusing name for a class.
Related
I need to refresh my GUI each time a function is triggered.
This is how I defined the skeleton of the GUI:
public class GUI {
private JFrame frame;
/**
* Launch the application.
*/
public static void runGui() {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GUI window = new GUI();
window.frame.setVisible(true);
window.frame.setSize(800, 600);
window.frame.setTitle("My Title");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public GUI() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
//CODE
}
So, the GUI.runGui() function is called in a function like:
public void myFunction()
{
//CODE
GUI.runGui();
}
The problem is the following: every time this function is called, it generates a new instance of the GUI, so I end up having multiple instances. This is not what I want, since I just need to refresh the content of the GUI that must be only one.
I believe the problem is architectural.
Is there a way to solve this?
Look at what runGui is doing: it's creating and initializing a GUI every time you call it. Pull that one time initialization code out of runGui and into another place that you run once (like the initialize method). And in runGui access a component that you want to refresh (perhaps the content pane) as:
// This needs to become an instance method (non static) in this example in
// order to access the frame
public void runGui() {
EventQueue.invokeLater(new Runnable() {
public void run() {
// Refresh your component here. Here I'm redrawing the
// content pane
frame.getContentPane().repaint();
}
});
}
frame.repaint();
This article contains some repaint examples:
https://www.programcreek.com/java-api-examples/?class=javax.swing.JFrame&method=repaint
The solution is to get the content pane from the frame and then use .repaint(). You can do the following:
JFrame frame = new JFrame("Title");
frame.getContentPane().repaint();
This question may be a simple matter of me lacking a fundamental understanding of Java Swing or Graphics, and if so, I apologize.
I'm trying to develop a GUI application using Java Swing that can be controlled by an external device that sends pitch, yaw, and roll values via bluetooth to the Application. My idea is to create a cursor (perhaps an empty circle) that moves around when the external device moves around. I have no problems with receiving the data from the device, just the part where I need to actually paint something over all of my components.
I figured that a GlassPane was the easiest way to show a cursor over the entire application, and have it move around when the external device is moved around. I use a Thread to capture the data, and I'm trying to call repaint() afterwards, but it doesn't seem to be triggering.
Here is the relevant code:
JFrame:
public class Frame extends JFrame {
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
//Thread myoHandlerThread = new Thread(myoHandler);
//myoHandlerThread.start();
Frame frame = new Frame();
GlassPane glassPane = new GlassPane();
glassPane.setVisible(true);
frame.setGlassPane(glassPane);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Frame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(50, 50, 1000, 650);
/* Code to add and place components */
}
}
And my GlassPane:
public class GlassPane extends JComponent {
private static double pitch;
private static double yaw;
private static double roll;
Point point;
public void setPoint(Point p) {
this.point = p;
}
public void paintComponent(Graphics g) {
if (point != null) {
System.out.println("Test print statement");
g.setColor(Color.red);
g.fillOval(point.x - 10, point.y - 10, 20, 20);
}
}
public GlassPane() {
Thread handler = new Thread(deviceHandler);
handler.start();
}
private Runnable deviceHandler = new Runnable() {
#Override
public void run() {
Hub hub = new Hub("com.garbage");
System.out.println("Attempting to find device...");
Device externalDevice = hub.waitForDevice(10000);
if (externalDevice == null) {
throw new RuntimeException("Unable to find device!");
}
System.out.println("Connected");
DataCollector dataCollector = new DataCollector();
hub.addListener(dataCollector);
while (true) {
hub.run(1000/20); //gathers data and stores in dataCollector
roll = dataCollector.getRoll();
pitch = dataCollector.getPitch();
yaw = dataCollector.getYaw();
Point p = new Point();
p.setLocation(Math.abs(pitch) * 10, Math.abs(yaw) * 10);
setPoint(p);
repaint();
}
}
};
}
What I would like to happen is for a red circle to be drawn somewhere on the GUI depending on the orientation of the external device. At this point, my "test print statement" doesn't fire even once.
My guess is that I'm lacking some sort of basic understanding of Java's GlassPane or even how paint, paintComponent, and repaint even works. Could anyone point out what I'm doing wrong?
The likely cause of your frustration is trying to set the glass pane visible (Swing components are visible by default), before setting it as the frames GlassPane.
The JFrame is likely resetting the glass pane to be invisible, meaning that it won't be painted (no point painting something that's not visible)
Try setting the glass pane visible AFTER you apply it to the frame
Class1 which is a NewJFrame Form and is also a calling class to class2, NewJFrame3 has a Jbutton component, jButton1, for which method actionPerformed has been over-ridden. I call the object with a thread. On calling, Class2, NewJFrame3, a new frame pops up. Problem is when I press the default cross of called class, NewJFrame3, button [X], both screens gets cancelled. I was not using threads before and was calling the layout method for JFrame3 with just object, but it had this problem, so i used thread but it wouldnt work.
Code of calling class (Layout is fine for this and on clicking its button JButton1, class NewJframe3’s layout pops up) :
public class NewJFrame extends javax.swing.JFrame implements ActionListener
{
public NewJFrame()
{
initComponents();
jButton1.addActionListener(this);
}
private void initComponents()
{
//layout code for JFrame1 – gives the desired layout/output
}
public void actionPerformed(ActionEvent e)
{
If(some_condition)
{
NewJFrame3 obj2 = new NewJFrame3();
Thread th1 = new Thread(obj2, "thread1");
th1.start();
}
}
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
new NewJFrame().setVisible(true);
}
});
}//Main ends
}//Calling Class NewJFrame1 ends
Code for called class(same package) :
public class NewJFrame3 extends javax.swing.JFrame implements Runnable
{
public void run()
{
// System.out.println("Inside Run");
this.test();
this.setVisible(true);
}
protected void test()
{
//layout code for JFrame3 – gives the desired layout/output
}
PSVM()
{
//Main method code not relevant here, since layout function is called from run() which gets called on starting thread for this class.
}
} //Called Class NewJFrame3 end
Thanks a ton for your time!
you have to set for defaultCloseOperation, set to DISPOSE_ON_CLOSE in this case, last of JFrames to terminating an Current JVM
your current issue is default value HIDE_ON_CLOSE, then current JVM is still running, consume and increase RAM in PC
use CardLayout or JDialog with parent, and/or setModal/ ModalityTypes instead of two JFrames
I have a public class AppHelper for displaying some help content using a jframe. There is an exit button on the same JFrame which on click disposes the jframe.
The ActionListener is implemented as a static nested class of the class mentioned above.
Also all the components of the help window are defined in the outer class and all of them are private and static. Also the method that shows the help window is static.
Here is some code that I have implemented:
public class AppHelper {
// helper frame
private static JFrame appHelperFrame;
// helper panel
private static JPanel appHelperPanel;
// helper pane
private static JEditorPane appHelperPane;
// exit helper button
private static JButton exitAppHelperButton;
// constraints
private static GridBagConstraints appHelperPaneCons, exitAppHelperButtonCons;
/**
set layout
*/
private static void setLayoutConstraints () {
// defines layout
}
/**
* initialize the helper elements
* #param void
* #return void
*/
public static void initializeElements () {
// initialize constraints
setLayoutConstraints();
// handler
AppHelper.AppHelperHandler appHelpHandler = new AppHelper.AppHelperHandler();
appHelperFrame = new JFrame("App Help");
appHelperPanel = new JPanel();
appHelperPanel.setLayout(new GridBagLayout());
appHelperPane = new JEditorPane();
exitAppHelperButton = new JButton("Exit");
exitAppHelperButton.addActionListener(appHelpHandler);
java.net.URL helpURL = null;
try {
helpURL = new File("AppHelp.html").toURI().toURL();
} catch (MalformedURLException ex) {
Logger.getLogger(AppHelper.class.getName()).log(Level.SEVERE, null, ex);
}
try {
appHelperPane.setPage(helpURL);
} catch (IOException ex) {
Logger.getLogger(AppHelper.class.getName()).log(Level.SEVERE, null, ex);
}
appHelperPane.setEditable(false);
appHelperFrame.add(appHelperPanel);
appHelperPanel.add(appHelperPane, appHelperPaneCons);
appHelperPanel.add(exitAppHelperButton, exitAppHelperButtonCons);
appHelperFrame.setSize(350, 400);
appHelperFrame.setResizable(false);
appHelperFrame.setVisible(true);
}
/**
* TODO
*/
public static void showAboutApp() {
//throw new UnsupportedOperationException("Not yet implemented");
}
/**
*
* Acts as the handler for the help window components
* Implement actionListener interface.
*/
private static class AppHelperHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if(source == exitAppHelperButton) {
appHelperFrame.dispose();
}
}
}
}
The reason of disposing the JFrame instead of setting it invisible is that I dont want this JFrame to consume memory when this JFrame is not in use.
Now the problem is first time I click on the help button (on some other window) the JFrame is shown. Now when I click the exit button on this help window the JFrame is disposed by the handler. Next time I again click on the help button, the help window is not shown. I wanted to know if there is any error in my code or I need to do some thing else.
The javadoc of Window.dispose() states that
The Window and its subcomponents can be made displayable again by rebuilding the native resources with a subsequent call to pack or show.
And that works too, I've tried it. Just call appHelperFrame.setVisible(true) and that's all. If the window is not activated, try calling appHelperFrame.setState(Frame.NORMAL) which will acitvate it.
You only have to call your initializeElements method once though. Your showAboutApp() method should look something like this:
public static void showAboutApp() {
if (appHelperFrame == null)
initializeElements(); // This also makes the frame visible
else {
appHelperFrame.setVisible(true);
appHelperFrame.setState(Frame.NORMAL);
}
}
Final note:
If you always call this showAboutApp() from the EDT (Event Dispatching Thread) then you're good. If you call this from multiple threads, you might want to execute it in the EDT with like SwingUtilities.invokeAndwait() or SwingUtilities.invokeLater() which also ensures synchronization between multiple threads.
I have made an applet name ParentApplet.java whose task is to create a child frame
Child frame coding is defined in ChildFrame.java
ParentApplet.java
public class ParentApplet extends Applet {
ChildFrame frame;
private static int time = 0;
#Override
public void start() {
frame.setVisible(true);
}
#Override
public void stop() {
frame.setVisible(false);
}
#Override
public void init() {
frame = new ChildFrame("Child");
this.setSize(400, 400);
}
#Override
public void paint(Graphics g) {
g.drawString("Child's Info : " + (++time), 50, 100);
g.drawString(frame.getMessage(), 400, 100);
System.out.println(frame.getMessage().isEmpty() ? "Empty" : frame.getMessage());
}
}
ChildFrame.java
public class ChildFrame extends Frame {
private String mess = "";
public ChildFrame(String title) {
super(title);
addMouseListener(new MyMouseAdapter(this));
addWindowListener(new MyWindowAdapter(this));
setSize(300, 500);
}
public String getMessage() {
return mess;
}
public void setMessage(String mess) {
this.mess = mess;
(new ParentApplet()).repaint();
System.out.println("Click");
}
}
MyMouseAdapter.java
public class MyMouseAdapter extends MouseAdapter {
ChildFrame frame;
public MyMouseAdapter(ChildFrame frame) {
this.frame = frame;
}
#Override
public void mouseClicked(MouseEvent e) {
frame.setMessage("Mouse Cliked in Child");
}
}
MyWindowAdapter.java
public class MyWindowAdapter extends WindowAdapter {
ChildFrame frame;
public MyWindowAdapter(ChildFrame frame) {
this.frame = frame;
}
#Override
public void windowClosing(WindowEvent we) {
frame.setVisible(false);
}
}
Now i am unable to reach the paint method again even after calling the repaint method from the ChildFrame class. Please suggest me whether i have done something wrong or some thing i need to understand.
Thanks in advance
Gagandeep Singh
The answer to your question is basically "you don't do that".
The Applet's paint() method is responsible for painting the contents of the actual applet component-- i.e. the visible component that appears in the web page. Your ChildFrame should then have a separate paint() method to paint itself (or in fact, would usually have a Canvas added to it, and that Canvas in turn has its own paint() method).
(Remember that in Java a "Frame" is effectively a "window"-- i.e. a standalone window that opens separately to the web page.)
You can call repaint() on whatever component from wherever you like. This will eventually lead to that component's paint() method being called. In your particular example, you shouldn't call "new ParentApplet()" -- you don't want to call repaint() on some randomly created new applet, but rather on the single already existing one. So change this by passing a reference to your applet into the constructor of ChildFrame which ChildFrame can then hold as an instance variable and re-use when needed:
public class ChildFrame extends Frame {
private String mess = "";
private final ParentApplet parentApplet;
public ChildFrame(ParentApplet applet, String title) {
super(title);
this.parentApplet = applet;
addMouseListener(new MyMouseAdapter(this));
addWindowListener(new MyWindowAdapter(this));
setSize(300, 500);
}
...
public void setMessage(String mess) {
this.mess = mess;
parentApplet.repaint();
}
}
I must admit that so far, it's not immediately obvious why you would have a setMessage() on a separate frame whose purpose is to set the message displayed in the applet. Why not put the setMessage() method on the applet in that case? But maybe you have another reason for doing it your way that isn't apparent so far.