简体   繁体   中英

Why wont this threading code work properly with the GUI? [Java Swing] [Threading]

My project uses Java Swing as a GUI. I am making a Towers of Hanoi game. I've just about got the GUI all working, but my solve command wont work properly.

Without threading calls, it immediately solves the towers as expected. I added a couple Thread.waits expected it to solve it step by step so the user can see how it does but instead, it waits some time, then solves the entire puzzle at once. I'm thinking it might not be repainting , but I'm not sure why. Does anyone know what is going on?

Heres the code for the solve:

public class Solver {

public Solver() {
    // nothing
}


public void solve(
    int numberBlocks,
    int startPin,
    int auxiliaryPin,
    int endPin) {
    if (numberBlocks == 1) {
        movePin(startPin, endPin);
        try {
           Thread.sleep(200);
       }
        catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    else {
        solve(numberBlocks - 1, startPin, endPin, auxiliaryPin);
        movePin(startPin, endPin);
        try {
            Thread.sleep(200);
        }
        catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        solve(numberBlocks - 1, auxiliaryPin, startPin, endPin);
    }
}


private void movePin(int startPin, int endPin) {
    TowersOfHanoiGame.moveTopBlock(startPin, endPin);
}

Here is the code from the GUI that does the work: I know its terribly written, this is my first time writing with Java Swing, Im learning it as I go. If anyone has any pointers on how to better structure this, I'd love to hear about that also. I'm pasting the whole class, but the important methods are initListeners, and moveTopBlock, and the methods they call.

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class TowersOfHanoiGame {
private static JFrame mainWindow;
private static JPanel mainContentPanel;
private static JPanel content;

private static ArrayList<Block> pegOneBlocks = new ArrayList<Block>();
private static ArrayList<Block> pegTwoBlocks = new ArrayList<Block>();
private static ArrayList<Block> pegThreeBlocks = new ArrayList<Block>();
private Color[] randomColors = new Color[8];

private Dimension menuSize = new Dimension(100, 100);
private static final int DISCSTEXTSIZE = 20;
private static final int MOVESTEXTSIZE = 30;

private ActionListener downButtonListener;
private ActionListener upButtonListener;
private ActionListener solveButtonListener;

private JLabel discs;
private JLabel moves;

private int discsNumber = 3;
private int movesNumber = 0;

private Solver solver = new Solver();


public TowersOfHanoiGame() {
    // do nothing
    initRandomColors();
    initBlocks(3);
}


/**
 * Initialize and display the game
 */
public void display() {
    initListeners();
    initWindow();
    mainWindow.setVisible(true);
}


private void initListeners() {
    downButtonListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            if (discsNumber > 3) {
                discsNumber--;
                updateLabels();
                // clearContentFrame();
                clearBlockArrays();
                initBlocks(discsNumber);
                reDrawContentFrame();
            }

        }
    };
    upButtonListener = new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            if (discsNumber < 8) {
                discsNumber++;
                updateLabels();
                // clearContentFrame();
                clearBlockArrays();
                initBlocks(discsNumber);
                reDrawContentFrame();
            }
        }
    };

    solveButtonListener = new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            solver.solve(discsNumber, 0, 1, 2);
        }
    };
}


private void updateLabels() {
    discs.setText("DISCS: " + discsNumber);
    moves.setText("MOVES: " + movesNumber);
}


/**
 * Init the main window
 */
private void initWindow() {
    mainWindow = new JFrame("Towers Of Hanoi");
    initContentPanel();
    mainWindow.setContentPane(mainContentPanel);
    mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mainWindow.setSize(1000, 1000);
    mainWindow.setResizable(false);
    mainWindow.getContentPane().setBackground(Color.WHITE);
}


/**
 * Init the main content panel
 */
private void initContentPanel() {
    mainContentPanel = new JPanel(new BorderLayout(50, 50));
    JPanel menu = initMenuFrame();
    content = initContentFrame();
    mainContentPanel.add(menu, BorderLayout.PAGE_START);
    mainContentPanel.add(content, BorderLayout.CENTER);
}


private static JPanel initContentFrame() {
    JPanel ret = new JPanel(new BorderLayout());
    JPanel pegs = new JPanel(new BorderLayout());
    pegs.setBackground(Color.WHITE);
    ret.setBackground(Color.WHITE);

    Peg peg1 = new Peg(25, 500, 1.2);
    Peg peg2 = new Peg(50, 500, 1.2);
    Peg peg3 = new Peg(0, 500, 1.2);

    peg1.addBlocks(pegOneBlocks);
    peg2.addBlocks(pegTwoBlocks);
    peg3.addBlocks(pegThreeBlocks);

    pegs.add(peg1, BorderLayout.LINE_START);
    pegs.add(peg2, BorderLayout.CENTER);
    pegs.add(peg3, BorderLayout.LINE_END);

    ret.add(pegs, BorderLayout.CENTER);
    return ret;
}


