1

I'm working on a simple Java swing project. This is the code of the main class (name changed):

public class MainProg
{
    private static MainProg program; 

    //mainWin is a JFrame
    private MainWindow mainWin;

    //Event handler class which extends MouseAdapter
    private TrayManager trayMgr;


    public static void main(String[] args)
    {                
        program = new MainProg();
    }

    public MainProg()
    {
        mainWin = new MainWindow();
        trayMgr = new TrayManager();

        mainWin.startBtn.addMouseListener(trayMgr);

        mainWin.setVisible(true);
    }
}

As is clear, when the program starts, in main() it creates a new instance of the MainProg class, which then calls the constructor. In the constructor, it creates a new instance of the JFrame mainWin. It then attaches an event handler to a button on mainWin.

In the event handler class trayMgr, the only method is mouseClicked() which does nothing except a System.out.println('Clicked');

The issue is, when I run this program in Netbeans, the JFrame is shown right away, but I seem to have to click the button 2-3 times before the message is printed in the console.

Is this just something specific to Netbeans, or do I have to change something to make the event handler be set before the window is made visible?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Ali
  • 261,656
  • 265
  • 575
  • 769
  • 2
    Any chance you could include [SSCCE](http://sscce.org/) that demonstrates the problem? – tenorsax Sep 05 '12 at 01:13
  • Netbeans doesn't change anything about how the program runs. Your code is also incorrect in that it's calling Swing from a thread other than EDT. This could be responsible for the problem. If you show us a SSCCE as @Max said we might be able to help. – Gene Sep 05 '12 at 01:19
  • @Gene How can I create an SSCCE? However I'm positive that what you mentioned re a thread other than the event loop, is the issue. Can you elaborate on that? – Ali Sep 05 '12 at 01:20
  • It definitely seems like a threading issue, e.g the thread for showing the Jframe and setting the event are running separately – Ali Sep 05 '12 at 01:21
  • 1
    See [Main Thread vs. UI Thread in Java](http://stackoverflow.com/q/7156949/1048330). – tenorsax Sep 05 '12 at 01:23
  • 1
    Why are you adding a MouseListener to a JButton anyway? You should almost never do this. – Hovercraft Full Of Eels Sep 05 '12 at 01:23
  • @HovercraftFullOfEels why not? How else would I handle a mouse click? (This is the first time I'm doing a swing app, so please be harsh and correct me if I'm doing something wrong) :) – Ali Sep 05 '12 at 01:28
  • 1
    An ActionListener handles a JButton's click. If you use a MouseListener you could potentially mess up the button's fuctioning. You need to read the tutorials as there's lots to learn. – Hovercraft Full Of Eels Sep 05 '12 at 01:29
  • @HovercraftFullOfEels If you'd like to post how I can handle this threading issue as well as the actionListener, and I'll accept your answer – Ali Sep 05 '12 at 01:34
  • @Click: no one said that it's definitely a threading issue, and in fact it probably isn't. All that was said is that you're not respecting Swing's threading rules when starting your Swing GUI. Place it in a Runnable and call it using `SwingUtilities.invokeLater(...)` as the tutorials will recommend you do. But it is not likely that this will solve your problem. I suspect you have a bug in code not shown. But again get rid of the MouseListener and only use ActionListeners with your JButton. – Hovercraft Full Of Eels Sep 05 '12 at 01:43
  • @HovercraftFullOfEels I'm using the Netbeans GUI wizard, so it generates all the code for me for setting up the UI components. So in the JFrame Class, a method `initComponents` is called from the constructor of the JFrame which adds all the controls to the JFrame. How do you suggest I refactor it? – Ali Sep 05 '12 at 01:50
  • 1
    Again first and foremost, get rid of that MouseListener and use an ActionListener. Please report back to see if that does anything. My own bias is against using NetBeans to generate GUI code for you until you've first learned to code Swing by hand. It's not a simple point and click tool, and can fail miserably if you're not familiar with the rudiments of Swing first. – Hovercraft Full Of Eels Sep 05 '12 at 01:52
  • @HovercraftFullOfEels Using the ActionListener seems to have fixed this lag issue. But still, if I'm doing something wrong with the threading, I'd like to know it. Cheers – Ali Sep 05 '12 at 02:02

2 Answers2

5

Your threading issue is not likely one that is causing your current problem, but there's the theoretic potential for problems, and I've seen some real problems associated with some of the more touchy look and feels. Quite simply you should queue your code that starts your GUI onto the Swing event thread. You do this by doing:

public void main(String[] args) {
  SwingUtilities.invokeLater(new Runnable(
    public void run() {
      program = new MainProg();
    }
  ));
}

Someone else recommended using invokeAndWait(...) instead of invokeLater(...) but this can be risky especially if you inadvertently make this call from within the Swing event thread itself. For your situation you're better off using invokeLater(...).

But again, I think the main problem with the code you have shown was inappropriate use of MouseListener where an ActionListener should have been used. Learning to code any GUI library can be quite tricky, and for that reason, you can't assume anything. Check out the tutorials and learn from the experts. Also if you are considering coding Swing for the long haul, consider ditching the NetBean's code-generation utilities and learn first to code Swing by hand. You won't regret doing this.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Question. So when this code is run, it will go like this: MainProg() -> JFrame() -> Jframe.initComponents() (this is where all the UI components are set up and added to the Jframe). Is this how you intended it? – Ali Sep 05 '12 at 02:18
  • 2
    I have to second the suggestion that you learn how to code Swing by hand first. I have a considerable amount of doing so and am just starting to learn how to use the NetBeans GUI Builder. Many of my recent Java questions here at Stackoverflow have been about using the GUI Builder. In general I find it unweildy and will most likely move away from using it at all for future projects. – Code-Apprentice Sep 05 '12 at 02:19
  • @ClickUpvote Because of the threading issues, the call stack is a little more complicated than what you state. Eventually run() is called which then calls the MainProg() constructor and so on. – Code-Apprentice Sep 05 '12 at 02:21
  • @Click: I'm not 100% sure what you're asking, but I think that yes, that is correct. All I can say for sure is that all GUI building should be done on the event thread. – Hovercraft Full Of Eels Sep 05 '12 at 02:21
  • @HovercraftFullOfEels And by the event thread, you mean the new thread that you're providing to `SwingUtilities.invokeLater` above? – Ali Sep 05 '12 at 02:27
  • @Click: no, the code that I've posted does not directly start any thread. I mean instead the thread created by Swing that is responsible for handling all user interactions and all graphics. Swing creates this thread on Swing start up. The code that I've shown below only queues a Runnable onto this thread if it exists. If it doesn't yet exist, I'm sure that it suggests to Swing to start it. Please read up on [concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html), but don't worry about using SwingWorkers just yet. – Hovercraft Full Of Eels Sep 05 '12 at 02:32
  • @HovercraftFullOfEels Thanks for your help, but I have one more q. Right now I'm using only one JFrame, but say I want to load up a new JFrame when the user clicks a button (e.g he clicks 'Show details' and I want to pop up a new JFrame with the details). Do I need to do anything specific for that in terms of threading, or can I just set up the JFrame and add the components onto it, and then show it, once the program is running through SwingUtilties.invokeLater in main()? – Ali Sep 05 '12 at 02:40
  • @Click: first of all, you'll not want to show a 2nd JFrame. If you want to display a second dependent window, you'll likely use a JDialog or swap views via a CardLayout. Next likely the code to create and show the dialog will be on the Swing event thread since it will likely be called from within an event listener which is always on the event thread. So then, no you won't need to do this. – Hovercraft Full Of Eels Sep 05 '12 at 02:43
  • @HovercraftFullOfEels Right, so once I have that `SwingUtilities.invokeLater` in main(), I can forget about it and program the rest of everything as I would normally? – Ali Sep 05 '12 at 02:46
  • @Click: if you don't create or use background threading, then yes, I believe that's right, but if your Swing app is anything more than a simple one, you'll likely need to do background threading at some point. – Hovercraft Full Of Eels Sep 05 '12 at 02:59
  • @HovercraftFullOfEels Thanks, Last Q.. so if I do any background threading, then in each thread I'd need to put the actual code within SwingUtilities.invokeLater like in main()? – Ali Sep 05 '12 at 03:21
  • @Click: not necessarily. If you use a SwingWorker, then it will do this for you in certain situations. – Hovercraft Full Of Eels Sep 05 '12 at 03:37
3

Since you asked, the code I posted here is a Java SSCCE on a different topic. invokeLater is a way of running computations on the EDT. (There is also invokeAndWait, which would work fine here, but under some other conditions can cause a deadlock.)

In fact this example is perhaps a bit over-conservative. Some references say you can run Swing from the main thread the call to show() or setVisible(). However I have a program that misbehaves under Java 7 when I try that.

Community
  • 1
  • 1
Gene
  • 46,253
  • 4
  • 58
  • 96
  • **No**, you almost never should use `invokeAndWait(...)`. Much better to use `invokeLater(...)`. The only exception that I know of is perhaps when starting JApplets, but that's about it. The OP is starting a JFrame so this recommendation is wrong. Please change it. – Hovercraft Full Of Eels Sep 05 '12 at 01:44
  • If you give me a reference to support this assertion, will do. I have never seen anything in long searching of Swing documentation. Here, `invokeLater` blocks the main thread, which is not allowed to do further UI work anyway. In the example I posted, `invokeLater` will let the main thread proceed a few more instructions to hang waiting for the EDT to exit. There is no material difference in the execution paths. But then, I'm very happy to be proven wrong by more than a simple statement **in boldface**. – Gene Sep 05 '12 at 20:30
  • The main problem I know is that you can call invokeLater on any thread while you cannot call invokeAndWait on the EDT. If you want documentation on that, just check the *SwingUtilities API*. – Hovercraft Full Of Eels Sep 05 '12 at 21:17
  • There's also a risk of causing deadlock with invokeAndWait, and you must make sure that the thread that calls invokeAndWait does not hold any locks that might be required by others when the call is occurring. For more on this, please read [Threads and Swing](http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html) by Hans Muller and Kathy Walrath, two who were part of the team that helped create Swing and its threading mechanics. – Hovercraft Full Of Eels Sep 05 '12 at 21:18
  • 1
    The OP was running simple Swing init code in the main thread, and my suggestion of `InvokeAndWait` was in that context. I agree that someone could take this out of context and get in trouble, so I'll change it. Thanks. – Gene Sep 05 '12 at 21:25
  • The person I'm worrying about taking this out of context is the original poster, and you. Ingrained habits die hard, and if he learns now to use `invokeAndWait`, he may easily run into trouble later when he runs into one of the problems listed above. You don't wear seat belts today because you know you'll be in a car accident today, you wear them because doing so decreases your odds of suffering unpredictable harm. It's the same with good programming practices. 1+ for the edit to your post. Thanks. – Hovercraft Full Of Eels Sep 05 '12 at 21:31
  • I worry about you, too, so we're even... ;-) – Gene Sep 06 '12 at 00:33