Java Swing GUI仅在单击按钮调用方法时被方法阻止
我正在用Java创建一个IRC机器人。我创建了一个名为“loginHandler”的类,它要求用户输入登录信息,例如nick/pass/server/etc。。。用户完成后,单击“连接”按钮,初始化IRC处理程序类,Java Swing GUI仅在单击按钮调用方法时被方法阻止,java,multithreading,swing,user-interface,block,Java,Multithreading,Swing,User Interface,Block,我正在用Java创建一个IRC机器人。我创建了一个名为“loginHandler”的类,它要求用户输入登录信息,例如nick/pass/server/etc。。。用户完成后,单击“连接”按钮,初始化IRC处理程序类,bot。参见代码: package irc_bot; import java.awt.BorderLayout; //more imports are here but they are irrelevant right now public class loginHandler
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\u bot.main方法()中)
GUI窗口会出现,但它是空白的,并且它不会响应我试图关闭它或其他任何操作
程序实际上并没有冻结,我仍然可以通过irc与机器人通信
奇怪的是,如果我在main()中启动bot对象,它就会按预期工作,initiate()方法不会阻塞gui
看看bot类(我只复制了相关部分):
我怀疑initiate()运行的线程以某种方式阻塞了GUI线程。我不明白的是,为什么只有当我从action listener/window listener/any listener调用上述方法时才会发生这种情况。关于如何解决这个问题,有什么想法吗?按钮单击匿名类的actionPerformed()方法在Swing线程上执行,因此在执行该块中的代码时,GUI不能做任何其他事情。您需要在其他线程中执行initiate()方法
要向自己证明情况确实如此,请使用以下(可怕的)代码:
这应该可以实现您想要的,尽管代码很糟糕。然后,您需要确定如何创建和管理该线程。您需要一种方法从GUI向后台线程发出信号,表明您希望它停止,可能是通过中断它
从main()方法执行代码时没有遇到此问题的原因是,您将免费获得一个新线程。使用main()启动应用程序时,调用bot上的构造函数,这将生成UI。主方法在其主线程中开始执行bot initiate()方法并进入该循环,而Swing线程负责运行UI 按钮click anonymous类的actionPerformed()方法在Swing线程上执行,因此在执行该块中的代码时,GUI不能执行任何其他操作。您需要在其他线程中执行initiate()方法
要向自己证明情况确实如此,请使用以下(可怕的)代码:
这应该可以实现您想要的,尽管代码很糟糕。然后,您需要确定如何创建和管理该线程。您需要一种方法从GUI向后台线程发出信号,表明您希望它停止,可能是通过中断它
从main()方法执行代码时没有遇到此问题的原因是,您将免费获得一个新线程。使用main()启动应用程序时,调用bot上的构造函数,这将生成UI。主方法在其主线程中开始执行bot initiate()方法并进入该循环,而Swing线程负责运行UI 对于按钮单击,在事件调度线程上调用actionPerformed,并且应该尽快从actionPerformed返回。稍后使用invokeLater进行长时间的工作。否则,事件队列将被阻塞(单线程),GUI将不响应
public void actionPerformed(ActionEvent event){
SwingUtilites.invokeLater(new Runnable() {
@Override
public void run() {
... the work
}
});
}
EventQueue.invokeLater(() -> {
... the work
});
第二个备选方案使用invokeLater的alternative类,并使用java 8的lambdas缩短代码。对于按钮单击,在事件调度线程上调用actionPerformed,并且应该尽快从actionPerformed返回。稍后使用invokeLater进行长时间的工作。否则,事件队列将被阻塞(单线程),GUI将不响应
public void actionPerformed(ActionEvent event){
SwingUtilites.invokeLater(new Runnable() {
@Override
public void run() {
... the work
}
});
}
EventQueue.invokeLater(() -> {
... the work
});
第二个备选方案使用invokeLater的alternative类,并使用Java8的lambdas缩短代码
我已经尝试添加SwingWorker,以使initiate内容在
背景,但它仍然冻结gui
您必须在SwingWorker上调用execute()
,而不是run()
方法(常见错误)
像这样:
new SwingWorker<Void, Void> {
@Override
public Void doInBackground() {
irc_bot.initiate(server, ports);
return null;
}
}.execute();
新SwingWorker{
@凌驾
公共无效doInBackground(){
irc_bot.initiate(服务器、端口);
返回null;
}
}.execute();
我已经尝试添加SwingWorker,以使initiate内容在
背景,但它仍然冻结gui
您必须在SwingWorker上调用execute()
,而不是run()
方法(常见错误)
像这样:
new SwingWorker<Void, Void> {
@Override
public Void doInBackground() {
irc_bot.initiate(server, ports);
return null;
}
}.execute();
新SwingWorker{
@凌驾
公共无效doInBackground(){
irc_bot.initiate(服务器、端口);
返回null;
}
}.execute();
非常感谢,这正是我需要的答案。您的代码按预期工作。我的问题是我不知道actionPerformed()方法是在Swing线程上执行的。看来,我将不得不沉溺于线程处理的奇迹中,也许会更好。是的,我不得不等待大约3分钟才能点击勾号,我想:非常感谢,这正是我需要的答案。您的代码按预期工作。我的问题是我不知道actionPerformed()方法是在Swing线程上执行的。看来我不得不沉溺于线程处理的奇迹中,也许会更好。是的,我必须等待大约3分钟才能点击勾号,我想:这看起来是一个非常优雅的解决方案,但我还不知道如何在我的代码中实现它。不过我很快会调查的。非常感谢你!这个厕所
new SwingWorker<Void, Void> {
@Override
public Void doInBackground() {
irc_bot.initiate(server, ports);
return null;
}
}.execute();