private Color randomColor() {
    int R = (int)(Math.random() * 256);
    int G = (int)(Math.random() * 256);
    int B = (int)(Math.random() * 256);
    Color color = new Color(R, G, B); // random color, but can be bright or
                                      // dull

    // to get rainbow, pastel colors
    Random random = new Random();
    final float hue = random.nextFloat();
    final float saturation = 0.9f;// 1.0 for brilliant, 0.0 for dull
    final float luminance = 1.0f; // 1.0 for brighter, 0.0 for black
    color = Color.getHSBColor(hue, saturation, luminance);
    return color;
}


private void initRandomColors() {
    for (int i = 0; i < 8; i++) {
        randomColors[i] = randomColor();
    }
}


private void initBlocks(int numBlocks) {
    int startWidth = Block.LONGESTWIDTH;
    for (int i = 0; i < numBlocks; i++) {
        Block b = new Block((startWidth - (i * 15)), randomColors[i]);
        pegOneBlocks.add(b);
    }
}


private static void clearContentFrame() {
    mainContentPanel.remove(content);
    mainContentPanel.repaint();
}


private void clearBlockArrays() {
    pegOneBlocks.clear();
    pegTwoBlocks.clear();
    pegThreeBlocks.clear();
}


public static void reDrawContentFrame() {
    content = initContentFrame();
    mainContentPanel.add(content, BorderLayout.CENTER);
    mainContentPanel.repaint();
}


public static void moveTopBlock(int startPin, int destinationPin) {
    Block b = null;
    if (startPin == 0) {
        b = pegOneBlocks.get(pegOneBlocks.size() - 1);
        pegOneBlocks.remove(pegOneBlocks.size() - 1);
    }
    else if (startPin == 1) {
        b = pegTwoBlocks.get(pegTwoBlocks.size() - 1);
        pegTwoBlocks.remove(pegTwoBlocks.size() - 1);
    }
    else if (startPin == 2) {
        b = pegThreeBlocks.get(pegThreeBlocks.size() - 1);
        pegThreeBlocks.remove(pegThreeBlocks.size() - 1);
    }

    if (destinationPin == 0) {
        pegOneBlocks.add(b);
    }
    else if (destinationPin == 1) {
        pegTwoBlocks.add(b);
    }
    else if (destinationPin == 2) {
        pegThreeBlocks.add(b);
    }

    reDrawContentFrame();
    content.validate();
    mainContentPanel.validate();
    mainWindow.validate();
}


/**
 * Build the menu panel
 * 
 * @return menu panel
 */
private JPanel initMenuFrame() {
    JPanel ret = new JPanel(new BorderLayout());
    ret.setPreferredSize(menuSize);

    // left
    JPanel left = new JPanel(new FlowLayout());
    left.setPreferredSize(menuSize);
    JLabel label = new JLabel("DISCS: 3");
    discs = label;
    label.setFont(new Font("Serif", Font.BOLD, DISCSTEXTSIZE));
    Button down = new Button("Decrease");
    down.addActionListener(downButtonListener);
    Button up = new Button("Increase");
    up.addActionListener(upButtonListener);
    left.add(label);
    left.add(up);
    left.add(down);

    // mid
    moves = new JLabel("MOVES: 0");
    moves.setHorizontalAlignment(JLabel.CENTER);
    moves.setFont(new Font("Serif", Font.BOLD, MOVESTEXTSIZE));

    // right
    JPanel right = new JPanel(new FlowLayout());
    Button solve = new Button("Solve");
    solve.addActionListener(solveButtonListener);
    Button reset = new Button("Reset");
    right.add(solve);
    right.add(reset);

    // sync
    JPanel menu = new JPanel(new BorderLayout());
    menu.add(left, BorderLayout.LINE_START);
    menu.add(moves, BorderLayout.CENTER);
    menu.add(right, BorderLayout.LINE_END);

    ret.add(menu, BorderLayout.CENTER);
    return ret;
}
}
solveButtonListener = new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {
        solver.solve(discsNumber, 0, 1, 2);
    }
};

The problem is that code invoked for any listener is executed on the Event Dispatch Thread (EDT) . The EDT is responsible for responding to event and repaint the GUI. The Thread.sleep() method causes the EDT to sleep and as a result the GUI can't repaint itself until all the code has finished executing.

What you need to do is start a separate Thread when you invoke the solver.solve(...) method.

Read the section from the Swing tutorial on Concurrency for more information.

Note, the above suggestion to use a separate Thread is still not a proper solution. Swing was designed to be single Thread, which means that all updates to the state of your GUI and the repainting of the GUI should be done on the EDT. So this would mean you should also be using SwingUtilities.invokeLater() to add code to the EDT for processing. I have never tried doing this when using recursion, so I'm not sure the best way to do this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM