进度条不立即更新

7
我尝试使用进度条和标签来显示一系列操作的进度。每次更新它们时,标签会立即更新,而进度条在改变之前会有明显的延迟。我有一个测试对话框,在按下按钮时,我会更新标签和进度条以反映增加1。这个过程在代码中立即发生,没有调用任何循环或线程(消除了任何与线程相关的问题作为原因),只是简单地改变值。当这种情况发生时,标签立即更新,而进度条需要大约半秒钟才能在视觉上更新。这导致出现这样的情况,例如当它短暂但明显地显示1/3的填充时,它会说“2 out of 3”。
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;

public class ProgressBarTest extends Shell {

    private ProgressBar progressBar;
    private Label lblXOfX;
    private Button btnGo;

    public static void main(String args[]) {
        try {
            Display display = Display.getDefault();
            ProgressBarTest shell = new ProgressBarTest(display);
            shell.open();
            shell.layout();
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch()) {
                    display.sleep();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ProgressBarTest(Display display) {
        super(display, SWT.SHELL_TRIM);
        createContents();
    }

    protected void createContents() {
        setText("SWT Application");
        setSize(450, 300);
        setLayout(new GridLayout());

        progressBar = new ProgressBar(this, SWT.NONE);
        progressBar.setMaximum(3);
        progressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));

        lblXOfX = new Label(this, SWT.NONE);
        lblXOfX.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
        lblXOfX.setText("x of x");

        btnGo = new Button(this, SWT.NONE);
        btnGo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent arg0) {
                go();
            }
        });
        btnGo.setText("Go");
    }

    @Override
    protected void checkSubclass() {
        // Disable the check that prevents subclassing of SWT components
    }

    protected void go() {

        int iCur = progressBar.getSelection();
        iCur++;
        if (iCur > progressBar.getMaximum())
            iCur = 0;

        progressBar.setSelection(iCur);
        lblXOfX.setText(String.format("%s of %s", iCur, progressBar.getMaximum()));
    }

}

尽管使用进度条的内部变量来跟踪值并使用该值更新标签,仍会发生此问题。值得注意的是,将进度条的“状态”属性更改为“ERROR”或“PAUSED”可消除此问题。这是否是动画中的某种错误?这只是特定版本的Windows(例如我正在使用的Windows 7)的问题吗?还是其他什么原因?

@Chris 很好,已经完成了。 - Southpaw Hare
我猜这与在 Windows 7 上 ProgressBar 中使用的动画有关,因为其他状态都没有动画。此外,您的代码在Linux上完美运行。您可以在此处发布错误报告,让开发人员知道。 - Baz
在 Mac OS X 上也可以正常运行。ProgressBar.setSelection 的 Windows 版本确实有一些代码尝试处理类似于这样的问题,但仅在状态不为 Normal 时执行 - 这可能是它在错误和暂停状态下工作的原因。他们的解决方案只是再次进行选择,所以可能调用 setSelection 两次会起作用! - greg-449
@greg-449 好的建议,评论中指出:“Vista 中存在一个错误。由于某种原因,当进度条不处于正常状态时,它会显示先前调用 PBM_SETPOS 的选择。这是未记录的。修复方法是再次调用 PBM_SETPOS。” 因此,似乎这是解决 Vista 问题的方法... 绝对值得提交错误报告。 - Baz
@Baz 你们干得不错。请用参考资料和详细信息给出答案,我们就好了。 - Southpaw Hare
显示剩余2条评论
4个回答

4

看起来这可能是Windows版本的ProgressBar中存在的错误,因为在Linux和Mac OS X上的代码是正常的。

ProgressBar.setSelection的Windows版本确实有一些代码,声称它正在尝试解决类似于此问题的问题,但仅当状态不是Normal时才执行-这可能是为什么它在Error和Paused状态下工作的原因。他们的修复方法只是再次进行选择,因此可能调用两次setSelection会起作用!


1
我已经尝试过连续两次使用setSelection,但没有成功。这里还有什么诀窍吗? - Southpaw Hare

1

几周前我遇到了这个问题,当使用条形图来显示连续完成的大约500个操作的进度时。我发现进程在条形图甚至只完成了一半之前就已经完成了很多。我使用下面的代码来解决:

bargraph.value = (Target + 1)
bargraph.value = (Target)

这里的bargraph是您对象的名称,target是您想要它移动到的位置。

由于bargraph具有图形效果——跑马灯效果,它需要时间才能追赶。 即使图形方面显示的值为50,实际的bargraph.value可能为60!通过将值设置为一个级别,然后将其减少一个,bargraph会快速(或稍微慢一些)更新图形界面。

编辑

正如下面的评论所示,上面的答案在您尝试将值设置为最大值时可能无法工作。

现在,我还没有尝试过这个,但我认为以下类似的方法会起作用:

if (bargraph.value <> Target) then
   if (Target = bargraph.maximumvalue) then
      bargraph.maximumvalue = Target + 1
      bargraph.value = (Target + 1)
      bargraph.value = (Target)
      bargraph.maximumvalue = Target
   else
      bargraph.value = (Target + 1)
      bargraph.value = (Target)
   end if
else
end if

就像我说的一样,没有经过测试,但原则应该是正确的。


嗯,是的,我也注意到了。但是如果目标是最大值,这种情况下会有效吗? - Southpaw Hare
我记不得确切的错误,但我想你会得到一个类似于访问大小为X的数组的X + 1索引的错误。我没有尝试过这个,但如果你尝试上面的编辑,它可能会起作用。 - Sarima

1
你可以参考这篇教程,它介绍了如何创建Eclipse Jobs来执行长时间任务,使其与EDT分离,并更新组件状态。 希望对你有所帮助,祝好!

除非另有说明,否则本教程不太可能有所帮助,这与线程有关或者与缺乏线程有关是不相关的。实际上,不展示线程的原因就在于此。如果您有不同看法,请详细阐述。 - Southpaw Hare

1

我已使用最新版本的SWT(4.3 Final Release - 2013年6月5日)在Windows 7 SP1和Windows XP SP3下测试了您提供的代码。在两种情况下,进度条都能够及时更新。也许更新SWT库会有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接