Multithreading extended panel - java

I'm trying to allocate a JPanel that implements Runnable interface in a JFrame. I'd made this sample for interpret my idea. I want to add a multi-threading panel that shows a text as demo to a window with a String as parameter of a new instance. The panel should have independent process so I implemented Runnable interface. But when I try to create a new instance o panel with a new instance of my class, It doesn't work.
What I am doing wrong?
imagePanel Panel class:
public class imagePanel extends JPanel implements Runnable{
JLabel imageTest;
public imagePanel(String textLabel)
{
this.setPreferredSize(new Dimension(300,300));
imageTest = new JLabel(textLabel);
imageTest.setPreferredSize(this.getPreferredSize());
}
public void setImageText(String newText){
imageTest.setText(newText);
}
public void run(){
this.add(imageTest);
}
}
Main class test class:
public class test {
public static void main(){
JFrame frame = new JFrame("Test Window");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(300,300));
JPanel panel = new imagePanel("Text label");
panel.setPreferredSize(frame.getPreferredSize());
frame.add(panel);
frame.setVisible(true);
}
}

Swing is not thread safe, meaning updates to the UI should only be made from within the context of the Event Dispatching Thread. See Concurrency in Swing for more details and Worker Threads and SwingWorker for a possible solution
Swing layouts are lazy, which is a good thing, they won't update till you tell them (or some other event requires them to update, for example, the container is resized). You can trigger an update by calling revalidate and repaint on the container you are updating
Creating a new instance of class doesn't magically connect all the instances you might have, that would make the program rather difficult to manage

there several things missing
VERY IMPORTANT - you should not update UI in other than the AWT EventDispatcher threads. Using Swing it even may lead to deadlocks. this post
you have to use the SwingUtilities to update the Swing UI components.
SwingUtilities.invokeLater(new Runnable(){
public void run(){
// update your UI components
}
});
Next information - How you create a multi-threaded thread? What you miss is the entry point to run your "run" method:
public class MyMultiThreadedType implements Runnable {
public void run() {
// this will run in parallel
}
public static void main(String[] args) {
MyMultiThreadedType mmt = new MyMultiThreadedType ();
Thread t = new Thread(mmt);
t.start(); // this will start a parallel thread
}
}

Related

Adding JPanel to JFrame works 50% of the time

Below are two images showing the problem I'm facing. Whenever I run the project there is 50/50 chance that my JPanels load properly, otherwise only 1 JPanel is loaded even though I'm simply looping through array and adding JPanels to the JFrame.
viewComponents.forEach(viewComponent -> this.add(viewComponent));
Working
Not working
DashboardView.java
public class DashboardView extends JFrame{
List<ViewComponent> viewComponents = new ArrayList();
ViewComponentFactory viewComponentFactory = new ViewComponentFactory();
JFrame dashboardInput = new JFrame();
public DashboardView(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new FlowLayout());
createGauges(); //Adding 2 JPanels
viewComponents.forEach(viewComponent -> this.add(viewComponent));
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
dashboardInput.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
dashboardInput.setLayout(new FlowLayout());
createInputs(); //Adding JPanels
dashboardInput.pack();
dashboardInput.setLocation(this.getX()+(this.getWidth()/2)-(dashboardInput.getWidth()/2), this.getY()+this.getHeight());
dashboardInput.setVisible(true);
}
private void createGauges(){
viewComponents.add(viewComponentFactory.getViewComponent(ViewComponentFactory.ViewComponentType.RadialCircleGauge,0,800, "Speedometer", "KM/H"));
viewComponents.add(viewComponentFactory.getViewComponent(ViewComponentFactory.ViewComponentType.LinearGauge, -100,100, "Temperature", "Celcius"));
}
Main.java
public class Main {
public static void main(String[] args) {
DashboardView dashboardView = new DashboardView();
dashboardView.setVisible(true);
}
}
The main problem seems to be, that your DashboadView isn't initialized within the EDT - Thread (Event Dispatching Thread). All GUI actions must be done within this thread. Otherwise strange things will happen (e.g. artifacts while updateing UI).
One should initialize its GUI this way, to ensure the start is wihtin the right Thread.
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}

