如何在循环中动态更新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
相关产品推荐
相关产品推荐

