Ok im really struggling with unlocking my GUI which is locked due to separate SwingWorker thread. Basically what my program does: Initializes webcam, and then grabs single frame and displays it as JLabel icon (doing single grab+display on Button click is ease, however i have immense difficulties in doing such operation consecutively in order to do some image processing). I am aiming to obtain such result:
Grab frame -> process it -> display as ImageIcon of Jlabel > ... repeat Grab frame >...
I need results while webcam is streaming, therefore i used SwingWorker publish and process. In my code "processing part" is not included as it is not necessary since i cant even obtain proper continuous frame grabbing. Generally my background thread will never finish unless cancelled (well thats the assumption as i want to process images as fast as possible with maximum frames per second - unless i should do it other way? - i guess separate thread for single frame grab&process would be bad idea due to fact that im aiming to get 10+ FPS). I know my SwingWorker thread works, since i made tests of saving consecutive images to C:\, and it did work, but my GUI is locked anyway, but at least i know that thread is running and grabbing frames.
Generally i have 2 problems:
no JLabel icon update
Locked GUI
My SwingWorker code:
private class FrameStream extends SwingWorker<Void, BufferedImage> {
#Override
protected Void doInBackground() throws InterruptedException{
BufferedImage processedImage = null;
while (!isCancelled()) {
processedImage = getPIC_COLOR(player);
publish(processedImage);
Thread.sleep(5000); // i made such delay to check whether program is not "choking" with data, but its not the case, without this delay everthing is the same
}
return null;
}
#Override
protected void process(List<BufferedImage> mystuff) {
Iterator it = mystuff.iterator();
while (it.hasNext()) {
img_field.setIcon(new ImageIcon(mystuff.get(mystuff.size()-1)));
}
}
#Override
protected void done() {
infoBAR.setText("FINISHED");
}
}
I am so desperate that i rewritten my code completely basing on tutorials example: Flipper as you can see it is ridiculously similar. All my previous efforts also locked gui so tried the 'tutorial way', nonetheless it did not work, which is a pity. Im in a dead end because i have no clue how to fix that. Im desperate for help since as you can see it seems exactly the same as the tutorial one, maybe something other causes issues: MY FULL CODE
Please help, i'm unable to solve it by myself.
One thing that looks a little different to me is your process method. Rather than specifying the last image, you might wish to iterate through all images like so:
#Override
protected void process(List<BufferedImage> mystuff) {
for (BufferedImage bImage : mystuff) {
img_field.setIcon(new ImageIcon(bImage));
}
}
I'm not sure if this will make a significant difference, but I believe that this is how process should be written. Another thought -- if the BufferedImages are large you may wish to create the ImageIcon in the background thread and publish the ImageIcon rather than the BufferedImage.
Nothing stands out as an issue. Steps I would suggest to further debug.
Instead of using a loop and the process method, just pull one frame and set the Icon once. You need to get that working before you try to make it loop. Your application should be able to set the icon that fact that it doesn't highlights a problem.
Second, because your GUI is locking up, there is something still happening on the EDT. Try, running a profiler or adding System.out.println in your code to find what is running while the GUI is locked.
Last, make sure you don't need to be repainting, revalidating the Label's container to make the image visible.
hmmmm, if I read your last post about this issue, there are maybe lots of possible mistakes (and I see your code), that's nothing to do with SwingWorker, another example just replace change Color for JButton#setBackground() with your Icon or ImageIcon
Have you tried to set the label to observe the image icon?
icon.setImageObserver(label);
Related
In my application I NEED to make sure that Canvas is actually appeared on the screen before going for the next step. The reason is that I need to retrieve an AWT Drawable - i.e. the native window surface of that canvas. And that drawable would be invalid if the canvas is not visible, even if it has the flag "visible" set to true.
The problem Im having is that I can't be sure when Java actually decides to display it.
I even made a code to actually delay after Canvas constructor, but what happens is that it is displayed AFTER the delay has expired.
canvas3d = new Canvas3d();
System.err.println("Before sleep");
try
{
Thread.sleep(3000);
}
catch (Exception e) {}
System.err.println("After sleep");
canvas3d.Init();
This is odd, because the drawing and rendering of elements should be handled by a completely separate thread, and my main thread sleeping should theoretically give it enough time to process the stuff. But it still waits for some reason.
Adding Repaint() or revalidate() doesn't help.
The problem with this is that sometimes, the canvas gets displayed before the Init() gets to the point that it needs it to be visible. But other times - it doesn't get displayed before that.
Can I somehow FORCE the application to display the element before I'm doing anything else? Or can I somehow retrieve if the canvas is displayed at the moment or not? Because checking Canvas.visible is pointless - whether it's true or false, it doesn't mean that it's actually displayed or hidden in that precise moment during runtime. It's just a flag showing that it's supposed to be displayed, but oh, oopsie, Java just can't guarantee that it actually is!
It seems that I can use canvas.isShowing() to test if it's actually visible on the screen. At least by checking that parameter I got the result I wanted (so far).
I am still not certain if that's enough and if there's something more that can go wrong on the low level, particularly if I try to access the surface during X11 rendering pipeline where AWT has already completely "displayed" the component but X11 hasn't yet finished drawing it on the screen.
That's one concern.
But for now, it seems to be the best answer I can come up with.
Currently I have a start menu for a game with a button which transform my menu background image from a PNG into a GIF after a button press. Now, I want my code to wait until the GIF animation is over. After that, it should continue normally (by opening a new JFrame with the actual game).
I've read some stuff about a swing timer, although I'm unsure of how to implement this as I am a Java beginner.
private ImageIcon a = new ImageIcon("a.png");
private ImageIcon b = new ImageIcon("b.gif");
class AddInterestListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
bgLabel.setIcon(b); //replace PNG with GIF
//This here is where I want my delay to happen without freezing the GUI
JFrame game = new JFrame(); //Actual game frame starting etc.
}
}
Any suggestions?
You can't use blocking methods like Thread.sleep() or Thread.join() in Swing EDT. This will freeze the UI as Swing EDT handles all the UI refresh events.
You must implement this with events, first event blocks the UI second unblocks it. If you know the GIF animation length you can use SwingUtils.invokeLater() to run an async thread with that will fire the event after a fixed delay.
[Edit] apparently there was already an answer and I don't know if that answer , or my answer for that matter, fits in your software. One thing I can say for sure is that my method allows you to use custom scaling algorithms etc instead of the built-in one (but you probably don't need that either).
Afaik it is not possible to monitor the progress of a GIF displayed using SWING. You'll have to make your own GIF decoder/animator in order for you to 'detect' when the GIF is about to loop or end (yes animated GIFs can end).
For that I used a 3rd party loader from https://github.com/DhyanB/Open-Imaging to obtain the individual frames and timing information. No guarantee that this is still the best library out there as I found this little over a year ago.
From there on you'll need to write your own animator using javax.swing.Timer or similar.
I cannot seem to force a layout in Swing. I have a JComponent added to a JLayeredPane and I set a border on the JComponent. Then, I want to immediately re-draw everything - not in the sense of "please do this asap" like invalidate(), but synchronously and immediately. Any help? I cannot seem to find the right method of doing this, and all my reading about invalidate(), validate(), repaint(), doLayout(), etc is just confusing me more!
According to this (see the section titled "Synchronous Painting") the paintImmediately() method should work.
The most reliable way to get Swing to update a display is to use SwingUtilities.invokeLater. In your case, it would look something like
SwingUtilities.invokeLater(new Runnable {
public void run() {
somecomponent.repaint();
}
});
I realize the 'invokelater' does not exactly sound like it does anything immediate, but in practice, events posted in this way tend execute pretty quickly compared to just calling e.g. somecomponent.repaint() directly. If you absolutely must make your control code wait for the GUI to update, then there is also invokeAndWait, but my experience is that this is rarely necessary.
See also: document on event dispatch in Swing.
This is super old, but I found a simple solution, which I'll show with a JPasswordField:
var pw = new JPasswordField();
...
pw.paint(pw.getGraphics()); // paints immediately
I wrote a GUI-based Java program that takes 25 screenshots per second and saves them at a user-defined location.
It works pretty well except that it has two problems:
The mouse cursor is missing from the images and I know it will be because BufferedImages do not contain cursor information in them. They have to be added programatically.
The thread that takes screenshots is a daemon thread. So if I close the application, the thread is killed and the PNG image that was being written gets corrupted. I want to avoid that.
Here are the images of my application:
The award-winning, intuitive GUI:
The high-definition images captured look like this:
As you can see from the image the cursor information is being displayed in the console using MouseInfo's static methods.
Please let me know how to solve the above mentioned two problems.
After solving the problem, the images now look like this:
The mouse cursor is missing from the images and I know it will be
because BufferedImages do not contain cursor information in them. They
have to be added programatically.
That is correct, you have to add the cursor afterwards. The reason for this is that a screenshot taken with the Robot class never contains the cursor. Not really because "the BufferedImage doesn't contain mouse info". A BufferedImage is a class that contains a raster of pixels.
The thread that takes screenshots is a daemon thread. So if I close
the application, the thread is killed and the PNG image that was being
written gets corrupted. I want to avoid that.
Simply, in the screenshot thread, use a flag that indicates wether it should continue or not. Keep taking screenshots as long as that boolean is set to true. Make sure to make it non-deamon. So, when you close the application, set the flag to false.
Probably the most easy way to do this is to add a WindowListener:
yourFrame.addWindowListener(new WindowAdapter()
{
public void windowClosed(WindowEvent e)
{
screenshotThread.stopTakingScreenshots(); // This methods set the flag
}
}
Also notice that you are not taking the time it takes to make and save a screenshot in count. You use a fixed sleep of 40 milliseconds, but let's say that it takes 4 milliseconds to take and save the screenshot, then you should sleep for only 36 milliseconds. To time how long it takes to make the screenshot, use System.currentTimeMillis(); before and after your takeShot() method and make the difference.
I have developed a simple MIDI application that allows me to play MIDI notes, in order for the user to be able to interact with the UI whilst the MIDI sounds are playing I have put the logic necessary in an anonymous subclass like so:
public static void Play()
{
new Thread(new Runnable()
{
public void run()
{
if (!_sequencer.isRunning())
{
try
{
_sequencer.setSequence(_sequence);
_sequencer.start();
}
catch (Exception e)
{
Logger.Add(e.getMessage());
}
}
}
}).start();
}
Although the music begins to play the UI still fails to respond when I click on a ComboBox for example, I have something similar working fine in C#.
Is there some sort of caveat to thread in Java that would explain the behaviour i'm seeing?
(The Java API I'm using if it helps - javax.sound.midi)
Thanks for your time.
Edit:
Click around the UI a little more and noticed something interesting; everything seems to work fine with exception to two ComboBoxes in the top left most corner, I've tried deleting them and replacing them but makes no difference. The boxes change to blue when clicked (as it would normally) but the drop down box does not appear below it and the colour does not return to it's default when focus is on another UI component. Could it be a bug introduced by NetBeans perhaps?
Example:
Edit 2:
Well after much trial and error I have finally found the cause of the problem, the threading works great. The problem was that NetBeans has somehow realigned my UI components which causes the Window to fill the screen, manually resizing and testing the ComboBoxes showed that they actually worked fine.
Thanks for everybodys feedback!
What does the combo box do when it's clicked? Does it interact with the _sequencer? Furthermore, was your sequencer created on the UI thread? I would suspect that while you're invoking the start method on a separate thread, the sequencer still runs on the context where it was created (i.e. UI thread).
Try creating the sequencer on the playing thread (i.e. NOT the UI thread) and see if that frees up your UI.
Well after much trial and error I have finally found the cause of the problem, the threading works great. The problem was that NetBeans has somehow realigned my UI components which causes the Window to fill the screen, manually resizing and testing the ComboBoxes showed that they actually worked fine.