Adding JPanel to JFrame works 50% of the time - java

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);
}
});
}

Related

How do i switch between screens in swing

This has been asked before but I would like clarification, I'm new to java coding (sort of, started coding last month) and would like to know simply how can I switch between UIs in one JFrame, Picture this, a settings menu, How do I make it in one JFrame window instead of just make a new window with all the settings, If you don't get it, ask for clarification.
You can implement a frame (JFrame) and, for example, two panels (JPanel). Initially you embed panel A inside frame, when you want to show panel B then call the method showPanelB()
public class MyFrame extends JFrame {
PanelA panelA = new PanelA();
PanelB panelB = new PanelB();
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MyFrame().setVisible(true);
}
});
}
public MyFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
showPanelA();
}
public void showPanelA() {
getContentPane().add(panelA, BorderLayout.CENTER);
}
public void showPanelB() {
getContentPane().add(panelB, BorderLayout.CENTER);
}
}
class PanelA extends JPanel {
// Panel implementation
}
class PanelB extends JPanel {
// Panel implementation
}

Multithreading extended panel

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
}
}

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.

How do I display multiple JPanels with multiple layouts?

I've been working on this project for an assignment and I've been stuck on this problem. I new and don't understand much of the programming jargon so if someone could help explain why my program isn't working that would be great.
The programs purpose is to display a randomly generated matrix of 1's and 0's in a 10x10 layout and have some buttons on the top that have functions. I'm just stock on how to get everything to display.
Thanks in advance.
UPDATE:: Told providing all my code would help
public class Module5 extends JFrame {
private static JTextArea area = new JTextArea();
private static JFrame frame = new JFrame();
private static JPanel general = new JPanel();
private static JPanel buttons = new JPanel();
private static JPanel numbers = new JPanel();
private static JButton button0 = new JButton("Reset to 0");
private static JButton button1 = new JButton("Resset to 1");
private static JButton buttonReset = new JButton("Reset");
private static JButton quit = new JButton("Quit");
public static class Numbers extends JPanel {
public Numbers() {
area.setText(Integer.toString((int) Math.round(Math.random())));
this.add(area);
}
public void Module5(){
numbers.setLayout(new GridLayout(10, 10));
for (int i = 0; i < 100; i++) {
this.add(new Numbers());
}
}
}
public static void main (String[] args) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
general.setLayout(new BoxLayout(general, BoxLayout.Y_AXIS));
general.add(buttons);
general.add(numbers);
buttons.add(button0);
buttons.add(button1);
buttons.add(buttonReset);
buttons.add(quit);
quit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
}
}
Since this does look like homework I'll give you some pointers but am not going to give you the code.
Move your constructor for Module5 out of the numbers class and into its own class. Also remove the void return type from this to make it a correct constructor.
Move the code in your main into the constructor for Module5. This is the main frame so when you build a new one it should be initialised here, not in main. And remove the setVisible call for now (this is addressed in number 6)
After doing 1 and 2, get rid of your frame variable, your Module5 is a JFrame so anything to do with frame can just be changed to the keyword this (meaning this Module5 object)
Also move the area variable to be within the Numbers class - otherwise every Number is essentially going to share the same text area and This is not what you want.
Don't have your variables as static they should not need to be.
Once this is all done make sure it is running on the Event Dispatch Thread by making your main method like this (the one piece of code I will give you)
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
Module5 mod5 = new Module5();
mod5.setVisible(true);
}
});
}

Using an ActionListener in one class to start a timer in another class

I have a class (simulation) which creates an instance of another class (GUI). Inside the class GUI there is a button (start) which has an actionlistener attached to it.
I need this actionlistener to start a timer in simulation but I can't figure out how to do it.
Code in Class Simulation:
public class Simulation{
private static JFrame frame;
private static GUI control;
public static Integer xcontrol = 100, ycontrol = 100;
public Timer timer;
public int steps;
public static void main(String[] args) {
Simulation sim = new Simulation ();
}
public Simulation() {
frame = new JFrame("Action Listener Test");
frame.setLayout(new BorderLayout(1,0));
control = new GUI (xcontrol, ycontrol);
frame.getContentPane().add(control , BorderLayout.CENTER);
frame.setResizable(false);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void StartTimer() {
timer.start();
System.out.println("It worked!");
}
Code in Class GUI:
panel1.add(button1a);
button1a.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent event) {
Simulation.StartTimer();
}
} );
The error Eclipse tells me there is, is that for "Simulation.timer.start();" :
Cannot make a static reference to the non-static method StartTimer() from the type Simulation.
However the method StartTimer() cannot be static as this seems to break the timer...
Any help would be very appreciated.
Pass this as an argument to the GUI constructor.
In general it is best to avoid such cyclic references. Both the GUI and Simulator become dependent upon one another. The essence of the solution is to separate out the GUI from the interesting domain-specific behaviour.
(BTW: I would strongly avoid using static variables for anything other than constants. Also avoid non-private instance variables. But point for not extending JFrame!)
There is some hideous boilerplate that you should add to prevent multithreading.
public static void main(final String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
Simulation sim = new Simulation();
}});
}
What I would do is have your GUI class expose the button via a getButton() method, then after creating the GUI object, your Simulation class can add its own ActionListener to the button, e.g. control.getButton().addActionListener(new ActionListener()... etc.

Categories