I am working on a project in that I am managing multiple windows in the main class. As you can tell from the title, I have a problem with JButtons in the windows.
The main method will wait for one specific button to be pressed:
public static void main(String[] args){
ButtonWindow bw = new ButtonWindow();
while(bw.buttonClicked() == false);
System.out.println("ok cool");
}
Meanwhile, the button's ActionListener will trigger the boolean d (the one the method buttonClicked() returns) to be set true.
public boolean d = false;
public ButtonWindow(){
JPanel cp;
JButton b;
setContentPane(cp = new JPanel());
cp.add(b = new JButton("Click me"));
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
d = true;
}
});
setSize(200, 200);
setVisible(true);
}
public boolean buttonClicked() {
return d;
}
Now, it seems as if the main method is stuck in the while loop. How is this possible, since "d" had been changed (I checked it in with an extra "System.out.println("" + d);" in the button's ActionListener)?
Thanks in advance
Oh, and also, is there any smarter way of doing this? I don't think continuously calling this method is the most practical thing to do, really.
I am managing multiple windows in the main class.
AN application should only have a single JFrame and the main window for the application.
For secondary windows you would generally use a modal JDialog. Execution will not continue until the dialog is closed.
In many cases you can use a JOptionPane to manage the dialog for you. Read the section from the Swing tutorial on How to Make Dialogs for more information.
You should be doing this:
public static void main(String[] args){
ButtonWindow bw = new ButtonWindow();
}
And
public void actionPerformed(ActionEvent e) {
if(e.getSource().getText().equals("Click me"))
System.out.println("ok cool");
}
Second Approach:-
Make your flag variable static, create new thread class, and loop using the thread which continuously checks class flag variable value. You can also make your flag variable an instance variable but in that case, your object ref will have to be shared across threads.
Related
I'm new to JAVA and trying to learn some concurrency concepts.
I have a simple GUI class that pops-up a window with 1 button which I want to use for pause/continue.
Also, I have a class that extends TimerTask, it looks like below and start with the GUI:
public class process extends TimerTask {
public void run() {
while(true) { /*some repetitive macro commands.. */ }
}
}
Real question is, how can I pause the task onClick of the button and also continue onClick of the button if already paused?
I have taken a step to use a boolean to flag the button as it changes from pause to continue on each click.
But then I had to type a lot of while(button); for busy waiting inside the while()...
Do you think I can make like Thread.sleep() or something but from outside the task thread?
OLD ANSWER
Basically, there is no support for pause and resume on TimerTask, you can only cancel, check here
perhaps you might want to read about threading as that's the alternative I know of that has an interrupt and start features and then you can keep track of the progress of what you're doing to resume where it stopped.
So, I will suggest you go through this link, because you need to understand threading not just copy a code to use, there is a sample code there that will definitely solve your problem also.
Note that running an endless while loop will basically cause your program not to respond, unless the system crashes. At a certain point, the data becomes an overload and the program will overflow. This means it will fail.
.
NEW ANSWER
So, response to the new question, I was able to run a tiny little program to demonstrate how you can achieve something that looks like multithreading when working with SWING.
To rephrase your question: You want to run an indefinite task like let say we're playing a song, and then onclick of a button to pause the song, on click again should continue the song?, if so, I think below tiny program might work for you.
public class Test{
static JLabel label;
static int i = 0;
static JButton action;
static boolean x = false; //setting our x to false initialy
public static void main(String[] args) {
JFrame f=new JFrame();//creating instance of JFrame
label = new JLabel("0 Sec"); //initialized with a text
label.setBounds(130,200,100, 40);//x axis, y axis, width, height
action=new JButton("Play");//initialized with a text
action.setBounds(130,100,100, 40);//x axis, y axis, width, height
f.add(action);//adding button in JFrame
f.add(label);
action.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
if(x){
x = false; //Update x here
action.setText("Play");
action.revalidate();
}else{
x = true; //Update x here also
action.setText("Pause");
action.revalidate();
if(x){ //Using x here to determind whether we should start our child thread or not.
(new Thread(new Child())).start();
}
}
}
});
f.setSize(500, 700);//500 width and 700 height
f.setLayout(null);//using no layout managers
f.setVisible(true);//making the frame visible
}
}
class Child implements Runnable{
#Override
public void run() {
while (x) {
//You can put your long task here.
i++;
label.setText(i+" Secs");
label.revalidate();
try {
sleep(1000); //Sleeping time for our baby thread ..lol
} catch (InterruptedException ex) {
Logger.getLogger("No Foo");
}
}
}
}
in the program that I am writing at the moment, I have 2 JFrames (each in a different class, each has a different purpose, however you could consider the widget frame to be a slave of some sort), one is a main window, and the other is a 'widget' that pops up upon hitting a button in the main window.
I only want one copy of the widget open at one time. I am currently doing this through boolean variables under an actionPerformed action listener. Below is the action listener for the main window.
public void actionPerformed(ActionEvent e) {
if(getOpenWidget() == false){
System.out.println(getOpenWidget()); //test line
widget.initialize(); // please note that the instance "widget" is declared just after "public class MainWindow{" :)
widget.frame.setVisible(true);
setOpenWidget(true);
System.out.println(getOpenWidget() ); // test line
}else{
System.out.println(getOpenWidget());
JOptionPane.showMessageDialog(frame, "There is already an instance of the Booking Widget open.");
}
}
Now the booking widget is open, on the booking widget there is a cancel button. Below here is the action listener for the widget's 'cancel' button.
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
MainWindow ui = new MainWindow();
frame.dispose();
ui.setOpenWidget(false);
}
}
Now, upon hitting my button in the main window again, in theory, the openWidget bool should be false, and allow me to open another window, however in the cancel button action listener, my variable isnt changed. So, am I going about my problem in the right way without making openWidget a static variable?(I should be using getters and setters right?)
What am I doing wrong and what don't I understand about instantiating a new instance of the main window every time I click that button?
Also, my getters and setters are stock standard as follows.
void setOpenWidget(boolean val){
this.openWidget = val;
}
boolean getOpenWidget(){
return this.openWidget;
}
Just pass the reference of MainWindow to the Widget class so that it can update its flag on cancel button.
You are calling setOpenWidget(false) on some other new instance you have created using this line MainWindow ui = new MainWindow();
You should call setOpenWidget(false) using same instance from which you have initialised widget. Pass the reference of MainWindow to widget while creating widget and invoke setOpenWidget(false) using that reference
When you are creating the object of widget within MainWindow you can call it like this:
widget = new Widget(this);
And change the Widget Window Constructor as follows:
MainWindow ui;
public Widget(MainWindow mw)
{
this.ui = mw;
//...initialize btnCancel...
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
frame.dispose();
ui.setOpenWidget(false);
}
});
//..do all other stuffs here...
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
using sleep() for a single thread
I'm having issues with JTextField.setText() when using Thread.sleep(). This is for a basic calculator I'm making. When the input in the input field is not of the correct format I want "INPUT ERROR" to appear in the output field for 5 seconds and then for it to be cleared. The setText() method did work when I just set the text once to "INPUT ERROR" and by printing out the text in between I found it does work with both that and the setText("") one after the other. The problem arises when I put the Thread.sleep() between them.
Here's a SSCCE version of the code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.regex.Pattern;
import javax.swing.*;
public class Calc {
static Calc calc = new Calc();
public static void main(String args[]) {
GUI gui = calc.new GUI();
}
public class GUI implements ActionListener {
private JButton equals;
private JTextField inputField, outputField;
public GUI() {
createFrame();
}
public void createFrame() {
JFrame baseFrame = new JFrame("Calculator");
baseFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPane = new JPanel();
BoxLayout layout = new BoxLayout(contentPane, BoxLayout.Y_AXIS);
contentPane.setLayout(layout);
baseFrame.setContentPane(contentPane);
baseFrame.setSize(320, 100);
equals = new JButton("=");
equals.addActionListener(this);
inputField = new JTextField(16);
inputField.setHorizontalAlignment(JTextField.TRAILING);
outputField = new JTextField(16);
outputField.setHorizontalAlignment(JTextField.TRAILING);
outputField.setEditable(false);
contentPane.add(inputField);
contentPane.add(outputField);
contentPane.add(equals);
contentPane.getRootPane().setDefaultButton(equals);
baseFrame.setResizable(false);
baseFrame.setLocation(100, 100);
baseFrame.setVisible(true);
}
/**
* When an action event takes place, the source is identified and the
* appropriate action is taken.
*/
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == equals) {
inputField.setText(inputField.getText().replaceAll("\\s", ""));
String text = inputField.getText();
System.out.println(text);
Pattern equationPattern = Pattern.compile("[\\d(][\\d-+*/()]+[)\\d]");
boolean match = equationPattern.matcher(text).matches();
System.out.println(match);
if (match) {
// Another class calculates
} else {
try {
outputField.setText("INPUT ERROR"); // This doesn't appear
Thread.sleep(5000);
outputField.setText("");
} catch (InterruptedException e1) {
}
}
}
}
}
}
I'm not actually using a nested class but I wanted it to be able to be contained in one class for you. Sorry about how the GUI looks but again this was to cut down the code. The the important section (if (e.getSource() == equals)) remains unchanged from my code. The simplest way to give an incorrect input is to use letters.
When you use Thread.sleep() you're doing it on the main thread. This freezes the gui for five seconds then it updates the outputField. When that happens, it uses the last set text which is blank.
It's much better to use Swing Timers and here's an example that does what you're trying to accomplish:
if (match) {
// Another class calculates
} else {
outputField.setText("INPUT ERROR");
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
outputField.setText("");
}
};
Timer timer = new Timer(5000, listener);
timer.setRepeats(false);
timer.start();
}
As Philip Whitehouse states in his answer, you are blocking the swing Event Dispatch Thread with the Thread.sleep(...) call.
Given that you've taken the time to set up an ActionListener already, it would probably be easiest to use a javax.swing.Timer to control clearing the text. To do this, you could add a field to your GUI class:
private Timer clearTimer = new Timer(5000, this);
In the constructor for GUI, turn off the repeats feature, as you really only need a one-shot:
public GUI() {
clearTimer.setRepeats(false);
createFrame();
}
Then, actionPerformed can be modified to use this to start the timer/clear the field:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == equals) {
inputField.setText(inputField.getText().replaceAll("\\s", ""));
String text = inputField.getText();
System.out.println(text);
Pattern equationPattern = Pattern.compile("[\\d(][\\d-+*/()]+[)\\d]");
boolean match = equationPattern.matcher(text).matches();
System.out.println(match);
if (match) {
// Another class calculates
} else {
clearTimer.restart();
outputField.setText("INPUT ERROR"); // This doesn't appear
}
} else if (e.getSource() == clearTimer) {
outputField.setText("");
}
}
You're doing a Thread.sleep() in the Swing main thread. This is NOT good practice. You need to use a SwingWorker thread at best.
What's happening is that it's running the first line, hitting Thread.sleep().
This prevents the (main) EDT thread from doing any of the repaints (as well as preventing the next line executing).
You should use a javax.swing.Timer to setup the delayed reaction and not put sleep() calls in the main thread.
I want to build a simple memory game. I want to put a replay button, which is play again the memory game.
I have built a class named MemoryGame and a main class.
Here is the part of the ButtonListener code.
public void actionPerformed(ActionEvent e) {
if (exitButton == e.getSource()) {
System.exit(0);
}
else if (replayButton == e.getSource()) {
//How can I declare it?
}
}
If I declare the replay button as :
new MemoryGame();
It's work fine, but it pops up another windows.
I want to clear the current display and return to the beginning, without a new windows. How can I do that?
EDIT :
I think I need to rewrite the code of my program, because my program does not have the init() method as suggested which is the initial state of the program.
My Java knowledge is very limited and usually I create less method and dump most into a method.
I will try to redo my program.
Thanks for the suggestions.
Show us what is inside the MemoryGame how you create its initial state. Effectively what folks are suggesting here is for you is to have an initial method which will set-up the game state which the MemeoryGame constructor will call. Then on replay-button of the game you call this method.
Something along these lines:
void init(){
this.x = 10;
this.y = 10;
}
public MemoryGame(){
init();
}
public void actionPerformed(ActionEvent e) {
if (exitButton == e.getSource()) {
System.exit(0);
}
else if (replayButton == e.getSource()) {
init();
}
}
one way you can do it although it might be dirty, is to grab your MemoryGame constructor, and put the stuff inside it inside another method, and call that method in your constructor and inside the button event.
as an example i have made the following class and it resets itself with the use of the previous technique:
public class App extends JFrame{
public static void main(String[] args){
new App();
}
public App(){
init();
}
private JButton changeColorButton;
private JButton resetAppButton;
private JPanel panel;
private void init() {
changeColorButton=null;
resetAppButton=null;
panel=null;
this.setSize(200,400);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
panel.setBackground(Color.white);
panel.setPreferredSize(new Dimension(200,400));
changeColorButton = new JButton("Change");
changeColorButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel.setBackground(Color.black);
panel.repaint();
}
});
changeColorButton.setPreferredSize(new Dimension(100,100));
resetAppButton = new JButton("Reset");
resetAppButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
init();
}
});
resetAppButton.setPreferredSize(new Dimension(100,100));
panel.add(changeColorButton);
panel.add(resetAppButton);
this.add(panel);
this.validate();
}
}
what this app does is it has two buttons. one changes the color and the other resets the app itself.
You should think about re-factoring your code so that the MemoryGame class does not create the GUI, then you wont be recreating it whenever you initialise a new Game.
It's a good idea to keep program logic separate to UI code.
What you could do is you could call dispose() on your JFrame. This will get rid of it and go to your title screen like this:
Here's the button code
public void actionPerformed(ActionEvent event)
{
if (closeButton = event.getSource())
{
System.exit(0);
}
if (playAgainButton = event.getSource())
{
Game.frame.dispose(); // Your class name, then the JFrame variable and call dispose
}
}
This will work but you may have a few problems reseting your program. If so then create a reset method where you can reset all your variables and call when playAgainButton is clicked. For example:
public void reset()
{
// Right here you'd reset all your variables
// Perhaps you have a gameOver variable to see if it's game over or not reset it
gameOver = false;
}
I'm making an App. in java , in which there is a Button to which I have added an actionlistener. The ActionEvent it(the button) generates executes some code. Now I want this piece of code to run whenever the app. starts and without pressing the button. I mean, I want to generate the Actionevent (without pressing the button) so that the piece of code the ActionPerformed contains gets executed as the app. start. After that, it may run whenever I press the button.
You can create ActionEvents like any other Java Object by just using the constructor. And then you can send them directly to the component with Component.processEvent(..)
However, in this case I think you are better making your code into a separate function which is called both:
By the ActionListener when the button is pressed
Directly by your application startup code (possibility using SwingUtilities.invokeLater() or SwingUtilities.invokeAndWait() if you need it to happen on the event-handling thread)
This way you don't mix up presentation logic with the business logic of whatever the code does....
Yes it can be done, but it doesn't really make sense, since your goal isn't to press a button or to call an ActionListener's code, but rather to have a common behavior on button press and on program start up. To me the best way to achieve this is to have a method that is called by both the actionPerformed method of the ActionListener and by the class at start up.
Here's a simple example. In the code below, a method disables a button, turns the JPanel green, and starts a Timer that in 2 seconds enables the button and resets the JPanel's background color to its default. The method that causes this behavior is called both in the main class's constructor and in the JButton's ActionListener's actionPerformed method:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionOnStartUp extends JPanel {
private static final int PANEL_W = 400;
private static final int PANEL_H = 300;
private static final int TIMER_DELAY = 2000;
private JButton turnGreenBtn = new JButton("Turn Panel Green for 2 seconds");;
public ActionOnStartUp() {
turnPanelGreen();
turnGreenBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
turnPanelGreen();
}
});
add(turnGreenBtn);
}
public void turnPanelGreen() {
setBackground(Color.green);
turnGreenBtn.setEnabled(false);
new Timer(TIMER_DELAY, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
setBackground(null);
turnGreenBtn.setEnabled(true);
((Timer) ae.getSource()).stop(); // stop the Timer
}
}).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PANEL_W, PANEL_H);
}
public static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ActionOnStartUp());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Usually, the button action event responds to an external event, to notify the application that the user (or rather something or someone) interacted with the application. If your button executes some code that you want to also execute at application start, why not just place everything at it's proper place?
Example:
public class SomeSharedObject {
public void executeSomeCode() { /*....*/ }
}
Set the button with something like
public void setButtonAction(final SOmeSharedObject obj) {
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
obj.executeSomeCode();
}
});
}
And run at application start with something like
public void initApplication(SomeSharedObject obj) {
obj.executeSomeCode();
}
And, if the code you need to execute takes a while to complete, you might want to use a separate thread inside your actionPerformed button event so your application UI does not freeze up.
Just Call JButton.doClick() it should fire the ActionEvent associated with the JButton.