Графический интерфейс не обновляется визуально перед запуском ActionEvent

Чтобы объяснить немного больше, у меня есть графический интерфейс, который выглядит так:

введите здесь описание изображения

Затем у меня есть прослушиватель действий на кнопке OK, который начинается так:

//OK Button Action Listener
private void okButtonActionPerformed(ActionEvent e) {   
    //Enable/Disable Buttons
    okButton.setEnabled(false);
    cancelButton.setEnabled(true);
    updateCheckbox.setEnabled(false);
    //Move on to a series of other methods here...

Что должно, по идее, сделать это:

введите здесь описание изображения

Однако вместо этого я получаю следующее, пока не будут выполнены ВСЕ методы и другие вещи, связанные с кнопкой ОК:

введите здесь описание изображения

Это, очевидно, не может произойти, потому что идея состоит в том, чтобы сделать кнопку отмены доступной, а кнопку ОК и несколько других флажков недоступными на время работы программы (Изображение 2), где вместо этого она зависает в полусостоянии. (Изображение 3). Есть ли способ бороться с этим?


person JTApps    schedule 31.07.2013    source источник
comment
все внутри actionListener делается (на экране) в один момент, после выполнения всех строк кода, пожалуйста, какова настоящая цель   -  person mKorbel    schedule 31.07.2013
comment
Мне трудно понять, что именно вы имеете в виду, но, по сути, у меня есть длительный процесс, который должен выполняться при сохранении доступности графического интерфейса. Основываясь на других ответах, я должен использовать другой поток через SwingWorker.   -  person JTApps    schedule 31.07.2013
comment
У меня есть длительный процесс ---› затем следует и принять один из ответов здесь, кучу примеров кода, некоторые из них с отличными описаниями   -  person mKorbel    schedule 31.07.2013
comment
:-) этот вопрос и ответы здесь похожи на дикие шорты, основанные на догадках и наклеенных изображениях, мило, очень мило, но вы получаете ответы   -  person mKorbel    schedule 31.07.2013


Ответы (4)


Каждый раз, когда вы выполняете логику из графического интерфейса, вы должны использовать SwingWorker следующим образом:

SwingWorker myWorker= new SwingWorker<String, Void>() {
    @Override
    protected String doInBackground() throws Exception {
        //Execute your logic
        return null;
    }
};
myWorker.execute();

Если вы хотите обновить графический интерфейс изнутри этой логики, используйте InvokeLater:

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        //To update your GUI
    }
});

При этом вы можете быть уверены, что и ваша логика, и ваш графический интерфейс останутся отзывчивыми.

Изменить:

Вы также можете использовать invokeAndWait, если это больше соответствует вашим потребностям. Ссылка на соответствующий ответ

person bjedrzejewski    schedule 31.07.2013
comment
Э, не так уж и много. В SwingWorker есть отличная структура для передачи промежуточных обновлений с использованием publish и process; с какой стати ты используешь invokeLater?! - person Boris the Spider; 31.07.2013
comment
Логика, которой я следую, приведена здесь: stackoverflow.com/a/7196920/1611957 Не могли бы вы объяснить, почему, по вашему мнению, это так? не применять? Я имею в виду - это облегчает. - person bjedrzejewski; 31.07.2013
comment
Поскольку SwingWorker уже имеет очередь асинхронных задач, посредством которой вы можете передавать элементы в process, а затем EDT вызовет publish. Вы переопределяете publish и используете элемент для обновления графического интерфейса. Ваш метод требует создания другого анонимного класса (а именно Runnable), и если вы хотите передать в него какие-либо данные, то это должен быть final и т. д. Также вам необходимо учитывать безопасность потоков, поскольку Runnable будет вызываться EDT. - person Boris the Spider; 31.07.2013
comment
Звучит разумно, вы можете добавить его в свое решение, чтобы здесь было два способа добиться этого. - person bjedrzejewski; 31.07.2013
comment
Я никогда раньше не использовал SwingWorker. В своей самой простой форме я в основном хочу обернуть весь мой код ActionListener, который напрямую не связан с графическим интерфейсом внутри SwingWorker, в методе ActionEvent? - person JTApps; 31.07.2013
comment
Да JTApps - если вы это сделаете, графический интерфейс будет реагировать. Как обновить GUI изнутри этого (если нужно) — отдельная история, обсуждаемая здесь, в комментариях :) - person bjedrzejewski; 31.07.2013
comment
Кажется, теперь я понял, и я скоро попробую здесь. Мне нужно будет обновить графический интерфейс из-за того, что есть индикатор выполнения и счетчик процентов выполнения на экране, который нужно будет обновлять по мере продвижения потока. Однако это станет препятствием после того, как я в первую очередь получу отзывчивый графический интерфейс. - person JTApps; 31.07.2013
comment
Просто поместите все, что медленно внутри рабочего, и все обновления графического интерфейса (из рабочего) в invokeLater, если вы хотите простое и правильное решение :) - person bjedrzejewski; 31.07.2013
comment
Я попробую и посмотрю, смогу ли я заставить его работать :) Я также приму ваш ответ, поскольку он наиболее полно решает проблему обновления графического интерфейса. - person JTApps; 31.07.2013
comment
этот ответ не объясняет правильно способы, процесс, setProcess, публикацию и выполнение уведомленного EDT, без использования invokeLater в качестве уведомителя, doInBackground() является мостом между Swing и Workers Thread, этот метод не предназначен для уведомления EDT - person mKorbel; 31.07.2013

//Перейдем к ряду других методов здесь...

Убедитесь, что вы не блокируете поток GUI (EDT) длительными операциями. Вместо этого используйте SwingWorker.

person Eng.Fouad    schedule 31.07.2013

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

person user1111284    schedule 31.07.2013
comment
Это именно то, что я делаю, и я вижу, что я делаю неправильно. Мне просто нужно попытаться понять реализацию другого потока сейчас. - person JTApps; 31.07.2013

Да, это потому, что вы заблокировали EDT другими методами.

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

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

Ознакомьтесь с документацией для ознакомления.

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

Если вы хотите иметь возможность отменить фоновую задачу, вам нужно сохранить ссылку на SwingWorker, чтобы вы могли вызвать метод cancel. В этом случае метод работы должен быть прерываемым, иначе задача не может быть отменена.

person Boris the Spider    schedule 31.07.2013
comment
Cancel в этом случае просто полностью закроет программу, а не прервет ее. Судя по тому, как это звучит в различных ответах, может потребоваться серьезная реструктуризация кода, которую я НЕ хочу делать. Хотя я могу неправильно интерпретировать. Вы предлагаете по существу обернуть весь код прослушивателя действий, который не связан с графическим интерфейсом, с помощью SwingWorker? - person JTApps; 31.07.2013
comment
Да, это то, что вы должны сделать. Если вы хотите предоставлять обновления графического интерфейса по мере обработки, вы публикуете обновления, используя метод publish, и переопределяете метод process. Метод process будет работать в EDT, тем самым не нарушая однопоточную модель Swing. - person Boris the Spider; 31.07.2013
comment
Сейчас я занимаюсь исследованием, но у меня небольшие проблемы с логикой. Например, если бы я хотел выполнить cancelButton.setEnabled(true) при выполнении метода doSomething(), как бы это выглядело? Визуальное представление логики здесь помогло бы мне наилучшим образом применить ее. - person JTApps; 31.07.2013