I am having trouble updating a jlabel in a method. here is my code:
JLabel curStatus = new JLabel("");
JButton jbtnSubmit;
public static void main(String[] args) {
test gui = new test();
gui.startGUI();
// gui.setCurStatus("testing!"); << seems to work here,
//but when i call it from another class, it doesn't want to run.
}
// Set up the GUI end for the user
public void startGUI() {
// These are all essential GUI pieces
new JTextArea("");
final JFrame jfrm = new JFrame("my program");
jfrm.setLayout(new FlowLayout());
jfrm.setSize(300, 300);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jbtnSubmit = new JButton("Submit");
jfrm.add(jbtnSubmit);
jfrm.add(curStatus);
jfrm.setVisible(true);
}
public void setCurStatus(String inCurStatus) {
curStatus.setText(inCurStatus);
curStatus.setVisible(true);
}
what is happening, is that the label, curStatus is not appearing. for example, here is a call:
gui1.setCurStatus("Now running diagnostics... Please wait!");
Your problem appears to be one of misplaced references.
Here is how you create your GUI:
public static void main(String[] args) {
test gui = new test();
gui.startGUI();
// gui.setCurStatus("testing!"); << seems to work here,
// but when i call it from another class, it doesn't want to run.
}
You create your "test" object (which should be named "Test" by the way to conform to Java naming conventions) inside of your main method. Since it is declared inside of main, this variable has scope only inside of main and is visible no where else.
You then tell us that you are calling the method like so:
gui1.setCurStatus("Now running diagnostics... Please wait!");
The gui1 variable refers to a test class object but it likely refers to a different object than the test object that is being displayed since the original displayed test object is only refered to by a variable local to the main method.
To solve this, you must make sure to call setCurStatus on the currently displayed test object. How to do this depends on the rest of your code, something you've refused to show us despite our requests for you to do so.
Edit: Based on your latest bit of posted code (which still won't compile for me since it is missing a method, createTasksFile(), my assumptions are correct, you are calling setCurStatus(...) on a gui object that is not the displayed one:
public static String[] runDiagnostics() throws IOException {
gui gui1 = new gui(); // (A)
gui1.setCurStatus("Now running diagnostics... Please wait!");
On line (A) you create a new gui object and call setCurStatus on it, but it is not the GUI object that is being displayed but a completely different and unrelated object. It's only relation is that it is an object of the same class as the one being displayed but that's it. The solution is to get a reference to the displayed GUI and call this method on that object, and that object only.
Also, Robin's assumptions are correct, in that even if you fix this, you're going to be stuck with a Swing concurrency issue. The JLabel won't update because the Swing thread is trying to open a file:
public static String[] runDiagnostics() throws IOException {
gui gui1 = new gui();
gui1.setCurStatus("Now running diagnostics... Please wait!");
int i = 0;
int errorsI = 0;
File f = new File("tasks.txt");
String[] errors = { "", "", "", "", "" };
// try to create the file three times
do {
f.createNewFile();
i++;
} while (!f.exists() && i < 3);
So we're both right. The solution to this is to open your file on a background thread, a SwingWorker would work nicely here.
Edit 2
So to fix the reference problem, pass a reference of the gui into the runDiagnostics method using a gui parameter. Then call the setCurStatus method on this parameter. For example:
public static String[] runDiagnostics(gui gui1) throws IOException {
//!! gui gui1 = new gui(); // !! no longer needed
gui1.setCurStatus("Now running diagnostics... Please wait!");
You would have to pass the GUI in when calling the method:
//!! results = taskBckg.runDiagnostics();
results = taskBckg.runDiagnostics(gui);
Also, please edit all your code so that it follows Java naming conventions. All class names should begin with a capital letter. This makes it much easier for others to understand what your code is doing.
I will have a guess as well based on the message you are trying to display, since the question lacks some essential information. Based on the
"Now running diagnostics... Please wait!"
message, I will assume you are running diagnostics and trying to update the UI on the same thread. The code you posted contains no obvious mistakes which would explain why your call
gui1.setCurStatus("Now running diagnostics... Please wait!");
would not update the label contents.
What you have to do is all explained in the Swing concurrency tutorial. The main point is that you update the UI on the Event dispatch thread, and you never perform heavy calculations on that thread since that will block the UI, leading to a terrible user experience. Heavy calculations should be done on a worker thread, for example by using the SwingWorker class, and only the update of the UI (for example for reporting progress) should happen on the EDT.
With this information and links you should be able to find all relevant information. Also on this site you will find multiple examples on how to use SwingWorker to perform background calculations and updating the UI, like for example my answer on a previous question
Related
I have a jframe i want to display while my main frame is running. i want to pause my main code, until the user does the necessary actions on the other frame. I've read a lot of solutions but i need to see it done for my code to understand and grasp it fully. i do not want to use jdialog like I've seen listed as an answer before. My main goal is to understand better threading so that i can use what i learn in different cases.
With the code I've created, when running the thread, only just the frame loads, none of the other features are there on the frame. (the frame is simple it has a label, a list the user selects from, and a button to basically return the chosen list value.) its like the thread is cut off from completing or something.
here is my class calling the screen:
public class myThread implements Runnable {
String result = null;
public void run() {
MessageScreen ms = new MessageScreen();
ms.setVisible(true);
}
public String getResult() {
return result;
}
public void setResult(String AS) {
result = AS;
}
}
in my main code, a method is called that is returning a String[] value, with this method at some point i have the following code calling the new thread to get the value necessary to return in the original main method:
myThread mt = new myThread();
Thread t = new Thread(mt);
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
myreturn = new String[] {"true", mt.getResult()};
without listing the whole code for the second frame, when the user presses the button, and at the end of the listener tied to the button press the i want to close the frame and return a string that was selected from the list:
jf.dispose();
myt.setResult(AdminSelection);
in the frame class, i have the following instance variables declared:
String AdminSelection = null;
myThread myt;
i hope this is enough information for someone to help me out and understand whats gone wrong here.
The function join() waits until the end of the run() method, when you do t.join(), your thread is already or almost ended. This is because in your run() method there is nothing that blocks the thread until the user has clicked the confirm button. And is better like this!
There is no sense to create a thread here, you should use a callback, or more generally in Java, a listener. You can take a look at Creating Custom Listeners.
But, especially if you want to pause your main code, you should use a (modal) JDialog which is made for this! Don't try to block the UI by yourself, you could block the UI thread (handled by Swing/AWT) by mistake. Creating a JDialog is better because everything is already made for this usage on the UI thread.
Also, you must know that create a Thread is really long, use a Thread when you really need it.
I'm currently trying to build a small program for school. If you click on a checkbox it should show other elements. I learned in python that you need a while loop because the program needs to go over the same lines again where you check if the box is checked but if i put a loop the whole program won't start. I don't understand why.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class test extends JFrame {
private JCheckBox moredetailscheck;
private JTextField inputfielduser;
public static void main(String[] args) {
test venster = new test();
venster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
venster.setSize(800, 600);
venster.setVisible(true);
venster.setTitle("true");
venster.setResizable(false);
}
public test() {
setLayout(new FlowLayout());
moredetailscheck = new JCheckBox("checkbox", false);
add(moredetailscheck);
inputfielduser = new JTextField(15);
while(true) { // you want to let the program keep going over these lines
if(moredetailscheck.isSelected()) {
add(inputfielduser);
}
}
}
If you click on a checkbox it should show other elements.
So, you would attach a listener to the JCheckBox, here an ItemListener, that responds when the state of the JCheckBox changes.
I learned in python that you need a while loop because the program needs to go over the same lines again where you check if the box is checked
This is called "polling" and is needed for linear console programs where you need to continually obtain input from the user, again in a "linear" fashion. In these types of programs, you the programmer are in complete control over program code flow, but that's not what you want here.
but if i put a loop the whole program won't start. I don't understand why.
That's because you're now using an event-driven GUI library, there the Swing library, and by calling a while (true) loop on the event thread, you completely block it, rendering your GUI useless. Your program is starting, but it can't construct the GUI, draw itself or listen for events.
Solution:
Get rid of the while (true) loop. Again, it is useful for simple console programs but not in this situation.
Add an ItemListener to your JCheckBox. You can find out how to do that in the check box tutorial
Don't keep adding items to your GUI. Use a CardLayout to swap views. The tutorial can be found here: CardLayout tutorial.
Or even better, have all the GUI items on the GUI at startup, but use the JCheckBox state to enable/disable an item.
As an aside, you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
For example:
import java.awt.event.ItemEvent;
import javax.swing.*;
public class TestCheckBox extends JPanel {
private static final long serialVersionUID = 1L;
private JCheckBox moreDetailsCheck = new JCheckBox("More Details", false);
private JTextField inputFieldUser = new JTextField(15);
public TestCheckBox() {
inputFieldUser.setEnabled(false);
add(moreDetailsCheck);
add(inputFieldUser);
// add a listener to the JCheckBox
moreDetailsCheck.addItemListener(e -> {
// if checkbox selected, enable the text field. else disable it
inputFieldUser.setEnabled(e.getStateChange() == ItemEvent.SELECTED);
});
}
private static void createAndShowGui() {
TestCheckBox mainPanel = new TestCheckBox();
JFrame frame = new JFrame("Test CheckBox");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
In Java, the AWT starts a thread to handle events automatically; you just let main finish and the program keeps running anyway until you call System.exit. You do need event handlers, though, for which any number of tutorials exist.
(Incidentally, your infinite loop comes before even showing your JFrame.)
I've been making a program that reads from a file, identifies common "posts" in the file, and makes a summary of these. My problem is that the GUI-event that allows the user to specify the name and search-term of the post, does not interrupt the running of the program, like I want it to.
I can make it stop, but then the GUI will not be correctly displayed. I have tried some solutions, which will be specified at the bottom of the post.
EDIT: removed codedump and added something resembeling an SSCCE:
class SSCCE{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
new Gui();
}
});
}
}
class Gui implements ActionListener{
boolean runn=true;
JFrame wind2;
JTextField nameF, searchtermF;
JButton done;
Gui(){
runEx();
}
public void runEx(){
int i =0;
while(runn){
if(i==10) break;
System.out.println("Open window and record information given! One at the time!!!");
System.out.println(" ");
giveName("test");
i++;
}
}
public void giveName(String s){
JLabel nameL = new JLabel("Give this post a name:");
JLabel searchL = new JLabel("What do you want the searchterm to be?");
wind2 = new JFrame("EazyMoney");
wind2.setLayout(new BorderLayout());
JPanel all = new JPanel();
all.setLayout(new GridLayout(2,2));
searchtermF = new JTextField(30);
nameF=new JTextField(30);
all.add(nameL);
all.add(nameF);
all.add(searchL);
all.add(searchtermF);
done = new JButton("Press when you have filled in the information!");
done.addActionListener(this);
String prn = "The post in question: " + s;
JLabel header = new JLabel(prn);
wind2.add(header, BorderLayout.NORTH);
all.setVisible(true);
wind2.add(all, BorderLayout.CENTER);
wind2.add(done, BorderLayout.SOUTH);
wind2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
wind2.pack();
wind2.setLocationRelativeTo(null);
wind2.validate();
wind2.setVisible(true);
}
public void actionPerformed(ActionEvent e){
System.out.println("Action recorded, new window can now be shown. All information stored.");
System.out.println(" ");
}
}
The solutions I have tried is:
A simple block, that does a while(true){} and sets the variable to true after the first instance of g.giveName() have been called. I used the ActionListener to call a method that then changed the variable to false again, when the necessary input was given. This resulted in a gray box, with nothing in it.
Making a cyclic barrier that did the same as the block above. Used a separate thread to call g.giveName() and then call the await() from the action listener. Same result as above.
Making readFile be run by a separate thread and call invokeAndWait() on the g.giveName() function. Gave cannot call invokeAndWait() from the EDT-thread, even though it was run from a new thread.
I can not give examples of the code used in instances above, as I have tried a lot of different solutions and do not have it any more. Please take into account that it might have been implemented wrong, and thus might be a valid answer to my question, even though I could not seem to get it to work!
Final note: all work can be found here, if you wish to test the code:
https://github.com/Robiq/EazyMoneyWork
The way to avoid blocking the EDT if you need to execute something else on the same thread it is to temporarily create a new event queue. Here is some example code. In this case it blocks the current thread waiting for some other event to be signalled but you could replace this with whichever long running process is required.
First check if you are running on the EDT: SwingUtilities.isEventDispatchThread. Then if you are:
EventQueue tempEventQueue = new EventQueue();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(tempEventQueue);
try {
wait();
} catch (InterruptedException ex) {
// stop waiting on interrupt
} finally {
tempEventQueue.pop();
}
Something similar to this is how modal dialogs work in Swing. However in general it's not good practice. Much better is to understand which events to listen for to perform specific actions. In your case the user event should not 'stop' your program - it should disable inappropriate components until the user has responded and then re-enable them.
Question: Ok, so this is going to sound slightly confusing due to my lack of total understanding on the subject. Currently, I am creating a game following a youtube tutorial (to understand the basics of game development in java).
After following some of the tutorials, I am confused as to what is the real purpose of the main method and the class that contains it. I thought that the main method's job was to call all the methods of its own class and the desired methods in other classes. However in my main method in my main "Game" class, here is all I have:
public static void main (String[] args) {
JFrame frame = new JFrame(title);
frame.add(game);
frame.setSize(WIDTH,HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setFocusable(true);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
frame.pack();
game.start();
}
Does frame.add(game) (game is an instance of Game) "call" all the other methods in other classes? Whatl the exactly does frame.add() do? Also, I am still trying to understand threads despite reading a lot of explanations. When I write game.start(); to "start" the thread, what exactly does this imply? Does it call all the methods in the game class only?
Here is my thread code if its needed:
private synchronized void start() {
if(running) {
return;
} else {
running = true;
}
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running) {
return;
} else {
running = false;
}
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit((0));
}
Here are the variables I initialized:
private static final long serialVersionUID = -8921419424614180143L;
public static final int WIDTH = 640;
public static final int HEIGHT = WIDTH / 4*3;
public static String title = "Game";
public static Game game = new Game();
public static GameState state = GameState.MENU;
private boolean running = false;
private Thread thread;
private Renderer gfx;
public static Game getInstance(){
I am just unsure of how all other methods in the game class and my other classes are being called if this is all I have in my main method. Any help would be GREATLY appreciated. I am really struggling here as you can probably tell.
The purpose of the main() method is to give an entry point for stand alone execution.
In your case (you did not show how game is initialized but I guess it was a static Game game = new Game(); initialized field) what happens is that first the static fields are initialized (which will call the Game() constructor).
Then the main method is started. It will set the initialized game as the content of the frame (which means the Frame will call various UI related methods like paint() from that moment on. Threading in Gui is a quite complicated topic, but for the sake of your question you can ignore it).
The rest of the methods in main are used to set up the containing frame. That's unusual for bigger applications but quite common for such small examples. (It does cause methods on game being called, for example for layout or drawing).
So finally main calls game.start() (which is the start() method on Game you have shown). It does create a new thread, which executes (by definition) the run() method on the Runable given to the new Thread(this) constructor (this, which is at this point the same instance as game).
The main() method is like any other method with respect to what it executes. Only what you explicitly execute (call) will be executed.
There is no method to automatically call all the other methods, because there is no purpose for such method. If, for some weird reason, you want such a method, just write it and from within it, do call the other methods.
A thread is a separate execution path in your program. When you call start() it begins and practically it goes to its special run() method, which is executed in parallel.
Any good introductory Java book should have detailed explanations to your questions.
The shortest answers that get at your bolded questions are:
frame.add(...) adds a component (like a button or label) to a frame or panel.
game.start() creates a new thread and calls the run() method of the game class in that thread.
So other methods you've written are called from game.run(). If they aren't in there (and they aren't in main(...)), they aren't being called.
I have to agree with the comment from PM77 though. It sounds like you're beginning in java, and Threading and GUIs are both pretty complex topics. There are basic games you can design (ie tic-tac-toe) that don't require either of them. Try those out before you tackle something as challenging as this.
I want that main should print hello (in a pop up dialogue box) everytime the button is clicked. So I designed the following program but it doesn't seem to work. The program compiles and executes just fine but when I click the button, I don't see the any dialogue box popping up. I have put in place the getter and setter of the boolean variable (button_clicked) whose value is being tested in the main() block.
Please help if you know..
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class abc extends JFrame implements ActionListener{
boolean button_clicked = false;
JButton b1;
abc(){
this.setSize (400, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.createUI();
}
void createUI(){
this.setLayout(null);
b1 = new JButton("x");
b1.setSize(110,30);
b1.setLocation(10,210);
this.add(b1);
b1.addActionListener(this);
}
public boolean isButton_clicked() {
return button_clicked;
}
public void setButton_clicked(boolean button_clicked) {
this.button_clicked = button_clicked;
}
public void actionPerformed(ActionEvent arg0) {
button_clicked = true;
//I don't want to print the dialogue box from here..
//I want to keep everything under main()'s control.
}
}
public class tempMain extends JFrame {
public static void main(String[] args) {
abc temp = new abc();
temp.setVisible(true);
while(true){
if(temp.isButton_clicked())
JOptionPane.showMessageDialog(null, "Hello");
}
}
}
Move the JOptionPane.showMessageDialog() call under the actionPerformed() method and delete the while() thing under the main method.
As has already been pointed out by a number of people, you approach and design are flawed. I won't go into further as it has already been dealt with, but as a suggestion you could try...
abc temp = new abc();
temp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
JOptionPane.showMessageDialog(null, "Look Ma, I'm in Main...");
}
});
temp.setVisible(true);
And in your abc class...
class abc JFrame implements {
// build your class as normal
public void addActionListener(ActionListener listener) {
b1.addActionListener(listener);
}
}
You might want to read about "How to Write an Action Listener" to help you implement an ActionListner in your code.
The basic idea for your code would be to:
Declare an event handler class
Register an instance of the event handler class as a listener with your JButton
Include code that implements the methods in listener interface. In your case, you would over-ride actionPerformed() and write your logic over there to show a dialog box. "How to Make Dialogs" would be another useful tutorial for you.
As #Quinman pointed out, your code design is really flawed. I understand that you do not want to put the JOptionPane under the actionperformed method for some personal reason which I don't understand. Based on that premise, I think that the best solution would be to create a callback, that is, make your JFrame know the main class and call it when the button is clicked.
It is possible to make this mechanism independent of the main class. Please check the Observer design pattern in order to understand how to do that.
Your code also has another flaw - when the button is clicked I get infinite Hello messages. In order to avoid that, you should set the button_clicked variable to false. I only mention that as a general tip, for in truth you really should get rid of the busy wait that your while is causing.
You may be looking for a modeless dialog. In this example, the main panel, named Observer, listens to an ObservedPanel in a dialog. By using a PropertyChangeListener, any changes made to the dialog are immediately reflected in the main panel.
Judging from the comments you provided, you want to reuse your abc class (which has a very poor name and does not comply to the Java naming standards) for several different purposes, so you do not want to include the code which is executed when you press the button in the abc class.
There are multiple solutions for this (where the first is my preferred one)
Pass an Action in the constructor of your abc class and couple that Action to the JButton. This way the class which constructs the abc instance is responsible for the behavior when the button is pressed
make abc abstract and let your ActionListener call that abstract method. You can then make concrete implementations of this class each time you want different behavior.
Further notes on your code:
get rid of that while( true ) loop
get rid of the null layout and use a LayoutManager instead
Swing components should be created and accessed on the Event Dispatch Thread. Consult the Concurrency in Swing tutorial for more information