You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在循环中动态更新JThermometer与JSlider的传感器数据?

传感器监控仪表盘实现方案

1. 封装传感器面板类

先把单个传感器的温度、湿度组件封装成独立面板类,让每个面板和传感器一一绑定,方便后续管理和更新:

import javax.swing.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.ThermometerPlot;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartPanel;
import org.jfree.data.general.DefaultValueDataset;

public class SensorPanel extends JPanel {
    private ChartPanel tempChartPanel;
    private JSlider humiditySlider;
    private final int sensorIndex;

    public SensorPanel(int sensorIndex) {
        this.sensorIndex = sensorIndex;
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        setBorder(BorderFactory.createTitledBorder("传感器 " + (sensorIndex + 1)));

        // 初始化温度组件(以JFreeChart的温度计为例)
        DefaultValueDataset tempDataset = new DefaultValueDataset(0);
        ThermometerPlot plot = new ThermometerPlot(tempDataset);
        plot.setRange(-10, 100); // 按需调整温度量程
        JFreeChart tempChart = new JFreeChart("温度(℃)", plot);
        tempChartPanel = new ChartPanel(tempChart);
        add(tempChartPanel);

        // 初始化湿度滑块
        humiditySlider = new JSlider(0, 100, 0);
        humiditySlider.setMajorTickSpacing(20);
        humiditySlider.setMinorTickSpacing(5);
        humiditySlider.setPaintTicks(true);
        humiditySlider.setPaintLabels(true);
        add(new JLabel("湿度(%):"));
        add(humiditySlider);
    }

    // 更新温度(强制在EDT线程执行)
    public void updateTemperature(double temp) {
        SwingUtilities.invokeLater(() -> {
            ThermometerPlot plot = (ThermometerPlot) tempChartPanel.getChart().getPlot();
            ((DefaultValueDataset) plot.getDataset()).setValue(temp);
        });
    }

    // 更新湿度(强制在EDT线程执行)
    public void updateHumidity(double humidity) {
        SwingUtilities.invokeLater(() -> {
            humiditySlider.setValue((int) Math.round(humidity));
        });
    }

    public int getSensorIndex() {
        return sensorIndex;
    }
}

2. 管理传感器面板集合

在主窗口中用列表存储所有传感器面板,解析数据时按索引直接匹配对应面板:

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import com.fazecast.jSerialComm.SerialPort;

public class DashboardFrame extends JFrame {
    private final List<SensorPanel> sensorPanels = new ArrayList<>();
    private final int sensorCount;

    public DashboardFrame(int sensorCount) {
        this.sensorCount = sensorCount;
        setTitle("传感器监控仪表盘");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BoxLayout(getContentPane(), BoxLayout.X_AXIS));

        // 初始化N个传感器面板
        for (int i = 0; i < sensorCount; i++) {
            SensorPanel panel = new SensorPanel(i);
            sensorPanels.add(panel);
            add(panel);
        }

        pack();
        setLocationRelativeTo(null);
        setVisible(true);

        // 启动串口读取线程(避免阻塞UI)
        startSerialReader();
    }

    // 串口读取逻辑(单独线程执行)
    private void startSerialReader() {
        new Thread(() -> {
            SerialPort serialPort = SerialPort.getCommPort("COM3"); // 替换为你的Arduino串口
            serialPort.setBaudRate(9600); // 匹配Arduino波特率
            if (!serialPort.openPort()) {
                JOptionPane.showMessageDialog(this, "无法打开串口");
                return;
            }

            while (serialPort.isOpen()) {
                try {
                    Thread.sleep(100);
                    if (serialPort.bytesAvailable() == 0) continue;
                    byte[] buffer = new byte[serialPort.bytesAvailable()];
                    serialPort.readBytes(buffer, buffer.length);
                    String rawData = new String(buffer).trim();
                    parseAndUpdate(rawData);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            serialPort.closePort();
        }).start();
    }

    // 解析Arduino数据并更新面板
    private void parseAndUpdate(String rawData) {
        String[] sensorDataArray = rawData.split(" ");
        if (sensorDataArray.length != sensorCount) {
            System.err.println("数据数量与传感器数量不匹配");
            return;
        }

        for (int i = 0; i < sensorCount; i++) {
            String[] tempHum = sensorDataArray[i].split(",");
            if (tempHum.length != 2) {
                System.err.println("传感器" + (i+1) + "数据格式错误");
                continue;
            }
            try {
                double temp = Double.parseDouble(tempHum[0]);
                double humidity = Double.parseDouble(tempHum[1]);
                sensorPanels.get(i).updateTemperature(temp);
                sensorPanels.get(i).updateHumidity(humidity);
            } catch (NumberFormatException e) {
                System.err.println("传感器" + (i+1) + "数据转换失败: " + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        // 必须在EDT线程启动UI
        SwingUtilities.invokeLater(() -> new DashboardFrame(2)); // 替换为实际传感器数量N
    }
}

3. 核心问题解决点

  • UI线程安全:所有组件更新操作都用SwingUtilities.invokeLater()包裹,确保在EDT线程执行,这是组件数值更新无效的常见原因。
  • 数据匹配逻辑:Arduino输出的每个传感器数据用空格分隔,单条数据内的温湿度用逗号分隔,解析后按索引直接对应到列表中的传感器面板,解决数据区分问题。
  • 串口读取线程:把串口IO放在独立线程,避免阻塞UI导致界面卡顿。

4. 依赖说明

如果使用jSerialComm串口库,添加Maven依赖:

<dependency>
    <groupId>com.fazecast</groupId>
    <artifactId>jSerialComm</artifactId>
    <version>2.9.0</version>
</dependency>

若使用其他第三方JThermometer组件,调整SensorPanel中的温度组件初始化和更新逻辑即可。

内容的提问来源于stack exchange,提问作者Mandakini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 19:35:19