簡體   English   中英

為什么該線程代碼無法與GUI一起正常工作? [Java Swing] [線程]

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

我的項目使用Java Swing作為GUI。 我正在制作河內塔游戲。 我幾乎可以正常使用GUI,但是我的Solve命令無法正常工作。

在沒有線程調用的情況下,它可以按預期立即解決問題。 我添加了幾個Thread.waits期望它可以逐步解決它,以便用戶可以看到它是如何工作的,但是相反,它要等待一段時間,然后立即解決整個難題。 我以為它可能不會重新粉刷 ,但是我不確定為什么。 有人知道發生了什么嗎?

這是用於解決的代碼:

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);
}

這是完成該工作的GUI中的代碼:我知道它的編寫非常糟糕,這是我第一次使用Java Swing編寫,我正在學習中。 如果有人對如何更好地構建結構有任何建議,我也很想聽聽。 我要粘貼整個類,但是重要的方法是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);
    }
};

問題在於在Event Dispatch Thread (EDT)上執行為任何偵聽器調用的代碼。 EDT負責響應事件並重新繪制GUI。 Thread.sleep()方法使EDT進入睡眠狀態,因此,在所有代碼執行完畢之前,GUI無法重繪自身。

您需要做的是在調用solver.solve(...)方法時啟動一個單獨的Thread

閱讀Swing 並發教程中的有關更多信息的部分。

注意,以上建議使用單獨的線程仍然不是正確的解決方案。 Swing被設計為單線程,這意味着對GUI狀態的所有更新和GUI的重新繪制都應在EDT上完成。 因此,這意味着您還應該使用SwingUtilities.invokeLater()將代碼添加到EDT中進行處理。 使用遞歸時,我從未嘗試過這樣做,所以我不確定做到這一點的最佳方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM