Как мне заставить JFrame отображать текстовое содержимое JPanel даже при вызове по событию действия?

У меня есть простой диалог JFrame, в котором должно отображаться сообщение «Пожалуйста, подождите», пока основная программа выполняет другие действия (что может занять от 30 секунд до более 10 минут):

public JFrame waitDialog() {
         JFrame wait = new JFrame();
         wait.setTitle("My Dialog");
         wait.setAlwaysOnTop(true); 
         wait.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
         wait.setSize(300, 150);
         wait.setLocationRelativeTo(null);  
         wait.setLayout(new BorderLayout());
         String msg = "Plase Wait...";
         wait.add(new JLabel(msg), BorderLayout.CENTER);
         return wait;
    }

Когда я вызываю его в ходе программы, он работает нормально:

     ...
     JFrame wait = waitDialog();
     wait.setVisisble(true);
     for (int i=0;i<1000;i++){
         // Other activities
         System.out.println(i);
     }
     wait.setVisisble(false);
     ...

Однако, если я использую выполнение этого диалогового окна через ActionEvent (я нажимаю кнопку «Выполнить другие действия» в JFrame основного приложения), даже если я использую ТОЧНО ЖЕ КОД для вызова waitDialog(), диалоговое окно отображается без какого-либо текста. Он отображает пустой диалог без сообщения «Пожалуйста, подождите...»:

     public void actionPerformed(ActionEvent action){
     if (action.getSource==waitforit) {
         runWait(); 
     }

     Public void runWait() {
        JFrame wait = waitDialog();
     wait.setVisisble(true);
     for (int i=0;i<1000;i++){
         // Other activities
         System.out.println(i);
     }
     wait.setVisisble(false);
         }

Я тщательно переработал это и не могу понять, почему текст JPanel не отображается ТОЛЬКО тогда, когда я вызываю диалог через ActionEvent.

Что мне здесь не хватает?

Всё равно так делать нельзя. У вас неполное представление о том, как работает потоковая обработка Swing. Взгляните на прекрасно документированную версию SwingWorker, и она даст вам некоторые идеи о том, как добиться того, чего вы хотите.

g00se 20.06.2024 17:26

После дополнительных исследований я вижу, что этот комментарий от @g00se является правильным ответом, но я не могу его выбрать, поскольку он не опубликован в качестве ответа.

Bud McGinty 21.06.2024 17:47
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Действие (например, нажатие кнопки) выполняется специальным потоком AWT-EventQueue. Тот самый поток, который отвечает за отрисовку всего кадра. Если вы позволите функции завершить рисование вашего JFrame и закрыть его после задержки в другом потоке, ваш код должен работать.

public void runWait() {
    JFrame wait = waitDialog();
    wait.setVisible(true);
    closeFrameDelayed(wait);
}
static void closeFrameDelayed(JFrame frame) {
    new Thread(() -> {
        try {
            Thread.sleep(2000); // Wait 2 seconds
        } catch (InterruptedException ex) {
            // No error handling (or printing) is bad error handling,
            // but that is out of scope here, i guess
            ex.printStackTrace(System.out);
        } finally {
            // SwingUtilities.invokeLater pushes the comand to the AWT-EventQueue
            // This is required, because AWT/Swing methods are not thread safe
            SwingUtilities.invokeLater(() -> {
                frame.setVisible(false);
            });
        }
    }).start();
}

При этом создавать несколько фреймов, как правило, не очень хорошая идея (см. Использование нескольких JFrames: хорошая или плохая практика?).

Как утверждает g00se в комментарии, основная проблема заключается в том, что вы выполняете тяжелые, блокирующие задачи в потоке пользовательского интерфейса. Попробуйте выполнить логику медленной «активности» в отдельном потоке и вызовите SwingUtilities.invokeLater(() -> frame.setVisible(false));, когда это будет сделано.

Вместо этого лучше используйте JDialog. На самом деле использование JDialog может по иронии судьбы показать реальную ситуацию. Потому что тогда «Другие действия» не будут выполняться, пока диалог не закроется ;)

g00se 20.06.2024 17:57

@ g00se это всего лишь опция (хотя и по умолчанию). setModal(false) делает свое дело. См. docs.oracle.com/javase/8/docs/api/java/awt/… или docs.oracle.com/javase/tutorial/uiswing/misc/modality.html для получения более новых версий Java.

Keks 20.06.2024 18:21

Да, я знаю setModal, и я предвидел, что вы это процитируете;) Дело в том, что ваш ответ работает против правильной координации потоков, поскольку тяжелые «Другие действия» в любом случае не должны выполняться в основном потоке. . Они должны выполняться в выделенном потоке, который координируется с потоком диспетчеризации событий. Ваш подход «Подожди 2 секунды» совершенно произволен и нескоординирован.

g00se 20.06.2024 19:24

@ g00se Вы правы, рекомендация JDialog без знания точного контекста может ввести в заблуждение. Я удалил эту строку и добавил подсказку по основной проблеме с «другими действиями» в очереди событий. Я согласен, что «подождите 2 секунды» — это произвольно. Это служит просто примером.

Keks 21.06.2024 11:17
Ответ принят как подходящий

Всё равно так делать нельзя. У вас неполное представление о том, как работает потоковая обработка Swing. Взгляните на прекрасно документированный SwingWorker, и он даст вам несколько идей о том, как добиться того, чего вы хотите.

Другие вопросы по теме