I've customized a JPanel that displays a large, complicated diagram. Depending on the size of the data, it can take a few minutes to render in paintComponent(). I'm looking for a strategy to:
draw the component without tying up the event dispatch thread.
draw something in the JPanel to let the user know the image is being rendered.
periodically update another container's label to show the progress
I've researched this a bit, and I'm wondering if the right strategy is to use a SwingWorker to create a background thread and draw to a BufferedImage. Timers would handle the status updates. Class member variables would hold the status.
Am I on the right track?
You need to look into using a SwingWorker. You should do the rendering of the diagram in a separate thread. The SwingWorker will help accomplish that.
To get started with multi-threading in concurrency, sun has a great tutorial that should prove very helpful.
Use background image which is updated by special working thread. Then in JPanel's paintComponent() method just paint this image. The strategy is called double-buffering. You have background and foreground image. If separate thread finish the painting of data then set this image as foreground and foreground load as background. Invalidate JPanel and continue painting on back image if necessary.
Related
I am making something that resembles a subtitle player that will go over a video.
I would like to make the background transparent so that the box that the text is in will not interfere with the movie/TV show playing behind it. I have tried 2 ways to do this and each way results in the same problem. The text does not disappear when the next sentence appears. If the background is a color (Eg: Color.red), then this works fine. After a certain time, I call text.setText("next sentence"), but this does not work with a transparent background. The relevant code is as follows. text is a JTextField
I should mention the video is not in this program. This program is ONLY subtitles.
First way:
text.setBackground(new Color(0,0,0,0));
Second way: (using a transparent image)
Graphics c = myPicture.getGraphics();
text.paintAll(c);
I update the text like this.
Thread.sleep(Graphix.subtitles.get(counter).getStart());
text.setText(Graphix.subtitles.get(counter).getText());
This also makes it overlap.
text.setForeground(Color.blue);
text.setBackground(new Color(0,0,0,0));
text.setOpaque(false);
The relevant code from Main is as follows.
final JFrame JFwindow = new JFrame("Subtitles");
JFwindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFwindow.getContentPane().add(new Subtitles());
JFwindow.setSize(1300, 150);
JFwindow.setUndecorated(true);
JFwindow.setBackground(new Color(0,0,0,0f));
JFwindow.pack();
JFwindow.setVisible(true);
Transparency is achieved by calling JPanel#setOpaque and passing it false.
You should NEVER be calling getGraphics. getGraphics and return null and is, at best, only as snap shot. Once the RepaintManager starts a new paint cycle, the results of painting to it will be overridden.
Using a tarnsparent color will only confuse the RepaintManager as it won't know that it needs to paint under the component
Sleeping within the Event Dispatching Thread (EDT) will stop Swing from performing any updates (as well as process any events). Instead I'd recommend using a javax.swing.Timer. See Concurrency in Swing for more details.
If you're using the VLC bindings, then it can't be achieved
Don't forget to make all the parent containers that the sub titles are contained in transparent as well
just working on some code to do with java graphics, very simple example from a lecture I had today. Anyway, the internet seems to say that update will not be called by a System trigger such as resizing a frame etc. In this example, update is called by such a trigger (hence update and paint are called when I only expect paint to be called). He seemed to put it down to Operating Systems and different results on each.
Can anyone clarify this for me?
Working on windows 7
Thanks in advance
Ben
Here's a great article that really says it all:
http://java.sun.com/products/jfc/tsc/articles/painting/
1) Painting in AWT
To understand how AWT's painting API works, helps to know what
triggers a paint operation in a windowing environment. In AWT, there
are two kinds of painting operations: system-triggered painting, and
application-triggered painting.
2) System-triggered Painting
In a system-triggered painting operation, the system requests a
component to render its contents, usually for one of the following
reasons:
The component is first made visible on the screen.
The component is resized.
The component has damage that needs to be repaired. (For example, something that previously obscured the component has moved, and a
previously obscured portion of the component has become exposed).
3) App-triggered Painting
In an application-triggered painting operation, the component decides
it needs to update its contents because its internal state has
changed. (For example,. a button detects that a mouse button has been
pressed and determines that it needs to paint a "depressed" button
visual).
4) The Paint Method
Regardless of how a paint request is triggered, the AWT uses a
"callback" mechanism for painting, and this mechanism is the same for
both heavyweight and lightweight components. This means that a program
should place the component's rendering code inside a particular
overridden method, and the toolkit will invoke this method when it's
time to paint.
I don't want to get called events during the render or update step, but before them. I also don't want to only redraw when an event has occurred, but as soon as possible, so I need a loop in my program.
poll events - call functions to associated listeners
update - may be merged with draw
draw
How can I accomplish this?
I don't really understand your problem, but if you want to draw continuously, then you should make a main loop. This thing cannot be made with swing components, but only if you design an event or use a predefined event. In that event, redraw the whole canvas, using validate() when you add a new component and repaint() or paint() when you just want to update the image.
If you don't want to render continuously then just render when "something" happens, such as when an event triggers.
I'm a little confused about the Swing painting model.
Suppose that I have a JComponent comp, and I do something like:
c.setBackground(Color.RED);
c.setBackground(Color.YELLOW);
Obviously, the end result is that the color is yellow.
How does Swing handle this?
Does the first call trigger an immediate repaint so there'll be a short flicker of red before the yellow? Is this sequence significantly slower than just a paint of yellow?
If I was running this from outside the Swing Event thread, I would assume that in most cases (though a race condition is possible), by the time the Swing EDT comes visiting the property is already set to yellow so there'll never be any red painted.
However, my understanding is that I should make these calls from inside a Runnable in the Swing EDT. Is that correct? In that case it would seem like the EDT would have to fully perform each change without any "lookahead"?
The area of the window is marked dirty immediately. A paint request will later come back on the EDT. The OS or the event queue mechanism (or even the component) may merge repaint events (strictly a quality of implementation matter, but in practice repaints in the same window will be merged, even if they do not intersect).
Methods on Swing components should be called on the EDT. The Event Dispatch Thread belongs to AWT, not Swing.
First of all you should not make such calls outside of EDT. The results can be unpredictable.
Since all Swing components are double-buffered you will see no flicker when doing this. In addition to that all consecutive repaint requests are joined into one when possible.
Overall, you shouldn't see any issues at all when doing this as long as it is done on EDT
I have a little java applet where I create 2 threads, one thread repaints and the other moves an image from a point to where the user clicks. The problem is that when I call the move function it loops until the image is where the user clicks but it wont repaint until I break out of the loop even though the thread doing the moving and the thread doing the painting are separate.
shortened version of key points:
my program is an applet using the paint() method
I have 2 threads one moves an image and the other paints that image
when I am moving the image it is in a while loop
the painting thread is still calling repaint() but that is as far as the call goes, it never repaints
thank you for your time.
It might be useful to read an introduction of the painting system of the AWT framework of Java. Take a look for example at th one from Sun: http://java.sun.com/products/jfc/tsc/articles/painting/index.html
In your case you don't need 2 threads. The thread in charge of repainting your applet is created by AWT. It is called the event dispatching thread or EDT. So you just need to change the position of your image and on each change call the repaint method on your applet.