GUI components don't load when another method is loaded

I am working on a game that uses a custom console-style GUI to play. I previously asked a question to find out what was causing a NullPointerException. See this link for context.
I now have the error eliminated, but I have a new issue: When I start the game, the GUI only loads the JFrame (EDIT: JPanel not JFrame) but none of the other components. In the original post, the GUI components loaded up normally, but starting the game itself would cause a NullPointerException due to a call to the JTextArea before the GUI dispatch thread was complete.
Here is the current code:
Classic.java
import javax.swing.*;
import java.util.*;
import static javax.swing.SwingUtilities.invokeLater;
public class Classic extends Game{
private static JFrame gui;
private static GUIClassic newContentPane;
...
public void play() {
invokeLater(Classic::startGUI);
invokeLater(Classic::startGame);
}
public static void startGame() {
//Game processes
...
}
private static void startGUI() {
gui = new JFrame("Karma :: Classic Mode");
gui.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
newContentPane = new GUIClassic();
newContentPane.setOpaque(true);
gui.setContentPane(newContentPane);
gui.pack();
gui.setVisible(true);
}
...
}
GUIClassic.java
...
public class GUIClassic extends JPanel implements ActionListener {
private JTextArea output;
private JTextField input;
private boolean inputReady;
private String inputText;
public GUIClassic() {
super();
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
output = new JTextArea(15, 15);
output.setEditable(false);
JScrollPane outputScroll = new JScrollPane(output);
input = new JTextField("",40);
add(outputScroll);
add(Box.createRigidArea(new Dimension(0,5)));
add(input);
}
}
In the play() method of Classic.java, I tried removing the invokeLater(...) methods, that came up with the same result.
public void play() {
startGUI();
startGame();
}
I also tried moving the call to startGame within startGUI:
public static void startGUI() {
...
startGame();
}
However, removing startGame altogether allows it to start up normally:
public void play() {
invokeLater(Classic::startGUI);
}
I am completely at a loss. I don't understand why loading the GUI without playing the game loads it fine, but beginning the game suddenly makes the components disappear.
Please note: there are no exceptions thrown during runtime.
As a side note, the GUI also does not allow me to close it via the [X] button in the current version, but in the cases where only 'startGUI' is called, the components pop up and the [X] allows you to exit.
Try to invoke the startGame() in a new Thread, because all of the painting stuff is done in the main-thread like that
new Thread(new Runnable() {
#Override
public void run() {
Classic.startGame();
}
}).start();
You never set the size of your Panel.
Try to use the setPreferredSize(Dimension)-method of your GUIClassic before you call the pack()-method of your JFrame.

Why actionPerform() run in Event Dispatch Thread but componentAdded() is not?

