Java Swing:在长事件期间重绘组件

发布于 2024-11-08 11:51:13 字数 3937 浏览 4 评论 0原文

我正在使用 Java Swing 来呈现应用程序的 GUI。我在 JButton 上设置了一个 ActionListener 来启动一系列测试。理想情况下,我想使用实时测试信息和结果更新状态区域(当前为 JTextArea)。换句话说,程序会在测试运行和完成时逐行设置文本区域,如下所示:

Running Tests... 
    Test 1:  Check Something...
         Success
    Test 2:  Check Something else...
         Success
    ..    //More testing output

All tests have completed successfully.  Yay.

问题是,当我的 actionPerformed 时,我无法更新状态文本事件方法仍在运行。当所有测试都快速完成时,这一切都很好。然而,我的一些测试需要无限的时间才能完成(想想“数据库连接超时”)。发生这种情况时,用户会陷入状态区域中已有的任何文本,并且我的应用程序会冻结,直到测试完成或失败。

每次更新状态时,我都尝试在 JTextArea 上调用 repaint ,但似乎只是将其添加到事件队列中(直到之后才会被触发)我的 actionPerformed 事件完成...doh)。我还尝试过调用 revalidateupdateUI,甚至尝试在运行的 ActionListener 类上调用 wait()我的测试,但到目前为止没有任何效果。这是我当前代码的结构:

..

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 

任何帮助将不胜感激:)

更新

对于那些对解决方案的一般结构感兴趣的人,这里是:

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

I am working with Java Swing to render my GUI for an application. I have an ActionListener set up on a JButton to fire off a series of tests. Ideally, I would like to update a status area (currently a JTextArea) with real-time testing information and results. 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. 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). 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. 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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蝶…霜飞 2024-11-15 11:51:13

您正在对“Swing 线程”进行检查,最好在不同的 线程(如果发生/更改,则通知 GUI)。

You are doing the checks on "the Swing thread", you would be better of performing those tasks in a different thread (which notifies the GUI if something happens/changes).

ま柒月 2024-11-15 11:51:13

在自己的线程中运行每个测试,这样它们就不会互相阻塞。另外,JTextArea已经有线程安全的append方法,不需要使用setText来模拟append

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.

︶ ̄淡然 2024-11-15 11:51:13

问题是您不能在一个操作中发生长时间运行的任务。正如您所注意到的,发生这种情况时不会有任何更新。

您应该尝试查看 SwingWorker 或其他一些后台线程。您将在后台线程中运行测试,每次您想要更新 UI 时,您都会调用:

SwingUtils.invokeLater(new Runnable() {
     public void run() {
        appendStatusText(....);
    }
});

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. You would run your tests in the background thread and every time you want to update the UI you would call:

SwingUtils.invokeLater(new Runnable() {
     public void run() {
        appendStatusText(....);
    }
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文