if you add a jpanel instance to the content pane, the paintComponent method is called, right?
content_pane.add (fDrawingPanel);
so why would you call "repaint" at the first line in the run() method? (it says "to paint the loading message, but it should already be painted, because paintComponent was called before, and once fShow is true, we don't set it back to false, so this code is, i think, supposed to be called once ) :
public class MediaTrackApplet extends JApplet
implements Runnable
{
// Need a reference to the panel for the
// thread loop.
DrawingPanel fDrawingPanel;
// Parameters to track the image
MediaTracker fTracker;
Image fImg;
int fImageNum = 0;
boolean fShow = false;
String fMessage ="Loading...";
/** Use a MediaTracker to load an image.**/
public void init () {
Container content_pane = getContentPane ();
// Create an instance of DrawingPanel
fDrawingPanel = new DrawingPanel (this);
// Add the DrawingPanel to the contentPane.
content_pane.add (fDrawingPanel);
// Get image and monitor its loading.
fImg = getImage (getCodeBase (), "m20.gif.jpg" );
fTracker = new MediaTracker (this);
// Pass the image reference and an ID number.
fTracker.addImage (fImg, fImageNum);
} // init
/** If the image not yet loaded, run the thread
* so the run() will monitor the image loading.
**/
public void start () {
if (!fTracker.checkID (fImageNum) ) {
Thread thread = new Thread (this);
thread.start ();
} else
// Unloading/reloading web page can will leave
// checkID true but fShow will be false.
fShow = true;
} // start
/** Use a thread to wait for the image to load
* before painting it.
**/
public void run () {
// Paint the loading message
repaint ();
// The wait for the image to finish loading
try {
fTracker.waitForID (fImageNum );
} catch (InterruptedException e) {}
// Check if there was a loading error
if (fTracker.isErrorID (fImageNum ))
fMessage= "Error";
else
fShow = true;
// Repaint with the image now if it loaded OK
repaint ();
} // run
}// class MediaTrackApplet
/** This JPanel subclass draws an image on the panel if
* the image is loaded. Otherwise, it draws a text message.
**/
class DrawingPanel extends JPanel {
MediaTrackApplet parent = null;
DrawingPanel (MediaTrackApplet parent) {
this.parent = parent;
}// ctor
public void paintComponent (Graphics g) {
super.paintComponent (g);
// Add your drawing instructions here
if (parent.fShow)
g.drawImage (parent.fImg,10,10,this);
else
g.drawString (parent.fMessage, 10,10);
} // paintComponent
} // class DrawingPanel
Thanks
Are you referring to the EDT?
Yes. This venerable, but superannuated, example hails from an era that predates our modern understanding that Swing GUI objects should be constructed and manipulated only on the event dispatch thread. The initial invocation of repaint() has the effect of posting a new Runnable on the EventQueue, while allowing the background thread to finish loading images in anticipation of the subsequent repaint(). See also Memory Consistency Properties and this related example.
Related
first questing for me here. Been searching forever but cant seem to find the answer.
Im working on a school assignment. Got given an ui and are supposed to make the different panels in it do different things in separate threads. Anyway, Im trying to make a triangle rotate inside one of the JPanels. I have managed to draw it and somewhat rotate it, but when I try to make a loop to update it it just blinks and then disappears again. Heres the code Ive written.
StartAssignment, starts the application
public class StartAssignment1 {
public static void main(String[] args) {
Controller controller = new Controller();
}
Controller, recieves calls from the ui and executes various functions in other classes
public class Controller {
private GUIAssignment1 gui = new GUIAssignment1(this);
private RotateShape rotateShape;
private Thread t1;
public Controller() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gui.Start();
}
});
}
public void startT1(JPanel panel) {
rotateShape = new RotateShape(panel);
t1 = new Thread(rotateShape);
t1.start();
}
public void t1Shutdown() {
rotateShape.shutdown();
}
RotateShape, where Im trying to rotate the damned thing
public class RotateShape implements Runnable {
JPanel panel;
private volatile boolean t1Running = true;
public RotateShape(JPanel panel) {
this.panel = panel;
}
private void draw() {
Graphics2D g2 = (Graphics2D) panel.getGraphics();
g2.rotate(Math.toRadians(10));
g2.drawPolygon(new int[] {50, 100, 150}, new int[] {150, 50, 150}, 3);
}
public void shutdown() {
t1Running = false;
System.out.println("Shutdown");
}
#Override
public void run() {
while (t1Running) {
try {
draw();
Thread.sleep(500);
System.out.println("loop working");
panel.repaint();
panel.revalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I'm not certain because I don't have access to your GUI code and therefore cannot test it myself, but here's an idea:
It looks like you're rotating your Graphics2D object by a fixed amount on every re-render (i.e. every invocation of draw()). It's possible that the internal JPanel code initiates the Graphics2D at a default rotation before every re-render, which may be why your rotation only causes the shape to "somewhat rotate."
Maybe try storing a variable for the radian offset (e.g. double offset;) as a member field of the RotateShapeclass, incrementing it on every re-render, then calling g2.rotate(Math.toRadians(offset));?
Addendum 1:
The reason that it may draw on top of the previous render is because the Graphics2D object is not clearing the canvas between re-renders. One way to fix this is to "clear" the canvas at the beginning of the draw() method by filling a rectangle that covers the whole canvas.
Addendum 2:
When you call panel.repaint(), it triggers the paintComponent() method of the JPanel object. So, by calling repaint() and your own draw() method, you are doing two separate renders, which may cause some graphical errors. If you were able to extends the JPanel class and use that, you should define an override to replace draw():
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// TODO: add your rendering operations here
}
This way, calls to repaint() should trigger this method.
Repaint is not calling PaintComponent.
I tried to call it from another method of the Try class too but it did not work out.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class Try extends JPanel {
int i =0;
#Override
public void paintComponent(Graphics g){
//super.paintComponent(g);
System.out.println("hey");
}
public static void main(String[] args) {
JFrame f=new JFrame();
Try t = new Try();
f.setSize(500,600);//400 width and 500 height
Container contentPane = f.getContentPane();
contentPane.add(new PaintComponent());
f.setVisible(true);//making the frame visible
while(true){
t.repaint();
}
}
}
There are multiple issues some of which are mentioned by #trashgod in comments.
You are calling repaint on the instance which you did NOT add to the content pane - you have a different one there (actually, you have something completely different there - new PaintComponent()).
Do not remove super.paintComponent ( g ); unless you clean up the area on your own (pretty much fill the whole component background if it is opaque), otherwise you will get visual glitches on such components upon repaint.
You are spamming repaint operations which is extremely bad, make at least some delay between the repaints giving Swing time to perform the repaints. The best case is if you repaint component only when it actually will display something different visually. If you need to update the view all the time - at least limit it to 30-60 frames (repaints) per second. Also, some internal Swing optimizations might "eat" some of the repaint calls, so expect that you might not see as many paintComponent calls as a number of repaints you call on the component.
You are working with Swing components outside of Event Dispatch Thread (shortly EDT) which might cause issues. Make sure you always use it to create Swing components and call any methods on them. SwingUtilities helps with that.
Any heavy operations that take a long time (or unknown time) to be completed should be executed outside of EDT, otherwise, your UI will simply hang while you are waiting for that operation to complete because all UI updates are performed on EDT and not anywhere else.
Considering all I said above, this is how your example should look like:
public class Try extends JPanel
{
#Override
public void paintComponent ( final Graphics g )
{
super.paintComponent ( g );
final Graphics2D g2d = ( Graphics2D ) g;
g2d.drawString ( Long.toString ( System.currentTimeMillis () ), 25, 35 );
System.out.println ( "repainted" );
}
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
#Override
public void run ()
{
final JFrame f = new JFrame ();
final Try t = new Try ();
f.getContentPane ().add ( t );
f.setSize ( 500, 600 );
f.setVisible ( true );
new Thread ( new Runnable ()
{
#Override
public void run ()
{
try
{
while ( true )
{
t.repaint ();
Thread.sleep ( 25 );
}
}
catch ( final InterruptedException e )
{
//
}
}
} ).start ();
}
} );
}
}
Hope that clarifies it a bit for you.
There is also SwingWorker class that helps to perform long-running tasks in Swing, but I didn't use it here to keep the example as simple as possible.
Also a side note - you do not need to call repaint () within EDT because it sends the repaint request to EDT on its own, so that method is safe to use on any thread (like I do in the example).
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.
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 wanted to draw an image on my panel based on the data I receive from another thread. I am sure the data and consequent pixel array works well, but the repaint() would never work. Can anyone tell me what's going wrong here?
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
/** Create an image from a pixel array. **/
public class PicturePlaza extends JApplet
{
ImagePanel fImagePanel;
ReadCom readComPort;
Thread readPortThread;
public void init () {
// initiate the read port thread so that it can receive data
readComPort = new ReadCom();
readPortThread = new Thread(readComPort,"ReadCom");
readPortThread.start();
Container content_pane = getContentPane ();
fImagePanel = new ImagePanel ();
content_pane.add (fImagePanel);
}
// Tell the panel to create and display the image, if pixel data is ready.
public void start () {
while(true){
if(readComPort.newPic){
fImagePanel.go();
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/** Create an image from a pixel array. **/
class ImagePanel extends JPanel{
Image fImage;
int fWidth = ReadCom.row, fHeight = ReadCom.col;
void go() {
//update the image if newPic flag is set to true
fImage = createImage (new MemoryImageSource (fWidth, fHeight, ReadCom.fpixel, 0, fWidth));
repaint();
readComPort.newPic = false; //disable the flag, indicating the image pixel has been used
}
/** Paint the image on the panel. **/
public void paintComponent (Graphics g) {
super.paintComponent (g);
g.drawImage (fImage, 0, 0, this );
}
}
}
Thanks
Just a little note on repaint(). repaint() schedules a repaint of the screen, it won't always do it immediately in my experience. I found the best solution is to directly call paint() yourself.
Graphics g;
g = getGraphics();
paint(g);
I put this as a new function to call in my code when I wanted it to paint immediately. Also this will not erase the previous graphics on the screen, you will have to do that manually.
Try repaint(); and then validate(); in your Applet (PicturePlaza).