Вывод в jTextArea в режиме реального времени - PullRequest
1 голос
/ 26 апреля 2010

У меня есть код, обработка которого занимает несколько минут, он должен подключаться к сети для каждой строки в длинном массиве, каждая строка является URL-адресом. Я хочу сделать так, чтобы при каждом подключении он обновлял jtextarea, чтобы пользователь не смотрел на пустую страницу, которая выглядит замороженной в течение 20 минут. или сколько времени это займет. Вот пример чего-то, что я пытался и не работал:

try {
            ArrayList<String> myLinks = LinkParser.getmyLinksArray(jTextArea1.getText());
            for (String s : myLinks) {
                jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
            }
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(jTextArea1, "Parsing Error", "Parsing Error", JOptionPane.ERROR_MESSAGE);
            Logger.getLogger(MYView.class.getName()).log(Level.SEVERE, null, ex);
        }

Ответы [ 3 ]

10 голосов
/ 26 апреля 2010

Проблема в том, что вам нужно выполнять вычисления асинхронно. Вы должны создать фоновый поток, который выполняет вычисления, а затем использовать SwingUtilities.invokeLater для обновления JTextArea.

final ArrayList<String> myLinks = //...
(new Thread()
{
    public void run(){
        for (String s : myLinks) {
            try{
               final String result = LinkChecker.checkFileStatus(s) + "\n";
               SwingUtilities.invokeLater(new Runnable(){ 
                    public void run(){    
                      jtextArea2.append(result);
                    }
                });
             }catch(IOException error){
                // handle error
             }
        }
    }
}).start();

Редактировать
Было отмечено, что функция добавления JTextArea на самом деле является поточно-ориентированной (в отличие от большинства функций Swing). Следовательно, для этого конкретного случая нет необходимости обновлять его через invokeLater. Тем не менее, вы все равно должны выполнять обработку в фоновом потоке, чтобы позволить обновлению GUI, поэтому код:

final ArrayList<String> myLinks = //...
(new Thread()
{
    public void run(){
        for (String s : myLinks) {
            try{
               jtextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
             }catch(IOException error){
                // handle error
             }
        }
    }
}).start();

Однако для почти любой другой операции, которая модифицирует объект Swing, вам нужно будет использовать invokeLater (чтобы гарантировать, что изменение происходит в потоке GUI), поскольку почти все функции Swing не являются поточно-ориентированными.

1 голос
/ 26 апреля 2010

Вам необходимо изучить многопоточность и ее отношение к обновлениям графического интерфейса в Swing. Все, что влияет или использует компоненты графического интерфейса в Swing, должно выполняться в специальном потоке, называемом Thread Dispatch Thread (EDT) .

Если ваш фрагмент кода, если он замораживает графический интерфейс, я представляю, что он выполняется в EDT. Выполнение длительного действия над EDT сделает графический интерфейс не отвечающим, поскольку дальнейшие обновления не могут быть выполнены, пока ваш длительный процесс использует поток.

Существует вспомогательный класс с именем SwingWorker, который позволяет вам выгружать длительные вычисления в фоновый поток, а затем вносить обновления в поток GUI после его завершения. SwingWorker обеспечивает переключение контекста между потоком GUI и фоновым потоком. Вы также можете отображать индикаторы выполнения, чтобы пользователь знал состояние длительного процесса, чтобы он знал, что ваше приложение не зависло.

0 голосов
/ 26 апреля 2010

swing / awt - это однопотоковая библиотека, поэтому после показа компонента изменение его внешнего вида будет работать некорректно. Вам нужно изменить компонент в потоке графического интерфейса, а не из вашего потока. Для этого оберните любой код, который обновляет компонент, с помощью SwingUtilities.invokeLater ... как в

SwingUtilities.invokeLater(new Runnable()
{
    public void run()
    {
        jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
    }
});

также вы хотите ограничить то, что вы делаете в потоке графического интерфейса, чтобы избежать замедления работы графического интерфейса, поэтому, если checkFileStatus занимает много времени, запустите его вне метода run и сохраните результат в конечной локальной переменной, а просто получите переменная в коде run ().

...