Обновление/перерисовка SWT/проблема компоновки

Я знаю, что многие люди сталкиваются с этой проблемой, но решения, которые я нашел в Интернете, похоже, не решают мою проблему. У меня есть композит с тремя кнопками. Я хочу следующее: когда я нажимаю одну кнопку, я хочу, чтобы какая-то другая кнопка была недоступна ( setEnabled(false) ), а через некоторое время (после выполнения метода) я хочу, чтобы кнопка снова была включена.

Многие такие проблемы решаются путем вызова метода layout() в родительском контейнере или это очень похожее решается вызовом Display.getCurrent().update();

Проще говоря, мой код можно резюмировать следующим образом:


import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;


public class app1 {

    protected Shell shell;

    /**
     * Launch the application.
     * @param args
     */
    public static void main(String[] args) {
        try {
            app1 window = new app1();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Open the window.
     */
    public void open() {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /**
     * Create contents of the window.
     */
    Button button1 , button2 , button3;
    Label label;
    protected void createContents() {
        shell = new Shell();
        shell.setSize(450, 300);
        shell.setText("SWT Application");
        shell.setLayout(new GridLayout(1,false));
        {
            final Composite composite = new Composite(shell, SWT.NONE);
            composite.setLayout(new GridLayout(3,false));
            GridData gd_composite = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);
            gd_composite.grabExcessHorizontalSpace = true;          
            gd_composite.horizontalSpan = 10;   //?
            gd_composite.verticalIndent = 5;
            composite.setLayoutData(gd_composite);
            GridData gd_button;

            {
                button1 = new Button(composite, SWT.NONE);
                button1.setText("Button 1");
                gd_button = new GridData(SWT.FILL, GridData.BEGINNING, false, false);
                gd_button.horizontalSpan = 1;
                button1.setLayoutData(gd_button);
                button1.addSelectionListener(new SelectionListener(){
                    public void widgetSelected(SelectionEvent e){
                        try{
                        button2.setEnabled(false);
                        button2.redraw();
                        button2.update();

                        //composite.redraw();
                        //composite.update();
                        //composite.layout();

                        shell.redraw();
                        shell.update();
                        shell.layout();                     
                        Display.getCurrent().update();
                        }   catch   (Exception e2)  {
                            System.err.println("exception e : " + e2.toString());
                        }

                        System.out.println("basla");


                        try {
                            System.out.println("sleep1");
                            Thread.sleep(100);
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }   catch (Throwable th)    {
                            System.err.println("th: " + th.toString());
                        }
                        try {
                            System.out.println("sleep2");
                            Thread.sleep(100);
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }   catch (Throwable th)    {
                            System.err.println("th: " + th.toString());
                        }
                        try {
                            System.out.println("sleep3");
                            Thread.sleep(100);
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }   catch (Throwable th)    {
                            System.err.println("th: " + th.toString());
                        }

                        for(int i=0 ; i < 10000 ; i++)
                        {
                            System.out.println(i);
                        }
                    }
                    public void widgetDefaultSelected(SelectionEvent e) {
                        System.err.println("widgetDefault !");
                    }
                });
            }
            {
                button2 = new Button(composite, SWT.NONE);
                button2.setText("Button 2");
                gd_button = new GridData(SWT.FILL, GridData.CENTER, false, false);
                gd_button.horizontalSpan = 1;
                button2.setLayoutData(gd_button);
                button2.addSelectionListener(new SelectionListener(){
                    public void widgetSelected(SelectionEvent e){
                        button1.setEnabled(false);
                        composite.layout();
                        for (int i=1; i<=100; i++) {
                             try {
                                  Thread.sleep(10);
                             } catch (Throwable th) {}
                            label.setText(i + " %");
                            label.update();
                        }
                    }
                    public void widgetDefaultSelected(SelectionEvent e) {}
                });
            }

            {
                label = new Label(composite , SWT.NONE);
                label.setText("0 %");
                label.update();
            }
        }
    }
}

Что происходит, кнопка отключается после достижения конца метода widgetSelected(). Однако метка часто обновляется без каких-либо проблем (даже если метод label.update() отсутствует)

Дополнительная информация: скажем, я отключаю кнопку, затем помещаю Thread.sleep() и затем включаю кнопку; он сначала спит, а затем быстро отключает и включает кнопку. Поэтому я считаю, что все такие запросы на отрисовку ставятся в очередь и обрабатываются в конце выполнения.

Полезная информация: я понял, что когда я создаю и отображаю MessageBox сразу после изменения отображения, происходит изменение отображения. Итак, если я внесу следующее изменение в свой метод widgetSelected:


button2.setEnabled(false)
MessageBox mBox = new MessageBox(Display.getCurrent().getActiveShell(), SWT.ICON_INFORMATION | SWT.OK);
mBox.setText("Information");
mBox.setMessage("Buttons updated!");
mBox.open();

кнопка станет серой, как только будет вызван метод widgetSelected(). Это заставляет меня поверить, что мое решение лежит в методах Display.getCurrent(). Тем не менее, я попытался

Display.getCurrent().getActiveShell().redraw()
Display.getCurrent().getActiveShell().update()
Display.getCurrent().getActiveShell().layout() 

методы, и они не решили мою проблему.

Спасибо, Эге


person Community    schedule 24.07.2009    source источник


Ответы (3)


Хорошо, я исправил ответ от ginu:

New Runnable().run() на самом деле ничего не делает, но идея верна:

Вам нужен новый поток для выполнения вашей работы. Проблема в том, что из этого потока вы не можете вызывать setEnabled для кнопок, потому что это можно сделать только из потока SWT-Event.

Так что вам нужен другой runnable для сброса кнопок. Второй исполняемый объект передается в Display.callAsync и возвращается до того, как он будет фактически выполнен, но здесь это не имеет значения. Вы также можете использовать Display.callSync( Runnable ), этот вызов заблокирует ваш вызывающий поток до тех пор, пока runnable не вернется.

Протестировал его в Eclipse, пока выглядит хорошо.

Изменить: кстати, причина, по которой вызов layout() или Display.update() не сработал, заключается в том, что вы в настоящее время блокируете SWT-Thread своей работой, поэтому вызовы layout/update ставятся в очередь и выполняются только при выходе обработчик события. Никогда не блокируйте обработчик событий для выполнения длительной работы. :)

package test;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class Test {

public static void main(final String[] args) {
    final Display display = new Display();
    final Shell shell = new Shell(display);
    shell.setLayout(new GridLayout(3, false));
    final Button button1 = new Button(shell, SWT.PUSH);
    button1.setText("Click");
    final Button button2 = new Button(shell, SWT.PUSH);
    button2.setText("Me");
    final Button button3 = new Button(shell, SWT.PUSH);
    button3.setText("Dude");

    button1.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    button2.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    button3.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));

