简体   繁体   English

通过线程更新GUI

[英]Updating GUI through threads

I'm fairly new to threads and haven't written Java in a long time so bear with me here. 我对线程还很陌生,很久没有写Java了,所以请在这里接受。 I have a very simple GUI. 我有一个非常简单的GUI。 It has a counter, a status label, and two buttons, start and stop respectively. 它有一个计数器,一个状态标签和两个按钮,分别是开始和停止。

What I wanted it to do was update my status label using a counter thread. 我想要做的是使用counter线程更新我的状态标签。 When I hit start it supposedly starts the counter at 0 and increments it every second , and when I choose to hit stop it should suspend the current thread and wait for the start button to be pressed again. 当我开始打它理应启动计数器为0,并增加它的每一second ,当我选择打stop它应该suspend当前线程和wait的启动按钮被再次按下。 However whenever I hit stop it just suspends an waits for a second and resumes the counting. 但是,无论何时我按下停止,它都会暂停一秒钟,然后恢复计数。 When in reality I want it to stay suspended. 实际上,我希望它保持暂停状态。 I'm not really sure why it's doing that, tried searching it before posting here but got nothing. 我不太确定为什么要这么做,尝试在将其发布到此处之前进行搜索,但一无所获。 Also feel free to criticize on anything you'd like. 也可以随意批评您想要的任何东西。

Here's what I have: 这是我所拥有的:

UPDATED AS PER @MadProgrammer's answer. 根据@MadProgrammer的答案进行更新。

import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class main extends JFrame 
{


    JLabel countLabel = new JLabel("0");
    JLabel statusLabel = new JLabel("Task not completed.");
    JButton startButton = new JButton("Start");
    JButton stopButton = new JButton("Stop");
    CounterThread worker = new CounterThread("worker", countLabel, statusLabel);

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Main("Counter Demo");
            }
        });
    }

    public Main(String title) {
        super(title);

        setLayout(new GridBagLayout());

        countLabel.setFont(new Font("serif", Font.BOLD, 28));

        GridBagConstraints gc = new GridBagConstraints();

        gc.fill = GridBagConstraints.NONE;

        gc.gridx = 0;
        gc.gridy = 0;
        gc.weightx = 1;
        gc.weighty = 1;
        add(countLabel, gc);

        gc.gridx = 0;
        gc.gridy = 1;
        gc.weightx = 1;
        gc.weighty = 1;
        add(statusLabel, gc);

        gc.gridx = 0;
        gc.gridy = 2;
        gc.weightx = 1;
        gc.weighty = 1;
        add(startButton, gc);

        gc.gridx = 0;
        gc.gridy = 3;
        gc.weightx = 1;
        gc.weighty = 1;
        add(stopButton, gc);

        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                worker.start();
                //notify();
            }
        });
        stopButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                worker.suspend();
            }
        });
        setSize(200, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

public class CounterThread implements Runnable {

    public Thread t;
    public String threadName;
    boolean suspended = false;
    JLabel countLabelName;
    JLabel statusLabelName;

    CounterThread(String name, JLabel cLabel, JLabel sLabel) {
        this.threadName = name;
        this.countLabelName = cLabel;
        this.statusLabelName = sLabel;
    }

    public void run() {
        try {
            // Simulate doing something useful.
            for (int i = 0; i <= 10; i++) {
                synchronized (this) {
                    if (suspended) 
                    {             
                        wait();
                    }
                }
                final int count = i;

                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        countLabelName.setText(Integer.toString(count));
                    }
                });
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
        }

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                statusLabelName.setText("Completed.");
            }
        });

        this.start();
    }

    public boolean getStatus() {
        return t == null;
    }

    public void start() {
        if (getStatus()) {
            //t = new Thread(new CounterThread(this.threadName, this.countLabelName, this.statusLabelName));
            t = new Thread(this);
            t.start();
        }
    }

    public void suspend() {
        statusLabelName.setText("Task is paused");
        suspended = true;
    }
    //create an object whose only purpose is to synchronize

    synchronized void resume() {
        statusLabelName.setText("Task has resumed");
        suspended = false;
        this.notify();
    }

}
}

Basically... 基本上...

synchronized(this)
{
    if(suspended)
    {
        if(getStatus())
            wait();
        resume();
    }
}

getStatus is returning false so it's skipping the wait call (because t != null . getStatus返回的是false因此它跳过了wait调用(因为t != null

I'm not really sure why need to check this, but I might have a enum or other flag which returns a more meaningful state (like RUNNING , STOPPED , PAUSED ... what ever) 我不太确定为什么需要检查一下,但是我可能有一个enum或其他标志返回了更有意义的状态(例如RUNNINGSTOPPEDPAUSED ……等等)

I was able to make it work by doing something like... 我可以通过做类似的事情来使其工作...

synchronized(this)
{
    if(suspended)
    {
        wait();
    }
}

instead. 代替。

Having said that though. 话虽如此。 I'd personally consider using a Swing Timer which would do all this work for you and would trigger it's updates within the context of the EDT 我个人考虑使用Swing Timer ,它将为您完成所有这些工作,并会在EDT范围内触发它的更新。

Updated after original code was modified 修改原始代码后更新

Even with your suggested answer it's still behaving the same way, it suspends it for a brief second and resumes right away 即使您建议的答案仍然是相同的方式,它会暂时将其暂停并立即恢复

