[英]Java Swing: repaint component during long event
I am working with Java Swing to render my GUI for an application. 我正在使用Java Swing渲染应用程序的GUI。 I have an ActionListener set up on a JButton
to fire off a series of tests. 我在JButton
上设置了一个ActionListener来触发一系列测试。 Ideally, I would like to update a status area (currently a JTextArea
) with real-time testing information and results. 理想情况下,我想使用实时测试信息和结果更新状态区域(当前为JTextArea
)。 In other words, the program would set the text area line-by-line as tests are run and completed to something like: 换句话说,程序会在运行和完成测试时逐行设置文本区域,例如:
Running Tests... Test 1: Check Something... Success Test 2: Check Something else... Success .. //More testing output All tests have completed successfully. Yay.
The problem is that I can't get the status text to update while my actionPerformed
event method is still running. 问题是当我的actionPerformed
事件方法仍在运行时,我无法更新状态文本。 This is is all fine and dandy when all of the tests complete quickly. 当所有测试都快速完成时,这一切都很好。 However, some of my tests take an indefinite amount of time to complete (think "database connection timeout"). 但是,我的一些测试花费的时间是不确定的(请考虑“数据库连接超时”)。 When this happens, users are stuck with whatever text was already in the status area and my application freezes until the tests complete or fail. 发生这种情况时,用户将被状态区域中已有的任何文本所困扰,并且我的应用程序将冻结,直到测试完成或失败。
I've tried calling repaint
on my JTextArea
every time I update the status, but it seems that just adds it to the event queue (which won't get fired till after my actionPerformed
event completes...doh). 我每次尝试更新状态时都尝试在JTextArea
上调用repaint
,但似乎只是将其添加到事件队列中(直到我的actionPerformed
事件完成后才会被触发... doh)。 I've also tried calling revalidate
and updateUI
, and even tried calling wait()
on the ActionListener
class running my tests, but nothing has worked thus far. 我还尝试调用revalidate
和updateUI
,甚至尝试在运行测试的ActionListener
类上调用wait()
,但到目前为止没有任何效果。 Here is the structure of my current code: 这是我当前代码的结构:
..
JTextArea statusOutput_textArea;
..
public void actionPerformed(ActionEvent e) {
setStatusText("Running Tests...");
//Run Tests
int currentTest = 1;
//Check something
appendStatusText(" Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
appendStatusText(" Success");
//Check something else
appendStatusText(" Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
appendStatusText(" Success");
//Other tests
.. //Run other tests here
appendStatusText("\nAll tests have completed successfully. Yay.");
}//End of actionPerformed method
public void setStatusText (String statusText) {
statusOutput_textArea.setText(statusText);
statusOutput_textArea.repaint();
}//End of setStatusText method
public void appendStatusText (String statusTextToAppend) {
statusOutput_textArea.setText(statusOutput_textArea.getText() + "\n" + statusTextToAppend);
statusOutput_textArea.repaint();
}//End of appendStatusText method
Any help would be much appreciated :) 任何帮助将非常感激 :)
UPDATE UPDATE
For those interested in the general structure of the solution, here it is: 对于那些对解决方案的总体结构感兴趣的人,这里是:
public class RunTestsButtonActionListener implements ActionListener {
JTextArea statusOutput_textArea;
JButton testDatabaseSettings_JButton;
public RunTestsButtonActionListener(JTextArea statusOutput_textArea){
this.statusOutput_textArea = statusOutput_textArea;
}
public void actionPerformed(ActionEvent e) {
testDatabaseSettings_JButton = (JButton) e.getSource();
Thread tests = new Thread(){
public void run() {
//Disable button and add tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("Running Tests...");
//Run Tests
try {
statusOutput_textArea.setText("Running Tests...");
int currentTest = 1;
//Check something
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
statusOutput_textArea.append("\n Success");
//Check something else
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
statusOutput_textArea.append("\n Success");
//Other tests
.. //Run other tests here
statusOutput_textArea.append("\n\nAll tests have completed successfully. Yay.");
} finally {
//Enable button and remove tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("");
}
}
};
tests.start();
}
}
Run each test in its own thread so they don't block each other. 在自己的线程中运行每个测试,以免彼此阻塞。 Also, JTextArea already has an thread-safe append
method, no need to use setText
to simulate append
. 而且,JTextArea已经具有线程安全的append
方法,无需使用setText
来模拟append
。
The problem is you can't have long running tasks happen in an action. 问题是您不能在一个动作中长时间运行任务。 As you've noticed, nothing will get updated when this happens. 如您所知,这种情况不会更新。
You should try taking a look at SwingWorker or some other background thread. 您应该尝试看看SwingWorker或其他一些后台线程。 You would run your tests in the background thread and every time you want to update the UI you would call: 您将在后台线程中运行测试,并且每次要更新UI时都将调用:
SwingUtils.invokeLater(new Runnable() {
public void run() {
appendStatusText(....);
}
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.