gcaglion gcaglion - 2 months ago 7
Java Question

unresponsive GUI when class instantiated outside main()

I am starting learning Java, and I wanted to create a simple camera feed viewer using OpenCV.
MyCV class works just fine when instantiated from its own main() method, or when the call is made from within the main() method of a caller class.
I then built a "MyClient" class, with a main() method and a simple GUI (just a JFrame and a JButton, really), because I want MyCV GUI to be shown when pressing a button in MyClient GUI.

The problem is, when the "caller" class has its own JFrame and associated GUI elements, and I click the button, the whole GUI freezes, the frame from MyCV class shows up empty, and both windows become unresponsive.

I've also tried the class with

SwingUtilities.invokeLater()
, to no avail.

As I said, I'm a novice at Java/Swing, and it looks to me like a paintComponent() issue, but for the life of me, I can't fix it.

Any help is greatly appreciated

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;


public class MyCV {
public static void main(String args[]){
MyCV cv=new MyCV();
}

public MyCV(){
System.loadLibrary( Core.NATIVE_LIBRARY_NAME );
//-- Video capture structure
VideoCapture feed=new VideoCapture(0);
//-- Mat structures
Mat imgFeed1=new Mat();
Mat imgFeed2=new Mat();
Mat imgFeed3=new Mat();
Mat imgFeed4=new Mat();

imgPanel feedPanel1=new imgPanel(); feedPanel1.setPreferredSize(new Dimension(400,400));
imgPanel feedPanel2=new imgPanel(); feedPanel2.setPreferredSize(new Dimension(400,400));
imgPanel feedPanel3=new imgPanel(); feedPanel3.setPreferredSize(new Dimension(400,400));
imgPanel feedPanel4=new imgPanel(); feedPanel4.setPreferredSize(new Dimension(400,400));
JPanel container=new JPanel(); container.setPreferredSize(new Dimension(800,800));
container.add(feedPanel1);
container.add(feedPanel2);
container.add(feedPanel3);
container.add(feedPanel4);

JFrame f=new JFrame("MyChild");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(container);
f.setSize(1300, 800);
f.setVisible(true);

//-- clear resources on exit
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
feed.release();
f.dispose();
}
});

//-- main loop
while(true){
if(!feed.isOpened()) break;
feed.read(imgFeed1);
if(imgFeed1.size().width==0) break;

//-- display images in panels (Colors +Feed +Norm)
feedPanel1.setimagewithMat(imgFeed1);
feedPanel2.setimagewithMat(imgFeed1);
feedPanel3.setimagewithMat(imgFeed1);
feedPanel4.setimagewithMat(imgFeed1);

// repaint frame
container.repaint();
}

}


class imgPanel extends JPanel{
private static final long serialVersionUID=1L;
private BufferedImage image;
//public imgPanel(){ super(); }
private BufferedImage getimage(){ return image; }
public void setimage(BufferedImage newimage){image=newimage; return; }
//-- called method
public void setimagewithMat(Mat newimage){
image=matToBufferedImage(newimage);
return;
}
//--
//--
public BufferedImage matToBufferedImage(Mat matrix) {
int cols = matrix.cols();
int rows = matrix.rows();
int elemSize = (int)matrix.elemSize();
byte[] data = new byte[cols * rows * elemSize];
int type;
matrix.get(0, 0, data);
switch (matrix.channels()) {
case 1:
type = BufferedImage.TYPE_BYTE_GRAY;
break;
case 3:
type = BufferedImage.TYPE_3BYTE_BGR;
// bgr to rgb
byte b;
for(int i=0; i<data.length; i=i+3) {
b = data[i];
data[i] = data[i+2];
data[i+2] = b;
}
break;
default:
return null;
}
BufferedImage image2 = new BufferedImage(cols, rows, type);
image2.getRaster().setDataElements(0, 0, cols, rows, data);
return image2;
}

@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
BufferedImage temp=getimage();
if(temp!=null){
g.drawImage(temp, 0, 0, temp.getWidth(), temp.getHeight(), this);
}
}
}
}


MyClient.java:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class MyClient {
public static void main(String args[]){
//MyCV cv=new MyCV();
//MyClient cli=new MyClient();
JFrame f=new JFrame("My Client");
f.setSize(300, 300);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);

JButton button=new JButton("call Color Detector");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
MyCV cv=new MyCV();
}
});

f.add(button);

}

public MyClient(){
JFrame f=new JFrame("My Client");
f.setSize(300, 300);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);

JButton button=new JButton("call Color Detector");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
MyCV cv=new MyCV();
}
});

f.add(button);

}
}

Answer
while(true){

The above is an infinite loop that runs on the Event Dispatch Thread (aka EDT). Swing is single threaded - all events, painting, etc...occur on a single thread (the EDT) - if that Thread is for any reason tied up doing work none of its other responsibilities can occur (in other words, it looks like your user interface locks up). The main method runs on a separate Thread, so you do not see the same behavior (although all calls to Swing components should be placed on the EDT so I would not necessarily call this the correct way to get around the behavior). Three options:

  1. Start one or more new Threads and place any long running tasks within that thread (note that any calls to Swing Components should be placed on the EDT using SwingUtilities)
  2. Use a SwingWorker
  3. If you wish to do something periodically, such as animation, you can use a java.swing.Timer

An example of 1:

//long running task inside a new Thread
new Thread(new Runnable(){

    @Override
    public void run(){
        while(true){

        }
    }

});