You modified the code from the original post, adding 您修改了原始帖子中的代码,并添加了

t = new Thread(new CounterThread(this.threadName, this.countLabelName, this.statusLabelName));

to the start method, but your UI code already has a reference to CounterThread which it's interacting with, so now you have two instances of the same class, one which is running in the background ticking away and one which your UI code is interacting with. start方法,但是您的UI代码已经引用了要CounterThread交互的CounterThread ,因此现在您有两个相同类的实例,一个在后台运行,并且与您的UI代码进行交互。

So when the UI calls suspend , it's not changing the suspended state of the instance which is actually running 因此,当UI调用suspend ,它不会更改实际正在运行的实例的suspended状态

import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Main extends JFrame {

    JLabel countLabel = new JLabel("0");
    JLabel statusLabel = new JLabel("Task not completed.");
    JButton startButton = new JButton("Start");
    JButton stopButton = new JButton("Stop");
    int holder;
    CounterThread worker = new CounterThread("worker", countLabel, statusLabel);

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Main("Counter Demo");
            }
        });
    }

    public Main(String title) {
        super(title);

        setLayout(new GridBagLayout());

        countLabel.setFont(new Font("serif", Font.BOLD, 28));

        GridBagConstraints gc = new GridBagConstraints();

        gc.fill = GridBagConstraints.NONE;

        gc.gridx = 0;
        gc.gridy = 0;
        gc.weightx = 1;
        gc.weighty = 1;
        add(countLabel, gc);

        gc.gridx = 0;
        gc.gridy = 1;
        gc.weightx = 1;
        gc.weighty = 1;
        add(statusLabel, gc);

        gc.gridx = 0;
        gc.gridy = 2;
        gc.weightx = 1;
        gc.weighty = 1;
        add(startButton, gc);

        gc.gridx = 0;
        gc.gridy = 3;
        gc.weightx = 1;
        gc.weighty = 1;
        add(stopButton, gc);

        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                worker.start();
            }

        });

        stopButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                worker.suspend();
            }
        });
        setSize(200, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

    public class CounterThread implements Runnable {

        public Thread t;
        public String threadName;
        boolean suspended = false;
        JLabel countLabelName;
        JLabel statusLabelName;

        CounterThread(String name, JLabel cLabel, JLabel sLabel) {
            this.threadName = name;
            this.countLabelName = cLabel;
            this.statusLabelName = sLabel;
        }

        public void run() {
            try {
                // Simulate doing something useful.
                for (int i = 0; i <= 10; i++) {
                    synchronized (this) {
                        if (suspended) {
                            wait();
                        }
                    }
                    final int count = i;

                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            countLabelName.setText(Integer.toString(count));
                        }
                    });

                    Thread.sleep(1000);

                }
            } catch (InterruptedException e) {
            }

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    statusLabelName.setText("Completed.");
                }
            });

            this.start();
        }

        public boolean getStatus() {
            return t == null;
        }

        public void start() {
            if (getStatus()) {
                //t = new Thread(new CounterThread(this.threadName, this.countLabelName, this.statusLabelName));
                t = new Thread(this);
                t.start();
            }
        }

        public void suspend() {
            statusLabelName.setText("Task is paused");
            suspended = true;
        }
        //create an object whose only purpose is to synchronize

        synchronized void resume() {
            statusLabelName.setText("Task has resumed");
            suspended = false;
            this.notify();
        }

    }
}

Also, I don't see how using a Swing Timer would help me in this case, since there's no actual delay for the wait 另外,在这种情况下,我看不到使用Swing计时器有什么帮助,因为等待没有实际延迟

Then you clearly don't understand how a Timer works 那么您显然不了解Timer工作原理

import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Main extends JFrame {

    JLabel countLabel = new JLabel("0");
    JLabel statusLabel = new JLabel("Task not completed.");
    JButton startButton = new JButton("Start");
    JButton stopButton = new JButton("Stop");
    int holder;

    Timer timer;
    int count = 0;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Main("Counter Demo");
            }
        });
    }

    public Main(String title) {
        super(title);

        setLayout(new GridBagLayout());

        countLabel.setFont(new Font("serif", Font.BOLD, 28));

        GridBagConstraints gc = new GridBagConstraints();

        gc.fill = GridBagConstraints.NONE;

        gc.gridx = 0;
        gc.gridy = 0;
        gc.weightx = 1;
        gc.weighty = 1;
        add(countLabel, gc);

        gc.gridx = 0;
        gc.gridy = 1;
        gc.weightx = 1;
        gc.weighty = 1;
        add(statusLabel, gc);

        gc.gridx = 0;
        gc.gridy = 2;
        gc.weightx = 1;
        gc.weighty = 1;
        add(startButton, gc);

        gc.gridx = 0;
        gc.gridy = 3;
        gc.weightx = 1;
        gc.weighty = 1;
        add(stopButton, gc);

        timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                count++;
                countLabel.setText(Integer.toString(count));
            }
        });

        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                timer.start();
            }

        });

        stopButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if (timer.isRunning()) {
                    timer.stop();
                    stopButton.setText("Resume");
                } else {
                    timer.restart();
                    stopButton.setText("Stop");
                }
            }
        });
        setSize(200, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

    }
}

Now, there is the pause and resume issues all taken care off 现在,暂停和恢复问题已全部解决

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM