简体   繁体   English

为什么该线程代码无法与GUI一起正常工作? [Java Swing] [线程]

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

My project uses Java Swing as a GUI. 我的项目使用Java Swing作为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. 我几乎可以正常使用GUI,但是我的Solve命令无法正常工作。

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. 我添加了几个Thread.waits期望它可以逐步解决它,以便用户可以看到它是如何工作的,但是相反,它要等待一段时间,然后立即解决整个难题。 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. 这是完成该工作的GUI中的代码:我知道它的编写非常糟糕,这是我第一次使用Java Swing编写,我正在学习中。 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. 我要粘贴整个类,但是重要的方法是initListeners和moveTopBlock及其调用的方法。

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) . 问题在于在Event Dispatch Thread (EDT)上执行为任何侦听器调用的代码。 The EDT is responsible for responding to event and repaint the GUI. EDT负责响应事件并重新绘制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. Thread.sleep()方法使EDT进入睡眠状态,因此,在所有代码执行完毕之前,GUI无法重绘自身。

What you need to do is start a separate Thread when you invoke the solver.solve(...) method. 您需要做的是在调用solver.solve(...)方法时启动一个单独的Thread

Read the section from the Swing tutorial on Concurrency for more information. 阅读Swing 并发教程中的有关更多信息的部分。

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. Swing被设计为单线程,这意味着对GUI状态的所有更新和GUI的重新绘制都应在EDT上完成。 So this would mean you should also be using SwingUtilities.invokeLater() to add code to the EDT for processing. 因此,这意味着您还应该使用SwingUtilities.invokeLater()将代码添加到EDT中进行处理。 I have never tried doing this when using recursion, so I'm not sure the best way to do this. 使用递归时,我从未尝试过这样做,所以我不确定做到这一点的最佳方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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