    button2.setEnabled(false);
    button3.setEnabled(false);

    button1.addSelectionListener(new SelectionAdapter() {
        @Override
        public void widgetSelected(final SelectionEvent e) {
            button1.setEnabled(false);
            button2.setEnabled(true);
            button3.setEnabled(true);

            new Thread( new Runnable() {
                public void run() {
                    try {
                        // Do your operation here.
                        //
                        // Dummy sleep performed here instead.
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    shell.getDisplay().asyncExec( new Runnable() {
                        public void run() {
                          button1.setEnabled(true);
                          button2.setEnabled(false);
                          button3.setEnabled(false);
                        }
                    });
                }
            } ).start();
        }
    });

    shell.open();
    shell.pack();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch()) {
            display.sleep();
        }
    }

}

}

person derBiggi    schedule 25.08.2009
comment
Большое спасибо, это работает. Тем не менее, я все еще не мог получить ваше объяснение на 100%. Я знаю, что невозможно выполнять действия SWT в потоке, отличном от потока SWT. Когда я пишу SelectionAdapter, который сначала отключает все кнопки, затем выполняет спящий режим (в том же потоке, без создания нового потока), а затем включает все кнопки, он показывает недетерминированное поведение. Иногда он отключает 3-ю кнопку, спит, а затем включает их все. Иногда он отключает 2-ю кнопку, спит, а затем включает их все и т. д. Не могли бы вы объяснить мне это недетерминированное поведение? Еще раз спасибо. - person ; 17.09.2009

