Search code examples
javamultithreadingswingeventsevent-dispatch-thread

JFrame is inactive during Thread.sleep(int)


I have some Java classes that should simulate an old calculating machine. One of them is a JFrame, called GUI, with a JTabbedPane. The calculating process should take some time, so I have included Thread.sleep(int). The problem is that while the calculating function is working, the tabs of the JTabbedPane in my GUI can't be selected.

Here is some code:

import java.util.*;
import java.awt.*;

public class Engine
{
    private GUI gui;
    private Store store;
    private Mill mill;

    public static void main(String[] args) {
        Engine e = new Engine();
    }

    public Engine() {
        gui = new GUI(this);
        mill = new Mill(this);
        store = new Store(this);

        store.draw();                       // affects GUI elements
    }

    public void read(String operations) {
        clearStatus();                      // affects GUI elements

        try {
            String[] op = operations.split("\n");

            for (int i = 0; i < op.length; i++) {
                setStatus(op[i]);        // affects GUI elements

                if (op[i].length() == 0) {  // empty line
                    continue;
                }

                int number = Integer.parseInt(op[i]);
                store.addNumber(number);

                Thread.sleep(200);
            }

            store.draw();                   // affects GUI elements

        } catch(Exception e) {
            System.out.println(e);
        }
    }

    public void clearStatus() {
        gui.statusLabel.setText("");
    }

    public void setStatus(String msg) {
        gui.statusLabel.setText(msg);
    }
}

Here's my GUI (this is a short version):

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GUI extends JFrame
{
    private Engine engine;

    // Swing
    JTabbedPane tabs;
    JPanel mill;
    JTextArea input, store;
    JLabel statusLabel;
    JButton sendInput;

    public GUI(Engine e)
    {
        engine = e;

        input = new JTextArea();
        sendInput = new JButton("Run");
        sendInput.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                pressInputButton(evt);
            }
        });

        JPanel inputPanel = new JPanel();
        inputPanel.add(input,     BorderLayout.CENTER);
        inputPanel.add(sendInput, BorderLayout.SOUTH);

        mill = new JPanel();
        statusLabel = new JLabel();
        mill.add(statusLabel);

        store = new JTextArea();

        JTextPane helpPane = new JTextPane();
        helpPane.setText("[...]");

        tabs = new JTabbedPane();
        tabs.addTab("Input", inputPanel);
        tabs.addTab("Mill", mill);
        tabs.addTab("Store", new JScrollPane(store));
        tabs.addTab("Help", helpPanel);

        add(tabs, BorderLayout.CENTER);
        setVisible(true);
        pack();
    }

    public void pressInputButton(ActionEvent evt) {
        engine.read(input.getText());
    }
}

Solution

  • What @sorifiend means is, calling sleep() from an event handler will "lock up" your application's event dispatch thread. The application will not respond to any other user input until the event handler function returns.

    If you want the application to be responsive while the calculator is "working", then what you need to do is:

    • Record that the state of the calculator is "working"
    • Disable whatever buttons you don't want the user to press while the calculator is "working", --OR-- change the handlers for those buttons to do something different (simulate jamming the machine?) if they are pressed while in the "working" state.
    • Submit a timer event for some time in the near future (however long the calculation is supposed to take.)
    • In the handler for the timer event, display the result of the calculation, re-enable the disabled buttons, and change the state from "working" back to "idle" (or whatever).