EDT on Swing (for Dummies) - java

So, I'm just working on a little game, which works quite fine, except for the GUI. Basically, I need to modify the GUI when clicking a button. I realize that I have to run my code on the EDT to do so, using this code:
EventQueue.invokeLater(new Runnable () {
#Override
public void run() {
// some code
}
});
I just don't now which part of my code is concerned by this. The part where I create the GUI (the constructor of my class)? Or only the part where i modify the values (in that case Listener.actionPerformed())? Actually I tested bot of this, neither worked.
Now what I want to know is how do I modify the following code to update the Button when i click it? Do I have to embed parts of it in the code above or am I completely wrong?
package edttest;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class EDTtest {
public static void main(String[] args) {
GUI gui = new GUI ();
}
private static class GUI extends JFrame {
int x;
public GUI () {
x = 0;
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
JButton button = new JButton (String.valueOf(x));
button.addActionListener(new Listener ());
JLabel label = new JLabel (String.valueOf(x));
add (label, BorderLayout.NORTH);
add (button);
pack();
setVisible (true);
}
private class Listener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
x++;
System.out.println (x);
}
}
}
}

Whether or not you execute this code on the EDT will do nothing to your label. It is not because you increment x that the label will update itself. You need to call label#setText with the updated value.
Concerning your question around the EDT. All access/modifications/creation/... of Swing components should happen on the EDT. This means you should wrap the contents of your main method in an SwingUtilities#invoke.... Every event that is triggered through the UI (e.g. the click on a button) will already be processed on the EDT. So no need to explicitly schedule a Runnable on the EDT in your listener.
When in doubt, you can always check whether you are on the EDT by using EventQueue#isDispatchThread.
I would also suggest to read the Concurrency in Swing tutorial

Related

Change the selected state of a JToggleButton (e.g. JCheckBox) without triggering its item listeners [duplicate]

I've got a Swing application with a model and a view. In the view (GUI) there are a lot of components, each of them mapping to some property of a model object and displaying it's value.
Now there are some UI components that automatically trigger the updating of some model properties when their value changes in the UI. This requires me to reload the complete model in the UI. This way I'm entering an infinite update loop, as every model reload in the UI triggers another model reload.
I have a flag indicating the load process, which I'd like to use to temporarily suppress the listener notifications, while the UI fields are being set from the model. So my question is:
Is there a way to globally temporarily disable some component's listeners in Swing without removing and reattaching them?
You could use a common base class for your listeners and in it, have a static method to turn the listeners on or off:
public abstract class BaseMouseListener implements ActionListener{
private static boolean active = true;
public static void setActive(boolean active){
BaseMouseListener.active = active;
}
protected abstract void doPerformAction(ActionEvent e);
#Override
public final void actionPerformed(ActionEvent e){
if(active){
doPerformAction(e);
}
}
}
Your listeners would have to implement doPerformAction() instead of actionPerformed().
(This would be awful in an enterprise scenario, but in a single-VM model like in Swing, it should work just fine)
Normally I use a flag indicating API changes or user changes. For each of the listeners I would check the flag and if it's API changes just return.
While searching stackoverflow, I found this question. I thought to add my opinion/answer.
It is really^inf bad idea to temporarily disable event listeners in Swing. If your code is broken (or something else goes wrong), you may not be able to bring your application back to life - respond to user and other events.
If you want to discard (respond but do nothing) to user events, you may use glass pane which can just ignore the events.
If your EDT is busy (which again you must avoid as much as possible) and you wanted to discard user action for that period, you may still use a glasspane and remove it using invokeLater to remove the pane after all the events have been responded (ignored by the glasspane) to.
Full details including an SSCE can be found in this question.
java wait cursor display problem
One option that might work for you is just to put a glass pane up while loading in order to block events during that time:
http://download.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane
As mentioned above, the GlassPane is helpful in this regard.
Here is a simple example:
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.SwingWorker;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GlassPaneExample extends JFrame implements ActionListener {
private JButton btnDisable;
private JButton btnTestOne;
private JButton btnTestTwo;
private MyGlassPane glass;
private boolean actionAllowed = true;
public GlassPaneExample() {
// init JFrame graphics
setBounds(300, 300, 300, 110);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setVisible(true);
// init buttons
btnTestOne = new JButton("Button one");
add(btnTestOne);
btnTestTwo = new JButton("Button two");
add(btnTestTwo);
btnDisable = new JButton("Disable ActionListeners for 2 seconds");
add(btnDisable);
// create Glass pane
glass = new MyGlassPane();
setGlassPane(glass);
// add listeners
btnTestOne.addActionListener(this);
btnTestTwo.addActionListener(this);
btnDisable.addActionListener(this);
}
public static void main(String[] args) {
new GlassPaneExample();
}
#Override
public void actionPerformed(ActionEvent e) {
JButton src = (JButton)e.getSource();
if (src.equals(btnDisable)) {
// setting glasspane visibility to 'true' allows it to receive mouse events
glass.setVisible(true);
setCursor(new Cursor(Cursor.WAIT_CURSOR));
SwingWorker sw = new SwingWorker() {
#Override
protected Object doInBackground()
throws Exception {
Thread.sleep(2000);
return null;
}
#Override
public void done() {
// set cursor and GlassPane back to default state
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
glass.setVisible(false);
// allow actions to be received again
actionAllowed = true;
}
};
sw.execute();
} else if (actionAllowed) {
if (src.equals(btnTestOne)) {
JOptionPane.showMessageDialog(this, "BUTTON ONE PRESSED");
} else if (src.equals(btnTestTwo)) {
JOptionPane.showMessageDialog(this, "BUTTON TWO PRESSED");
}
}
}
class MyGlassPane extends JPanel {
public MyGlassPane() {
setOpaque(false);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
actionAllowed = false;
}
});
}
//Draw an cross to indicate glasspane visibility
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.drawLine(0, 0, getWidth(), getHeight());
g.drawLine(getWidth(), 0, 0, getHeight());
}
}
}
This question looks like a similar problem and no satisfactory solution to it.
I found this article helpful in critically examining my own designs.
Is there a way to globally temporarily disable some component's listeners in Swing without removing and reattaching them?
Every JComponent maintains an EventListenerList, which is accessible to your subclass. If necessary, you can always operate on the list directly or build the desired behavior into your custom implementation of EventListener