I have Ex1 below:
main(String args[]) {
JFrame frame = new JFrame("Title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Press Here");
ContainerListener container = new ContainerAdapter() {
public void componentAdded(final ContainerEvent e) {
System.out.println("On the event thread? : " +
EventQueue.isDispatchThread());
}
};
frame.getContentPane().addContainerListener(container);
frame.add(button, BorderLayout.CENTER);
frame.setSize(200, 200);
System.out.println("I'm about to be realized: " +
EventQueue.isDispatchThread());
frame.setVisible(true);
}
My result is: On the event thread? : FALSE | I'm about to be realized: false
Other Ex2:
public class GridBagLayoutTester
extends JPanel implements ActionListener{
public GridBagLayoutTester() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JButton button = new JButton("Testing");
// do something...
button.addActionListener(this);
add(button, gbc);
}
public void actionPerformed(ActionEvent e) {
System.out.println("On the event thread? : " +
EventQueue.isDispatchThread());
}
public static void main(String[] args) {
JFrame frame = new JFrame("GridBagLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
frame.setSize(800, 600);
frame.pack();
frame.setVisible(true);
System.out.println("I'm about to be realized: " +
EventQueue.isDispatchThread());
}
}
result is: I'm about to be realized: false | On the event thread? : TRUE
My question is why Ex1- componentAdded() run in Intial Thread, but Ex2- actionPerformed() run in EDT ?
Few facts about GUI applications in Java:
- In Java GUI applications the main() method is short-lived, after scheduling the construction of GUI in the Event Dispatcher Thread (EDT) it quits.
- So its EDT's responsibility to handle the GUI.
Now coming to your code:
- Initial Thread is the main() thread, and EDT is the GUI Thread.
- In EX1 you are forcing the GUI to run on the main() thread which is a wrong way to do it, where as in EX2 when you use the GridBagLayoutTester which extends JPanel, main() thread gets an early chance to quite by delegating the work of GUI to the EDT.
- main() method should be used on to execute the EventQueue.invokeLater() method which further deals with the GUI, this will help the UI to be responsive and avoid dealing with any Non-UI work.
- Moreover Java has SwingUtilities Class that does a great synchronization between the UI and Non-UI work on the UI and Non-UI thread respectively.
Eg: The proper way of doing it.......
public class Test extends JFrame{
public Test(){
this.setSize(300,300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
new Test().setVisible(true);
}
});
}
}
Your very first line in the main method creates a new object of type JFrame. This creation starts a new thread (in reality it starts more than one thread) - a new thread that waits for event queue items. This can be a mouse click for example.
To answer your question: The main thread - which is really called "main" - is invoking your 10 lines of code of the main method. This should be finished in some milliseconds. After that the main thread is gone, not existend anymore.
But as I said before, the AWT/Swing library has internally created one (yes, more) thread that is basically an ininite loop checking for user input. And the actionPerformed method is invoked from this thread.
My suggestion for you:
Create a breakpoint in your first line of the main method.
Debug your program.
When the debugger stops at line one (before JFrame is created) go to your command line and start jconsole
go to tab threads
notice thread "main"
execute single line (new JFrame)
notice coexistence of thread "main" and thread(s) "AWT-*"
press play on debugger and "main" will be gone but AWTs will persist

How to instance more than one JFrame with an animation using threads in java?

I have this code here that instance a JFrame with a simple animation, where you click the button and keeps running by using thread. I made an UI with a button that instances the JFrame, the problem is, when I click the button again and it instances another JFrame, the animation of the second one doesn't work properly. Even if I instance, like, 5 JFrames, all of the buttons to start the animation works only with the first JFrame. What I want to do is instance as much as I want to and all of them work separately. Here is the code of the class with the animation:
public class duduleo extends JFrame implements ActionListener, Runnable{
JButton imagens;
int x=1;
static ImageIcon icon[] = new ImageIcon[11];
static JLabel l;
boolean anima=false;
public static void main(String args[]){
JFrame janela = new duduleo();
janela.show();
}
duduleo(){
icon[0]= new ImageIcon("duken1.jpg");
icon[1]= new ImageIcon("duken2.jpg");
icon[2]= new ImageIcon("duken3.jpg");
icon[3]= new ImageIcon("duken4.jpg");
icon[4]= new ImageIcon("duken5.jpg");
icon[5]= new ImageIcon("duken6.jpg");
icon[6]= new ImageIcon("duken7.jpg");
icon[7]= new ImageIcon("duken8.jpg");
icon[8]= new ImageIcon("duken9.jpg");
icon[9]= new ImageIcon("duken10.jpg");
icon[10]= new ImageIcon("duken11.jpg");
setSize(100,200);
setLocation(200,150);
getContentPane().setLayout(new GridLayout(2,1));
imagens = new JButton(icon[0]);
l= new JLabel(icon[0]);
imagens.addActionListener(this);
getContentPane().add(l);
getContentPane().add(imagens);
}
public void run(){
while(anima){
for(int i=0;i<icon.length;i++){
l.setIcon(icon[i]);
try{
Thread.sleep(100);
}catch(Exception e){}
}}}
public void actionPerformed(ActionEvent e){
anima=!anima;
Thread t;
t = new Thread(this);
t.start();
}}
Thanks for any help.
I believe you are supposed to use Swing's invokeLater(), otherwise they get put on the event dispatch thread.
Take a look at the example here for a grasp on the idioms: http://www.java2s.com/Code/Java/Swing-JFC/Swinginvokelater.htm
Also see:
use of invokeLater
Should we use EventQueue.invokeLater for any GUI update in a Java desktop application?
When to use SwingUtilies.invokeAndWait/invokeLater
Should i use SwingUtilities.invokeLater() inside of SwingWorker.doInBackground()?

