仅当按钮单击时调用方法时,Java Swing GUI才被方法阻止

时间:2014-09-24 08:59:35

标签: java multithreading swing user-interface block

我正在用Java创建一个IRC bot。我创建了一个名为" loginHandler"它要求用户输入登录信息,例如nick / pass / server /等......当用户完成时," Connect"单击按钮,初始化IRC处理程序类bot。见代码:

package irc_bot;

import java.awt.BorderLayout;
//more imports are here but they are irrelevant right now

public class loginHandler extends JFrame {

private static final long serialVersionUID = 6742568354533287421L;

private bot irc_bot;

private String username;
private String password;
private int[] ports={80,6667};
//.....

//gui vars
private final JTextField usernameInput;
private final JTextField passwordInput;
//......

public loginHandler(){

    super("Login data");

    Panel = new JPanel(new BorderLayout());
    Panel.setLayout(new GridLayout(13, 1));

    JLabel label = new JLabel("Hover over labels for information!!");
    Panel.add(label);

    label = new JLabel("Username: ");
    label.setToolTipText("Type in your username!");
    Panel.add(label);
    usernameInput=new JTextField("");
    usernameInput.setEditable(true);
    Panel.add(usernameInput);

    label = new JLabel("Password: ");
    label.setToolTipText("Type in your password! Starts with 'oauth:'");
    Panel.add(label);
    passwordInput=new JPasswordField("");
    passwordInput.setEditable(true);
    Panel.add(passwordInput);

    //.......
    //the other textfields are here but they are irrelevant right now


    //The important part:
    JButton okButton=new JButton("Connect");
    okButton.addActionListener(
            new ActionListener(){
                public void actionPerformed(ActionEvent event){
                    //set data method
                    setData();
                    dispose();
                    //bot object:
                    try {
                        irc_bot=new bot(username, password, master_channel, master_admin);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    //initiate  the bot:
                    try {
                        irc_bot.initiate(server, ports);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        );

    add(okButton, BorderLayout.SOUTH);


    add(new JScrollPane(Panel), BorderLayout.NORTH);

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(300,400);
    setVisible(true);

}
private void setData(){
    username=usernameInput.getText();
    password=passwordInput.getText();
    server=serverInput.getText();
    master_channel=master_channelInput.getText();
    master_admin=master_adminInput.getText();
    port=portsInput.getText();

    //set up the ports: TO-DO
}

}

我的问题是bot.initiate()方法阻止了bot对象的GUI。当未调用bot.initiate()时,GUI按预期工作。当bot.initiate()停止时,GUI将再次起作用。问题是initiate()方法包含一个无限循环,它从irc服务器读取行(这是irc_bot.mainMethod())。

GUI窗口显示,但它是空白的,它不响应我试图关闭它或其他任何东西。

该程序实际上没有冻结,我仍然可以通过irc与bot通信。

奇怪的是,如果我在main()中启动bot对象,它可以按预期工作,则init()方法不会阻止gui。

看看bot类(我只复制了相关部分):

package irc_bot;

//import stuff

public class bot {

//gui vars
private JFrame mainWindow;
//.....


//irc variables
private String server;
//.....

//lists
private List<String> botadminlist;
//.......

//for communicating with IRC
private Socket socket;
private BufferedWriter writer;//write to IRC
//.....
pritate bot_msg_handler handler;

private String line;//the received line from the IRC server is stored in this

//-----methods-----
//constructor: does the basic setup
public bot(String nick, String password, String master_channel, String master_admin) throws Exception {


    //gui stuff
    mainWindow=new JFrame("irc_bot");

    //setup the menu
    menuBar = new JMenuBar();

    //first menu
    menu = new JMenu("Commands");
    menu.setMnemonic(KeyEvent.VK_C);
    menuBar.add(menu);
    //ide jön a menü kifejtése
    menuItem = new JMenuItem("Show/Edit commands",
            KeyEvent.VK_S);
    //event handling
    menuItem.addActionListener(
            new ActionListener(){
                public void actionPerformed(ActionEvent event){
                    showCommands();
                }
            }
        );
    menu.add(menuItem);
    menuItem = new JMenuItem("Add command",
            KeyEvent.VK_A);
    //event handling
    menuItem.addActionListener(
            new ActionListener(){
                public void actionPerformed(ActionEvent event){
                    addCommand();
                }
            }
        );
    menu.add(menuItem);
    //more menus.......

    //more GUI elements

    //.......
    //setup the window
    mainWindow.add(bottomPanel,BorderLayout.SOUTH);
    mainWindow.add(menuBar, BorderLayout.NORTH);
    mainWindow.add(new JScrollPane(textBox), BorderLayout.CENTER);

    mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mainWindow.setSize(800,600);
    mainWindow.setVisible(true);

    sendBotMsg=false;

    //setup lists
    modlist=new ArrayList<mod_element>();
    //.....

    //user settings
    this.nick=nick;
    this.password=password;
    this.master_channel=master_channel;
    this.master_admin=master_admin;

    //create the message handler object
    handler = new bot_msg_handler(this.nick, this.master_channel, this.master_admin, modlist,
            botadminlist, stafflist, adminlist, active_channels, cooldown, commands);
    handler.setTextBox(textBox);

    textBox.append("Constructor setup complete\n");
}//constructor

//IRC SPECIFIC STUFF
public void initiate(String server, int... ports) throws Exception{
    this.server=server;
    if(connect(server, ports)==-1){
        JOptionPane.showMessageDialog(null, "Bot couldn't connect to the server!");
        mainWindow.dispose();
        return;
    }
    if(logon()==-1){
        JOptionPane.showMessageDialog(null, "Bot couldn't log in!");
        mainWindow.dispose();
        return;
    }
    join(master_channel);

    mainMethod();

    }

private int connect(String server, int... ports) throws Exception {
    // Connect directly to the IRC server.
    //this.server=server;
    boolean connected=false;
    for(int port:ports){
        textBox.append("Trying to connect to "+server+" on port "+port+"...\n");
        try{
            socket = new Socket();
            socket.setSoTimeout(1000);//if nothing happens in 1000 milliseconds, it is gonna advance in the code. IMPORTANT!
            socket.connect(new InetSocketAddress(server, port), 2000);//2000 is the timeout value in milliseconds for the connection
            textBox.append("Connected to "+server+":"+port+"\n");
            connected=true;
            this.port=port;
            break;
        }
        catch(SocketTimeoutException e1){
            textBox.append("Connection timed out\n");
        }
    }
    if(connected){
        writer = new BufferedWriter(
                new OutputStreamWriter(socket.getOutputStream( ), "UTF-8"));//utf-8 (international characters)
        reader = new BufferedReader(
                new InputStreamReader(socket.getInputStream( ), "UTF-8"));
        handler.setWriter(writer);
        return 1;//connection successful
    }else{
        textBox.append("Connection timed out, cannot connect to the IRC server\nApp will shut down now.\n");
        return -1;//this means that the connection failed
    }

}//connect

private int logon() throws Exception {//logs the bot in the irc
    writer.write("PASS " + password + "\r\n");//twitch specific stuff
    writer.write("NICK " + nick + "\r\n");
    writer.flush( );

    // Read lines from the server until it tells us we have connected.
    String line = null;
    while ((line = reader.readLine()) != null) {
            rawBox.append(line+"\n");
            if (line.contains("004")) {
                // We are now logged in.
                textBox.append("The bot is successfully logged in\n------------------\n");
                return 1;
            }
            else if (line.contains("Login unsuccessful")) {
                textBox.append("Login was unsuccessful.\n");
                return -1;
            }
    }
    return -1;
}//logon

private void join(String channel) throws Exception {
    // Join the channel. Only for initial use.
    writer.write("JOIN " + channel + "\r\n");
    writer.flush( );

    writer.write("TWITCHCLIENT 3"+"\r\n");
    writer.flush();
}//join

private void mainMethod() throws Exception{
    // Keep reading lines from the server.
    //-------MAIN PROCESS------
    msgInput.setEditable(true);//the textbox is ready to be used

    while (true){
        try{
            line = reader.readLine( );//waits for a line for 1000 millisecs
            handler.setLine(line);
        }catch(SocketTimeoutException e7){
            //if there is no incoming line in a second, it's gonna create an exception
            //we want nothing to do with the exception though
        }

        if(line!=null){
            handler.set_msg_type(0);//default for each line

            if (line.startsWith("PING ")) {
                // We must respond to PINGs to avoid being disconnected.
                handler.sendPONG();
            }

            //Print the raw line received by the bot to the rawBox
            rawBox.append(line+"\n");


            //analyze the line and gather information
            handler.msgAnalyzer();

            //message handling:
            handler.msgHandler();

            //update channellist and other lists
            updateLists();
        }//if

        //other tasks
        handler.otherTasks();
        line=null;


        //send the message 
        if(sendBotMsg){
            handler.sendPRIVMSG(channelToSend, msgToSend);
            sendBotMsg=false;
        }

    }//while

}//mainMethod


//Methods called from the gui are here, but they are irrelevant now

我尝试添加SwingWorker,因此启动内容会在后台运行,但它仍会冻结gui。

当我......

时,该程序按预期工作
  1. 不要调用启动方法。我仍然在actionPerformed方法中创建对象,并且我得到一个正常运行的GUI。

  2. 请勿通过actionPerformed调用启动函数。

  3. 例如,当我这样做时,机器人按预期工作:

    public static void main(String[] args) throws Exception {
        String server="irc.twitch.tv";
        int[] ports={80,6667};
        String nick ="test";
        String password = "test";
        String master_channel = "#master";
        String master_admin="master";
    
        //bot object:
        bot irc_bot=new bot(nick, password, master_channel, master_admin);//this is gonna be our irc_bot object
        //initiate  the bot:
        irc_bot.initiate(server, ports);
    }
    

    我怀疑使用initiate()运行的线程以某种方式阻止了GUI线程。我不明白的是为什么只有当我从动作监听器/窗口监听器/任何监听器调用所述方法时才会发生这种情况。关于如何解决这个问题的任何想法?

3 个答案:

答案 0 :(得分:1)

按钮单击匿名类的actionPerformed()方法在Swing线程上执行,因此当执行该块中的代码时,GUI不能执行任何其他操作。您需要在其他一些线程中执行initiate()方法。

为了向自己证明这是这种情况,请使用这个(可怕的)代码:

new Thread(){
    public void run() {
        //initiate  the bot:
        try {
            irc_bot.initiate(server, ports);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }}.start();

这应该可以实现你想要的,尽管代码很糟糕。然后,您需要弄清楚如何创建和管理该线程。您需要一种方法,从您的GUI发送信号到您希望它停止的后台线程,可能是通过中断它。

从main()方法执行代码时没有遇到此问题的原因是您正在获取 free 的新线程。当您使用main()启动应用程序时,您可以在bot上调用构造函数,这会产生UI。您的主要方法,在其主线程中然后开始执行bot的init()方法并进入该循环,而Swing线程负责运行UI。

答案 1 :(得分:1)

对于按钮单击,在事件调度线程上调用actionPerformed,并且应尽可能快地从actionPerformed返回。使用invokeLater稍后进行长时间的工作。否则事件队列被阻止(单线程),并且GUI没有响应。

public void actionPerformed(ActionEvent event){
    SwingUtilites.invokeLater(new Runnable() {
        @Override
        public void run() {
            ... the work
        }
    });
}

    EventQueue.invokeLater(() -> {
        ... the work
    });

第二种方法是使用invokeLater的替代类,并使用java 8的lambdas缩短代码。

答案 2 :(得分:1)

  

我尝试过添加SwingWorker,以便启动内容运行   背景,但它仍然冻结了gui。

您必须在SwingWorker上调用execute(),而不是run()方法(常见错误)。

像这样:

new SwingWorker<Void, Void> {
    @Override
    public Void doInBackground() {
        irc_bot.initiate(server, ports);
        return null;
    }
}.execute();
相关问题