从另一个线程实时更新JLabel文本 [英] Updating JLabel text from another thread in real-time

查看:125
本文介绍了从另一个线程实时更新JLabel文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要创建一个GUI来显示来自串行端口的实时数据.我正在从一个单独的线程读取串行端口数据,并且需要从那里更新GUI.我目前的实现方式是这样的.

I need to create a GUI to show real-time data coming from the Serial port. I'm reading serial port data from a separate thread and I need to update GUI from there. My current implementation is like this.

class Gui extends JFrame {
    private JLabel lbl = new JLabel();
    ....
    void updateLabel(String text) {
        lbl.setText(text);
    }
}

class CommPortReceiver extends Thread {
    private Gui gui = new Gui();

    void run() {
        gui.setVisible(true);
        ....
        while (true) {  
            if (dataAvailable) {    
                ....          
                gui.updateLabel(data);
                sleep(10);
            }
        }
    }
}

我每秒接收大约10个值,我希望Swing可以处理.我的问题是JLabel无法实时更新,并且由于显示了最新数据而错过了一些数据.我该如何解决?

I'm receiving about 10 values per second, I hope Swing can handle that. My problem is the JLabel is not updating real-time and it misses some data as it shows the latest one. How can I fix this?

推荐答案

您可以实现一个线程安全模型,该模型封装了视图所需的数据.应该通过来自串行端口(由Worker类表示)的信息来更新模型.
该视图应侦听模型更改和更新.

You could implement a thread-safe model, that encapsulates the data the view needs. The model should be updated by the information from the serial port (represented by the Worker class).
The view should listen to model changes and update.

以下代码实现了Model-View-Controller模式.它是一个文件 SSCCE :可以复制粘贴到ViewUpdatedByThread.java中并运行.
看法就是这样.它使用Observer界面侦听Model中的更改.
该模型封装了视图所需的信息(在这种情况下,只是一个双精度值).它允许值的线程安全更新,并在信息更改时通知观察者(视图).
Worker类使用线程来更改Model中的信息.
Controller协调各种成员:初始化它们,并将视图链接到模型:

The following code implements Model-View-Controller pattern. It is a one-file SSCCE : it can be copy-pasted into ViewUpdatedByThread.java and run.
The view is just that. It listens to changes in Model using Observer interface.
The Model encapsulates the information that the view needs (in this case just a double value). It allows thread-safe update of the value, and notifies the observers (the view) when information changes.
The Worker class uses a thread to change the information in Model.
The Controller orchestrates the various members : initialize them, and links view to model:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.util.Collections;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class ViewUpdatedByThread {

    public static void main(String[] args) {
        new Controller();
    }
}

//Controller of the MVC pattern."wires" model and view (and in this case also worker)
class Controller{

    public Controller() {

        Model model = new Model();
        View view = new View(model);
        model.registerObserver(view); //register view as an observer to model

        Worker worker = new Worker(model);

        view.getStopBtn().addActionListener(e -> worker.cancel());
    }
}

//view of the MVC pattern. Implements observer to respond to model changes
class View implements Observer{

    private final Model model;
    private final DataPane pane;
    private final JButton stopBtn;

    public View(Model model) {

        this.model = model;
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        pane = new DataPane();
        frame.add(pane, BorderLayout.CENTER);

        stopBtn = new JButton("Stop");
        frame.add(stopBtn, BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);
    }

    JButton getStopBtn()  { return stopBtn; }

    @Override
    public void onObservableChanged() { //update text in response to change in model
        pane.setText(String.format("%.2f",model.getValue()));
    }

    class DataPane extends JPanel {

        private final JLabel label;

        DataPane() {
            setPreferredSize(new Dimension(200, 100));
            setLayout(new GridBagLayout());
            label = new JLabel(" ");
            add(label);
        }

        void setText(String text){  label.setText(text); }
    }
}

//Model of the MVC pattern. Holds the information view needs
//Notifies observers (in this case View) when model changes
class Model { //you can make it generic Model<T>

    //the value that needs to be updated
    private Double value = 0.;

    // thread safe set for observers
    private final Set<Observer> mObservers = Collections.newSetFromMap(
                                        new ConcurrentHashMap<Observer, Boolean>(0));
    Model() {}

    //set all elements to value
    void changeValue(Double value){
        this.value = value;
        notifyObservers();
    }

    synchronized Double getValue() { return value; }

    synchronized void setValue(Double value) {  this.value = value; }

    //-- handle observers

    // add new Observer - it will be notified when Observable changes
    public void registerObserver(Observer observer) {
        if (observer != null) {
            mObservers.add(observer);
        }
    }

    //remove an Observer
    public void unregisterObserver(Observer observer) {
        if (observer != null) {
            mObservers.remove(observer);
        }
    }

    //notifies registered observers
    private void notifyObservers() {
        for (Observer observer : mObservers) {
            observer.onObservableChanged();
        }
    }
}

//Interface implemented by View and used by Model
interface Observer {
    void onObservableChanged();
}

//Encapsulates thread that does some work on model
class Worker implements Runnable{

    private final Model model;
    private boolean cancel = false;
    private final Random rnd = new Random();

    public Worker(Model model) {
        this.model = model;
        new Thread(this).start();
    }

    @Override
    public void run() {
        while(! cancel){
            model.changeValue(rnd.nextDouble()* 100);  //generate random value
            try {
                TimeUnit.MILLISECONDS.sleep(300); //pause
            } catch (InterruptedException ex) { ex.printStackTrace();   }
        }
    }

    void cancel() { cancel = true;  }
}



这篇关于从另一个线程实时更新JLabel文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