0

I have VTreePanel class that extends from CPanel which extends from JPanel. The class has a JSplitPane object that is divided into two area: left & right. The left side contains tree menu selection object. At right side, it contains JTabbedPane object. The VTreePanel class is like this:

public final class VTreePanel extends CPanel
    implements ActionListener
{
    private JSplitPane centerSplitPane = new JSplitPane();

    private JTabbedPane tabbedPane;

    ...

    // GET method for the tabbedPane    
    public JTabbedPane getTabbedPane() {
        return tabbedPane;
    }

    // Constructor
    public VTreePanel(int WindowNo, boolean hasBar, boolean editable)
    {

        ...

        tabbedPane = new JTabbedPane();
        centerSplitPane.add(treePart, JSplitPane.LEFT);
        centerSplitPane.add(tabbedPane, JSplitPane.RIGHT);  // Look at this

        ...
    }

}

In constructor, I added the tree selection (treePart) and JTabbedPane object (tabbedPane) into the JSplitPane object (centerSplitPane). I don't add any Tab into the tabbedPane yet. Look at the screenshot below:

http://i45.tinypic.com/2v3j0nl.jpg

Then how do I add the tab when user click one of the menu?

I have AMenu class where it has implemented PropertyChangeListener that fired propertyChange method when user clicked a menu:

public final class AMenu extends CFrame
    implements ActionListener, PropertyChangeListener, ChangeListener
{

    private VTreePanel treePanel = null;    // this is the VTreePanel object

    ...

    public void propertyChange(PropertyChangeEvent e)
    {
        ...

        // Here I pass the VTreePanel object as parameter to AMenuStartItem thread object
        (new AMenuStartItem(cmd, true, sta, this, treePanel)).start();
    }

}

You can see that I have VTreePanel object (treePanel) and I pass the VTreePanel object as parameter to AMenuStartItem thread. The AMenuStartItem contains logic that perform adding Tab in JTabbedPane (remember, JTabbedPane object (tabbedPane) is in the VTreePanel).

Here is the AMenuStartItem thread class:

public class AMenuStartItem extends Thread implements ActionListener
{
    private VTreePanel m_vtreePanel;

    public AMenuStartItem (int ID, boolean isMenu, String name, AMenu menu, VTreePanel vtreepanel)
    {
        ...

        m_vtreePanel = vtreepanel;  // save the VTreePanel object
    }

    // The thread method that executed when thread is started
    public void run()
    {
        ...

        startWindow(0, cmd);

        ...
    }

    private void startWindow(int AD_Workbench_ID, int AD_Window_ID)
    {
        ...

        // Here I perform adding new tab
        m_vtreePanel.getTabbedPane().addTab(frame.getTitle(), frame.getAPanel());

        ...
    }

}

So, the getTabbedPane() returned the JTabbedPane object and the addTab() method is executed but no tab showed up at all.

Anyone know how to fix this problem?

null
  • 8,669
  • 16
  • 68
  • 98
  • Firstly, your broke one of the most important runs of Swing, you're trying to update UI components from outside the [Event Dispatching Thread](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html). Have a read through [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) for ways to work around it. Secondly, you many need to call invalidate and repaint on the JTabbedPane – MadProgrammer Oct 18 '12 at 03:58
  • Did you mean I should use this code in propertyChange() method ? javax.swing.SwingUtilities.invokeLater(new AMenuStartItem(cmd, true, sta, this, treePanel)); I tried, but still no tab showed up. – null Oct 18 '12 at 04:20
  • Hi MadProgrammer, I made a mistake when testing the application ^^. It turned out I tested in wrong Main Tab (Main Tab is the horizontal tabs row which consist of: Accounting, Finance, CRM, etc). So it turned out everything run well. I will delete my question in next hour. However, my original code runs well without using SwingUtilities.invokeLater(). Thanks for the advice anyway. – null Oct 18 '12 at 04:50
  • Up until you get some weird paint artefact that randomly pops up from time to time. Nice to know you worked outsourcing problem – MadProgrammer Oct 18 '12 at 04:52
  • How we determine if a thread must be invoked by invokeLater() ? Actually, inside the AMenuStartItem thread, it also instantiated AWindow object (variable name: frame) and called frame.initWindow() which perform adding child componenet (toolbar, menu, statusbar, etc). I believe you had known from my previous question that AWindow is subclass of JFrame. However, the code author doesn't use SwingUtilities.invokeLater to run the AMenuStartItem thread. – null Oct 18 '12 at 05:02
  • 1
    If EventQueue.isDispatchingThread returns false, then you are outside the EDT. You must NEVER, create or interact with a UI component outside if this thread – MadProgrammer Oct 18 '12 at 05:07
  • where should I put the EventQueue.isDispatchThread() ? Inside the thread? – null Oct 18 '12 at 05:13
  • You should `EventQueue.isDispatchThread() ` anywhere you're not sure if the method is being called from within the context of the EDT & you want to update the UI – MadProgrammer Oct 18 '12 at 05:23
  • I've updated my answer with an exmple – MadProgrammer Oct 18 '12 at 05:25