Displaying JFrame

I'm having issues figuring out how to open one window when another closes if the other window is initiated within a sub class. Here is the clumsy code I am trying to use, but it halts the setting visible of the sub classe's window. Perhaps due to it being within an action event or perhaps it is halting the main thread.
tutorial = new tutorialWindow();
this.setVisible(false);
tutorial.setLocationRelativeTo(null);
tutorial.setVisible(true);
tutorial.setCurrentUser(users.getCurrentUser());
while(tutorial.isOpen() == true ) {
}
this.setVisible(true);
users.updateUser(tutorial.getCurrentUser());
My thoughts were that it would just get stuck in the section of code until the other window closes and would then appear again when the tutorialWindow has a Open boolean set to false due to it breaking the while loop.
Im sure this is a matter of using correct threads, or perhaps the various notify methods but as of now I am not sure how to do that.
You could do it using WindowListener. In the following sample WindowAdapter implements WindowListener and I just override the public void windowClosed(final WindowEvent e) method, opening the second window.
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TestJFrame {
public static void main(final String args[]) {
JFrame jFrame1 = new JFrame();
jFrame1.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jFrame1.add(new JLabel("First JFrame"));
jFrame1.pack();
final JFrame jFrame2 = new JFrame();
jFrame2.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jFrame2.add(new JLabel("Second JFrame"));
jFrame2.pack();
jFrame1.addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(final WindowEvent e) {
jFrame2.setVisible(true);
}
});
jFrame1.setVisible(true);
}
}

Buttons all over the window - Java

I am trying to learn java and i am practicing with a simple program with 2 simple buttons. Here is my code :
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Askhsh 3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ColorJPanel application = new ColorJPanel();
frame.add(application);
frame.setSize(500,500);
frame.setVisible(true);
}
}
And the class ColorJPanel:
import java.awt.*;
import javax.swing.*;
public class ColorJPanel extends JPanel{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.setBackground(Color.WHITE);
JButton arxikopoihsh = new JButton("Αρχικοποίκηση");
JButton klhrwsh = new JButton("Κλήρωση");
add(arxikopoihsh);
add(klhrwsh);
this.revalidate();
this.repaint();
}
}
As you can see the only thing i want to do for now is to place 2 simple buttons that do nothing! Here is my output:
http://imageshack.us/photo/my-images/847/efarmogh.jpg/
When i am running the application i am seeing the buttons filling the window!
Note that if i remove the "this.revalidate();" command i have to resize the window to see the buttons !
Thanks very much for your time :)
Don't add components in paintComponent. This method is for painting only, not for program logic or to build GUI's. Know that this method gets called many times, often by the JVM and most of the time this is out of your control, and also know that when you ask for it to be called via the repaint() method, this is only a suggestion and the paint manager may sometimes choose to ignore your request. The paintComponent method must be lean and fast as anything that slows it down will slow down the perceived responsiveness of your application.
In your current code, I don't even see a need to have a paintComponent method override, so unless you need it (if doing for instance custom painting of the component), I suggest that you get rid of this method (and the calls to repaint and revalidate). Instead, add your components in the class's constructor and make sure to pack your top level container after adding components and before calling setVisible(true). Most important -- read the Swing tutorials as this is all covered there.
e.g.,
Main.java
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Askhsh 3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ColorJPanel application = new ColorJPanel();
frame.add(application);
frame.pack();
frame.setVisible(true);
}
}
ColorJPanel.Java
import java.awt.*;
import javax.swing.*;
public class ColorJPanel extends JPanel{
public static final int CJP_WIDTH = 500;
public static final int CJP_HEIGHT = 500;
public ColorJPanel() {
this.setBackground(Color.WHITE);
JButton arxikopoihsh = new JButton("Αρχικοποίκηση");
JButton klhrwsh = new JButton("Κλήρωση");
add(arxikopoihsh);
add(klhrwsh);
}
// let the component size itself
public Dimension getPreferredSize() {
return new Dimension(CJP_WIDTH, CJP_HEIGHT);
}
}

