I have my GUI set up so that if the button b1 is pressed:
public class CubeCalc {
static int next = 0;
public static void MakeTitlePage()
{
final JFrame window = new JFrame("Cubic Feet Calculator"); //Creates Frame
JButton b1 = new JButton("Start");
b1.setBackground(Color.decode("#5A20DF"));
b1.setForeground(Color.WHITE);
/*b1.setLayout(new GridBagLayout());*/
b1.setPreferredSize(new Dimension(150,50));
b1.addActionListener(new ActionListener() { // action when button is pressed
int pressCount=0;
#Override
public void actionPerformed(ActionEvent e) {
window.dispose();
next = 1;
}
});
then it will dispose of the title page and and next will equal one, and on the Event Dispatch Thread, it creates a new page that does other things:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() { // launch frame on the Event Dispatch Thread
#Override
public void run() {
MakeTitlePage();
if (next==1)
{
MakeCalcPage();
}
System.out.println(next);
}
});
}
The problem is that the variable next remains equal to zero even though I have changed it in the method MakeTitlePage(). How do I change the variable across all the methods, and not just that one?
I think you might have misunderstood how the event dispatch thread works. When you add a listener to a component then you are telling Swing to listen for certain events and invoke the associated listener on the event dispatch thread. If you are using the static variable next to communicate between threads then, firstly, that's not the way to do it and, secondly, you are communicating to the same thread anyway.
If you want the button to close the current window and open a new one then you should do that directly in the actionPerformed method:
public void actionPerformed(ActionEvent e) {
window.setVisible(false);
showCalculationFrame();
}
Related
I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java. However, I have no idea how I can manipulate these events to stop/continue/start. I want to refrain from moving on to the next line of main() (after the ones which put the Runnable in the EventQueue) until a certain key is pressed.
I put together an example for clarity. What I'd like to do here is spawn the JFrame, allow the user to move the box around with the arrow keys and then press Enter to cease the box-shifting operations, and ONLY then make the calculation at the end of main() and cause the answer to appear. I should be able to get 400, 500, 600, etc. As it is, the calculation is made immediately after the JFrame appears, so the answer is always 300.
I carved out a spot for whatever action should be bound to Enter; it's underneath the declarations for the actions bound to the arrow keys.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EndTheShifter extends JFrame
{
private Color ourRectColor = new Color(28,222,144);
private int ourRectWidth = 50;
private int ourRectHeight = 50;
protected static Point ourRecLocation = new Point(100,100);
// Rectangle object can paint itself
public class Rectangle
{
protected void paint(Graphics2D g2d)
{
g2d.setColor(ourRectColor);
g2d.fillRect(ourRecLocation.x, ourRecLocation.y, ourRectWidth, ourRectHeight);
}
} // Rectangle class
// OurRectangle can create a Rectangle and call paint() on it
public class OurRectangle extends JPanel
{
private Rectangle capableRectangle;
public OurRectangle()
{
capableRectangle = new Rectangle();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
capableRectangle.paint(g2d);
g2d.dispose();
}
} // OurRectangle class
KeyStroke pressRight = KeyStroke.getKeyStroke("RIGHT");
KeyStroke pressLeft = KeyStroke.getKeyStroke("LEFT");
KeyStroke pressUp = KeyStroke.getKeyStroke("UP");
KeyStroke pressDown = KeyStroke.getKeyStroke("DOWN");
KeyStroke pressEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0);
OurRectangle recToWorkWith = new OurRectangle();
// Create InputMap and ActionMap
InputMap inputMap = recToWorkWith.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = recToWorkWith.getActionMap();
// Mapping Shortcut
protected void setTheAction(KeyStroke a, String b, Action c)
{
inputMap.put(a,b);
actionMap.put(b,c);
}
// Constructor!!!
public EndTheShifter()
{
add(recToWorkWith);
Action rightAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.x != 600)
ourRecLocation.x += 50;
else
ourRecLocation.x = 100;
recToWorkWith.repaint();
}
};
Action leftAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.x != 100)
ourRecLocation.x -= 50;
else
ourRecLocation.x = 600;
recToWorkWith.repaint();
}
};
Action downAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.y != 600)
ourRecLocation.y += 50;
else
ourRecLocation.y = 100;
recToWorkWith.repaint();
}
};
Action upAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.y != 100)
ourRecLocation.y -= 50;
else
ourRecLocation.y = 600;
recToWorkWith.repaint();
}
};
/*
Action enterAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
}
}
setTheAction(pressEnter,"enterAction",enterAction);
*/
setTheAction(pressRight,"rightAction",rightAction);
setTheAction(pressLeft,"leftAction",leftAction);
setTheAction(pressDown,"downAction",downAction);
setTheAction(pressUp,"upAction",upAction);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800,800);
setVisible(true);
}
// Main kicks things off by putting all of the above
// in the Event Dispatch thread
// On an enter press, I want the last line of main() to run
public static void main(String[] argv)
{
EventQueue.invokeLater(
new Runnable()
{
#Override
public void run()
{
new EndTheShifter();
}
});
// What I want to trigger only on Enter
System.out.println(ourRecLocation.x + 2*ourRecLocation.y);
}
} // EndTheShifter, our outermost class
and ONLY then make the calculation at the end of main()
That is not the way a GUI works.
The main() method is only used to display the frame.
Once the frame is visible the EDT is started and the frame sits there waiting for user events to be generated.
Your application code then responds to these user events.
I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java.
All code invoked in a listener does execute on the EDT. So the code in your Action does execute on the EDT. You don't need to do anything special.
What I want to trigger only on Enter
Then that logic should be contained in the Enter Action.
I would like to support what camickr said; there is likely a better way to achieve what you are trying to do. That said, if you really want to make your main method wait until the enter key is pressed, here's how:
First, at the top of your file, define an object to use as a synchronization lock like so:
public static final Object LOCK = new Object();
Then, in your main method, before your println statement, put the following code:
synchronized (LOCK) {
LOCK.wait();
}
What this does is it waits until the LOCK object's monitor lock is not being used by any thread (very simplified explanation, read more here), and then it makes the current thread (in this case, the thread that started your main method) wait indefinitely.
Next, add a throws declaration to the method header on your main method:
public static void main(String[] argv) throws InterruptedException
This tells the compiler that your code could throw an InterruptedException, which would happen if your thread was interrupted while it was waiting.
Finally, anywhere in your EndTheShifter constructor, put the following code:
synchronized (LOCK) {
LOCK.notify();
}
This again waits until the LOCK object's monitor lock becomes available, and it then "notifies" all threads waiting on the LOCK object that they may continue. In this case, it will make our main thread continue and execute the println.
I have a Question on performing other buttons action with single button click. Some example code for three buttons:
JButton a = new JButton("a");
a.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of a is Here
}
});
JButton b = new JButton("b");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of b is Here
}
});
Those should come together, like:
JButton c = new JButton("c");
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of c is Here
// Action of a
// Action of b
}
});
In the above example i have three buttons a,b,c with its own action; but as you can see, C also has to run the actions of A and B. What are good ways to address this?
The other answers are all correct, but there is one important aspect missing here: be careful about dong "too many things" on the AWT event dispatcher thread.
Meaning: when a button is clicked, an event gets created, and the UI framework uses that special thread to trigger the registered listeners. If one of the listeners now decides to do a intensive computation ... the UI event threads stays busy doing "that". And while doing "that thing"; this thread isn't available to dispatch any other UI event.
So, this is "not only" about creating methodA(), methodB(), methodC() and invoking them in your third action listener. It is also about understanding if combining multiple calls becomes subject to "I should better run those things in a separate thread; to not block the event dispatcher thread".
In that sense: the other answers tell you where to go from here; but be really careful about the "amount of activity" that your "joined actions" button is about to create!
1) Methods
Use methods for each action and call those in the ActionListener.actionPerformed
public void methodA(){}
public void methodB(){
methodA();
}
2) Action instance
You could create your own classes of ActionListener to perform the actions
First action :
class ActionA implements ActionListener{
public void actionPerformed(ActionEvent e) {
...
}
}
An improved action
class ActionB extends ActionA{
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e); //Will call the first action
...
}
}
This is limited since you can't have multiple extends but is also a nice solution
3) Click
Last but I don't like it, use AbstractButton.doClick to dynamicly click on other buttons.
4) Add multiple action
Just notice that the methods is not a setActionListener but a addActionListener meaning that it will accept multiple ActionListener.
So define create two instances
ActionListener listenerA = new ActionLisener ..
ActionListener listenerB = new ActionLisener ..
buttonA.addActionListener(listenerA);
buttonB.addActionListener(listenerB);
buttonC.addActionListener(listenerA);
buttonC.addActionListener(listenerB);
With a small test, I notice that the actions are execute in the order B -> A (might not be a generality).
As said in comment, this should be us knowing the risk, this will . If an action failed because of an exception, should the next one be executed ? By default it won't because the process will not hide exceptions.
I would restrict this solution to GUI management like reseting fields, disabling, ... that could be use in different buttons.
Whatever you want to do on Button click a, you can put in a method and call it from wherever you want.
public void methodForA(){
// do here what you want
}
You can call this now in the methods you want it to call from. In your case from button click A and button click C
JButton a = new JButton("a");
a.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
methodForA();
}
});
// and also in your c-Button
JButton c = new JButton("c");
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of c is Here
methodForA();
}
});
Create 3 methods for each button indepently from the actionListeners action Perform method and call them from the actionPerfomed methods:
private void btnAClicked(){};
private void btnBClicked(){};
private void btnCClicked(){};
JButton c = new JButton("c");
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btnCClicked();
btnAClicked();
btnBClicked();
}
});
In this example I have a simple JFrame containing a JButton with an ActionListener tied to it. This AcitonListener just changes a boolean flag that should allow the program to complete.
public class Test {
public static void main(String[] args){
final boolean[] flag = new boolean[1];
flag[0] = false;
JFrame myFrame = new JFrame("Test");
JButton myButton = new JButton("Click Me!");
myButton.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Button was clicked!");
flag[0] = true;
}
});
myFrame.add(myButton);
myFrame.setSize(128,128);
myFrame.setVisible(true);
System.out.println("Waiting");
while(!flag[0]){}
System.out.println("Finished");
}
}
This never prints "Finished", and after the button has been clicked once prints
Waiting
Button was clicked!
However, if I modify the while loop to read
while(!flag[0]){
System.out.println("I should do nothing. I am just a print statement.");
}
This works! The printout looks like
Waiting
I should do nothing. I am just a print statement.
I should do nothing. I am just a print statement.
....
I should do nothing. I am just a print statement.
Button was clicked!
Finished
I understand this probably isn't the proper way to wait on an action, but nonetheless I am interested in knowing why Java behaves this way.
The likeliest reason is that flag[0] = true; is executed on the UI thread, whereas while(!flag[0]) is executed on the main thread.
Without synchronization, there is no guarantee that the change made in the UI thread will be visible from the main thread.
By adding the System.out.println you introduce a synchronization point (because the println method is synchronized) and the problem gets solved.
You could make flag a volatile instance or class boolean variable (not an array), or, more simply, put whatever code you want executed on the button being pressed in the listener itself.
For reference, the code with a volatile variable would look like this:
private static volatile boolean flag;
public static void main(String[] args) {
JFrame myFrame = new JFrame("Test");
JButton myButton = new JButton("Click Me!");
myButton.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent arg0) {
System.out.println("Button was clicked!");
flag = true;
}
});
myFrame.add(myButton);
myFrame.setSize(128, 128);
myFrame.setVisible(true);
System.out.println("Waiting");
while (!flag) { }
System.out.println("Finished");
}
I'm new to Swing and I was trying to do this:
On pressing a JButton, the program will start iterating over hundreds of items, taking 1 second to process each one, and after finishing each one he should update a label to show the number of items already processed.
The problem is, the label's text is not updated until the cycle finishes iterating over all the items.
I searched online and apparently it's because this is running in the same thread, so I created a new thread to process the data and to update the variable to be used in the label (number of processed files).
But it didn't work. Then I even made another thread, which I start after the previous one, that just repaints the label. Still nothing works.
The code is like this:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try { SwingUtilities.invokeLater(validateFiles); }
}); }
Runnable validateFiles = new Runnable() {
#Override
public void run() {
while(x_is_not_100) {
processLoadsOfStuff();
label.setText(x); }
}
};
Can you help me with this?
Simple - use a SwingWorker. For more information, read the Tasks that Have Interim Results tutorial.
Here's a pretty generic example that will use a JLabel to display counting from 0 to 30 -
public final class SwingWorkerDemo {
private static JLabel label =
new JLabel(String.valueOf(0), SwingConstants.CENTER);
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
JLabelSwingWorker workerThread = new JLabelSwingWorker();
workerThread.run();
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static class JLabelSwingWorker extends SwingWorker<Void, Integer>{
#Override
protected Void doInBackground() throws Exception {
for(int i = 1; i < 31; i++){
Thread.sleep(1000);
publish(i);
}
return null;
}
#Override
protected void process(List<Integer> integers) {
Integer i = integers.get(integers.size() - 1);
label.setText(i.toString());
}
}
}
The background processing must be done in a separate thread. But the label update must be done in the event dispatch thread.
So your code should look like this:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// start a new thread for the background task
new Thread(validateFiles).start();
});
}
Runnable validateFiles = new Runnable() {
#Override
public void run() {
while(x_is_not_100) {
processLoadsOfStuff();
// use SwingUtilities.invokeLater so that the label update is done in the EDT:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
label.setText(x);
}
});
}
};
But you might want to use the SwingWorker class, which is designed to do that in a simpler way. Its documentation is very well done and contains examples.
How can I update the JProgressBar.setValue(int) from another thread?
My secondary goal is do it in the least amount of classes possible.
Here is the code I have right now:
// Part of the main class....
pp.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
new Thread(new Task(sd.getValue())).start();
}
});
public class Task implements Runnable {
int val;
public Task(int value){
this.val = value;
}
#Override
public void run() {
for (int i = 0; i <= value; i++){ // Progressively increment variable i
pbar.setValue(i); // Set value
pbar.repaint(); // Refresh graphics
try{Thread.sleep(50);} // Sleep 50 milliseconds
catch (InterruptedException err){}
}
}
}
pp is a JButton and starts the new thread when the JButton is clicked.
pbar is the JProgressBar object from the Main class.
How can I update its value?(progress)
The code above in run() cannot see the pbar.
Always obey swing's rule
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
What you can do is to create an observer that will update your progress bar -such as
- in this instance you want to show progress of data being loaded on click of a button.
DemoHelper class implements Observable and sends updates to all observers on when certain percent of data is loaded.
Progress bar is updated via public void update(Observable o, Object arg) {
class PopulateAction implements ActionListener, Observer {
JTable tableToRefresh;
JProgressBar progressBar;
JButton sourceButton;
DemoHelper helper;
public PopulateAction(JTable tableToRefresh, JProgressBar progressBarToUpdate) {
this.tableToRefresh = tableToRefresh;
this.progressBar = progressBarToUpdate;
}
public void actionPerformed(ActionEvent e) {
helper = DemoHelper.getDemoHelper();
helper.addObserver(this);
sourceButton = ((JButton) e.getSource());
sourceButton.setEnabled(false);
helper.insertData();
}
public void update(Observable o, Object arg) {
progressBar.setValue(helper.getPercentage());
}
}
Shameless plug: this is from source from my demo project
Feel free to browse for more details.
You shouldn't do any Swing stuff outside of the event dispatch thread. To access this, you need to create a Runnable with your code in run, and then pass that off to SwingUtilities.invokeNow() or SwingUtilities.invokeLater(). The problem is that we need a delay in your JProgressBar checking to avoid jamming up the Swing thread. To do this, we'll need a Timer which will call invokeNow or later in its own Runnable. Have a look at http://www.javapractices.com/topic/TopicAction.do?Id=160 for more details.
There is need not to call pbra.repaint explicitly.
Update JProgressBar shall be done through GUI dispatch thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Remember to make pbar final variable.
pbar.setValue(i);
}
});