Похоже, ваш фрагмент не завершен, но в связи с вашей проблемой пришло на ум несколько вещей. Вероятно, вы можете использовать setEnabled, как показано во фрагменте ниже. Для более продвинутых вещей вы можете посмотреть GridLayout и GridData со свойством .exclude в сочетании с setVisible. Для справки страница SWT Snippets действительно великолепна.

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class App2 {

    public static void main(final String[] args) {
        final Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new GridLayout(3, false));
        final Button button1 = new Button(shell, SWT.PUSH);
        button1.setText("Click");
        final Button button2 = new Button(shell, SWT.PUSH);
        button2.setText("Me");
        final Button button3 = new Button(shell, SWT.PUSH);
        button3.setText("Dude");

        button1.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        button2.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        button3.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        button2.setEnabled(false);
        button3.setEnabled(false);

        button1.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                button1.setEnabled(false);
                button2.setEnabled(true);
                button3.setEnabled(false);
            }
        });

        button2.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                button1.setEnabled(false);
                button2.setEnabled(false);
                button3.setEnabled(true);
            }
        });

        button3.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                button1.setEnabled(true);
                button2.setEnabled(false);
                button3.setEnabled(false);
            }
        });

        shell.open();
        shell.pack();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }

    }
}
person Community    schedule 13.08.2009
comment
Привет, Джаред. Спасибо за ответ, но это не решает мою проблему. Моя проблема заключается в том, чтобы включить и отключить кнопку (с паузой между ними) в одном и том же теле функции (метод WidgetSelected). (т. е. в методе widgetSelected кнопки 1, сначала включив кнопку 3, а затем приостановив, а затем отключив кнопку 3) - person ; 14.08.2009

Я не совсем уверен, есть ли у вас проблема с включением/отключением кнопок или с задержкой между потоком выполнения.

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

Ваше здоровье. :-)

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class app1 {

    public static void main(final String[] args) {
        final Display display = new Display();
        final Shell shell = new Shell(display);
        shell.setLayout(new GridLayout(3, false));
        final Button button1 = new Button(shell, SWT.PUSH);
        button1.setText("Click");
        final Button button2 = new Button(shell, SWT.PUSH);
        button2.setText("Me");
        final Button button3 = new Button(shell, SWT.PUSH);
        button3.setText("Dude");

        button1.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        button2.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        button3.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));

        button2.setEnabled(false);
        button3.setEnabled(false);

        button1.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                button1.setEnabled(false);
                button2.setEnabled(true);
                button3.setEnabled(true);

                new Runnable() {
                    public void run() {

                        try {
                            // Do your operation here.
                            //
                            // Dummy sleep performed here instead.
                            Thread.sleep(1000);
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }

                    }
                }.run();

                button1.setEnabled(true);
                button2.setEnabled(false);
                button3.setEnabled(false);
            }
        });

        shell.open();
        shell.pack();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }

    }
}
person Community    schedule 24.08.2009
comment
Привет, Джину, Моя цель - включить какую-то кнопку, поработать и снова отключить кнопку. В частности, мне нужна кнопка отмены, которая активна только во время некоторого процесса. Поэтому, когда процесс начинается, я хочу, чтобы пользователь мог отменить кнопку. И когда процесс завершится, кнопка отмены должна стать неактивной. Спасибо за ваш ответ, но это тоже не сработало. Что происходит, так это то, что он не меняет состояние кнопки до тех пор, пока не будет выполнен фиктивный сон. И когда это будет сделано, он сделает все обновления на кнопке одновременно. Вы можете видеть, как он мигает (последовательно отключается и включается). Однако, спасибо - person ; 25.08.2009