1 Answers1

1

All interactions with the UI MUST be done via the Event Dispatching Thread, no exceptions...

public class AMenuStartItem extends Thread implements ActionListener
{
    private VTreePanel m_vtreePanel;

    public AMenuStartItem (int ID, boolean isMenu, String name, AMenu menu, VTreePanel vtreepanel)
    {
        ...

        m_vtreePanel = vtreepanel;  // save the VTreePanel object
    }

    // The thread method that executed when thread is started
    public void run()
    {
        ...

        startWindow(0, cmd);

        ...
    }

    private void startWindow(final int AD_Workbench_ID, final int AD_Window_ID)
    {
        ...

        if (EventQueue.isDispatchingThread()) {
            // This is safe, we're in the EDT
            m_vtreePanel.getTabbedPane().addTab(frame.getTitle(), frame.getAPanel());
            m_vtreePanel.getTabbedPane().invalidate();
            m_vtreePanel.getTabbedPane().repaint();
        } else {
            // This is unsafe, we need to resync with the EDT
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    // Here I perform adding new tab
                    m_vtreePanel.getTabbedPane().addTab(frame.getTitle(), frame.getAPanel());
                    m_vtreePanel.getTabbedPane().invalidate();
                    m_vtreePanel.getTabbedPane().repaint();

                }
            });
        }

        ...
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Hi MadProgrammer, I made a mistake when testing the application ^^. It turned out I tested in wrong Main Tab (Main Tab is the horizontal tabs row which consist of: Accounting, Finance, CRM, etc). So it turned out everything run well. I will delete my question in next hour. However, my original code runs well without using SwingUtilities.invokeLater(). Thanks for the advice anyway. – null Oct 18 '12 at 04:48
  • Is invalidate() and repaint() necessary? I leave them out and found no problem. I also had these lines too: m_vtreePanel.getTabbedPane.indexOfComponent() and m_vtreePanel.getTabbedPane.setSelectedIndex(), should I put that two lines inside invokeLater() too? – null Oct 18 '12 at 05:43
  • 1
    invalidate marks *a container invalid indicating that the container needs to be laid out*, repaint request that the repaint manager add our container to the dirty list for updating during the next paint cycle. Are they required, not always, but if you have problems with components not showing up, it helps. `setSelectedIndex` changes the UI, so yes, it must be called from within the EDT – MadProgrammer Oct 18 '12 at 06:40
  • So that many java examples in internet doesn't show a good practice (no calling from EDT), e.g.: http://www.java2s.com/Code/Java/Swing-JFC/CardLayoutDemo.htm compared to this oracle java's example: http://docs.oracle.com/javase/tutorial/uiswing/examples/layout/CardLayoutDemoProject/src/layout/CardLayoutDemo.java – null Oct 18 '12 at 07:20
  • thanks for your help, I decided to keep this question. Who knows it will benefit someone in future. – null Oct 18 '12 at 07:47
  • You could have a look at [The Single Thread Rule in Swing](http://weblogs.java.net/blog/cayhorstmann/archive/2007/06/the_single_thre.html) and [Why are most UI frameworks single threaded](http://stackoverflow.com/questions/5544447/why-are-most-ui-frameworks-single-threaded) and [Will the real Swing Single Threading Rule please stand up?](http://bitguru.wordpress.com/2007/03/21/will-the-real-swing-single-threading-rule-please-stand-up/) – MadProgrammer Oct 18 '12 at 07:49