Element inserted into custom ListModel is automatically selected if between two selected values

I've noticed is that when an element is added to a JList and the element happens to fall within a range of values that are selected, it ends up being selected by default. In fact, if an element is added just above a selected value it is added to the selection.
I've tried looking at the JList code (in the open JDK6) and the BasicListUI code, but I'm having trouble teasing out the details of why this happens or what I need to do to fix it.
I'm considering supplying a custom SelectionModel, as that would be a sensible place to do some of the other work in my application, but I'm afraid that might make the problem worse, or harder to fix. Does anyone know why this happens? What I can do to fix it?
I've created this example that demonstrates the issue:
package jlistsscce;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
public class JListSSCCE extends JPanel
{
private final JList list;
private ScheduledExecutorService ses;
public JListSSCCE()
{
list = new JList();
list.setModel(new DefaultListModel());
ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleAtFixedRate(new NewElement(), 100, 100, TimeUnit.MILLISECONDS);
add(list);
}
private static void createGui()
{
// create new JFrame
JFrame jf = new JFrame("JListSSCCE");
// this allows program to exit
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// You add things to the contentPane in a JFrame
jf.getContentPane().add(new JListSSCCE());
// size frame
jf.pack();
// make frame visible
jf.setVisible(true);
}
public static void main(String[] args)
{
// threadsafe way to create a Swing GUI
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run()
{
createGui();
}
}
);
}
private class NewElement implements Runnable
{
int n = 0;
#Override
public void run()
{
((DefaultListModel)list.getModel()).add((int)Math.floor(Math.sqrt(n)), ("hey"+n));
n++;
}
}
}
This isn't the problem but I believe you should be using a Swing Timer instead of an Executor so the code get executed on the EDT.
Yes the problem is the selection model. The last time I looked at the code I found it rather confusing, so I'm not sure you want to play with it.
The only thing I can think of is to make sure you are using the multiple selection interval. Then after inserting an elemnt you check to see if the element is selected. If it is then you remove the selection for that element.

What is the right action to take upon closing windows in java/swing?

I just wrote this test code in my CustomUIPanel class:
public static void main(String[] args) {
final JDialog dialog = CustomUIPanel.createDialog(null,
CustomUIPanel.selectFile());
dialog.addWindowListener(new WindowAdapter() {
#Override public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
It works correctly if CustomUIPanel.main() is the program's entry point, but it makes me wonder something: what if another class called CustomUIPanel.main() for testing? Then my call to System.exit(0) is incorrect.
Is there a way to tell the Swing event dispatch thread to exit automatically if there are no top-level windows?
If not, what's the right thing for a JDialog/JFrame to do upon closing if the goal is for the program to exit when all the top level windows are closed?
You can use the setDefaultCloseOperation() method of JDialog, specifying DISPOSE_ON_CLOSE:
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
See also 12.8 Program Exit.
Addendum: Incorporating #camickr's helpful answer, this example exits when either the window is closed or the close button is pressed.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
/** #see http://stackoverflow.com/questions/5540354 */
public class DialogClose extends JDialog {
public DialogClose() {
this.setLayout(new GridLayout(0, 1));
this.add(new JLabel("Dialog close test.", JLabel.CENTER));
this.add(new JButton(new AbstractAction("Close") {
#Override
public void actionPerformed(ActionEvent e) {
DialogClose.this.setVisible(false);
DialogClose.this.dispatchEvent(new WindowEvent(
DialogClose.this, WindowEvent.WINDOW_CLOSING));
}
}));
}
private void display() {
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new DialogClose().display();
}
});
}
}
Not sure about when using a JDialog.
But when using a JFrame you should use frame.dispose(). If the frame is the last open frame then the VM will exit.
Note a dialog does not have an EXIT_ON_CLOSE option since it should not generally exit the VM.
When closing the dialog you could always get the dialogs parent frame. Then you could dispatch an event to the frame to tell it to close itself. Something like:
WindowEvent windowClosing = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
//Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowClosing);
frame.dispatchEvent(windowClosing);
Use
this.dispose();
It should work.
Well,
You could use a JFrame instead. JDialog is supposed to be used as popup of an application that runs in an JFrame to catch the users attention and to pause the main application.
If the JFrame is closed, you can call System.exit(0)
dialog has a getParent() method, which I guess, is set to null in your case here CustomUIPanel.createDialog(null,
you can use that to exit conditionally.
Here is what I would recommend : dialog.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Categories