(Java) Appearing/Disappearing JLabel in a JPanel only disappears on resize

I'm working in Java, and I have a JPanel in a JFrame. In that JPanel, among other things, I have a JLabel that I want to make appear and disappear at will. I've tried setting visibility to true/false, adding and removing it from the JFrame and JPanel, and, having looked online, I tried validate()ing and revalidate()ing ad infinitum. What can be done here to solve this problem?
In general, calling the setVisible method is sufficient to make a Swing component to be shown or hidden.
Just to be sure that it works, I tried the following:
public class Visibility {
private void makeGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel l = new JLabel("Hello");
final JButton b = new JButton("Hide Label");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
l.setVisible(false);
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(l, BorderLayout.CENTER);
f.getContentPane().add(b, BorderLayout.SOUTH);
f.setSize(200, 200);
f.setLocation(200, 200);
f.validate();
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Visibility().makeGUI();
}
});
}
}
The above program is able to affect the visibility by clicking on a JButton.
Could it be a Threading Issue?
My next suspicion was that perhaps a Thread that is not on the event dispatch thread (EDT) may not be affecting the display immediately, so I added the following after initializing the JLabel and JButton.
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
b.setVisible(!b.isVisible());
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception /* }
}
}
});
t.start();
With the new Thread running, it changed the toggled the visibility of the JLabel every 100 ms, and this also worked without a problem.
Calling a Swing component off the event dispatch thread (EDT) is a bad thing, as Swing is not thread-safe. I was a little surprised it worked, and the fact that it works may just be a fluke.
Repaint the JPanel?
If the JLabel's visibility is only being affected on resizing, it probably means that the JLabel is being drawn only when the JPanel is being repainted.
One thing to try is to call the JPanel's repaint method to see if the visibility of the JLabel will change.
But this method seems to be just a band-aid to a situation, if the main cause is due to a thread off the EDT is attempting to make changes to the GUI. (Just as a note, the repaint method is thread-safe, so it can be called by off-EDT threads, but relying on repaint is a workaround than a solution.)
Try using SwingUtilities.invokeLater
Finally, probably the thing I would try is the SwingUtilities.invokeLater method, which can be called (and should only be called) from a thread running separate from the EDT, if it wants to affect the GUI.
So, the earlier Thread example should be written as:
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
b.setVisible(!b.isVisible());
}
});
} catch (Exception e1) { /* Handle exception */ }
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception */ }
}
}
});
t.start();
If the change to the GUI is indeed occurring on a separate thread, then I would recommend reading Lesson: Concurrency in Swing from The Java Tutorials in order to find out more information on how to write well-behaving multi-threaded code using Swing.
setVisible() or removing it should work fine, make sure you are doing it from the event dispatch thread though. There are utility methods in EventQueue for running blocks in that thread.
http://helpdesk.objects.com.au/java/how-do-i-update-my-gui-from-any-thread
You would need to call revalidate() on the parent JPanel if you need its components to be re-laid out.
If you can post an example that demonstrates the problem I can have a look at it for you.

Categories