forked from M-Labs/thermostat
Plot temperature and current graphs
- Have units - Samples are limited - pglive is used for better live graphs -- Also fixes bug with constantly updating normal pyqtgraphs where it will bug out if right-clicked on and context menu is brought up --Since pglive requires pyqtgraph == 0.13.3, upgrade pyqtgraph to that too.
This commit is contained in:
parent
90df3ae784
commit
fdf4c4f0d6
27
flake.nix
27
flake.nix
@ -68,13 +68,36 @@
|
||||
propagatedBuildInputs = [ pkgs.python3Packages.pyqt6 ];
|
||||
};
|
||||
|
||||
pyqtgraph = pkgs.python3Packages.buildPythonPackage rec {
|
||||
pname = "pyqtgraph";
|
||||
version = "0.13.3";
|
||||
format = "pyproject";
|
||||
src = pkgs.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "sha256-WBCNhBHHBU4IQdi3ke6F4QH8KWubNZwOAd3jipj/Ks4=";
|
||||
};
|
||||
propagatedBuildInputs = with pkgs.python3Packages; [ numpy pyqt6 ];
|
||||
};
|
||||
|
||||
pglive = pkgs.python3Packages.buildPythonPackage rec {
|
||||
pname = "pglive";
|
||||
version = "0.7.2";
|
||||
format = "pyproject";
|
||||
src = pkgs.fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "sha256-jqj8X6H1N5mJQ4OrY5ANqRB0YJByqg/bNneEALWmH1A=";
|
||||
};
|
||||
buildInputs = [ pkgs.python3Packages.poetry-core ];
|
||||
propagatedBuildInputs = [ pyqtgraph pkgs.python3Packages.numpy ];
|
||||
};
|
||||
|
||||
thermostat_gui = pkgs.python3Packages.buildPythonPackage {
|
||||
pname = "thermostat_gui";
|
||||
version = "0.0.0";
|
||||
src = "${self}/pytec";
|
||||
|
||||
nativeBuildInputs = [ pkgs.qt6.wrapQtAppsHook ];
|
||||
propagatedBuildInputs = [ pkgs.qt6.qtbase ] ++ (with pkgs.python3Packages; [ pyqtgraph pyqt6 qasync ]);
|
||||
propagatedBuildInputs = [ pkgs.qt6.qtbase ] ++ (with pkgs.python3Packages; [ pyqtgraph pyqt6 qasync pglive ]);
|
||||
|
||||
dontWrapQtApps = true;
|
||||
postFixup = ''
|
||||
@ -100,7 +123,7 @@
|
||||
buildInputs = with pkgs; [
|
||||
rust openocd dfu-util
|
||||
] ++ (with python3Packages; [
|
||||
numpy matplotlib pyqtgraph setuptools pyqt6 qasync
|
||||
numpy matplotlib pyqtgraph setuptools pyqt6 qasync pglive
|
||||
]);
|
||||
};
|
||||
defaultPackage.x86_64-linux = thermostat;
|
||||
|
@ -1,8 +1,12 @@
|
||||
from PyQt6 import QtWidgets, QtGui
|
||||
from PyQt6.QtCore import pyqtSignal, QObject, QSignalBlocker, pyqtSlot
|
||||
from pyqtgraph import PlotWidget
|
||||
from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType
|
||||
import pyqtgraph as pg
|
||||
from pglive.sources.data_connector import DataConnector
|
||||
from pglive.kwargs import Axis
|
||||
from pglive.sources.live_plot import LiveLinePlot
|
||||
from pglive.sources.live_plot_widget import LivePlotWidget
|
||||
from pglive.sources.live_axis import LiveAxis
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
@ -126,11 +130,17 @@ class ClientWatcher(QObject):
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
|
||||
"""The maximum number of sample points to store."""
|
||||
DEFAULT_MAX_SAMPLES = 1000
|
||||
|
||||
def __init__(self, args):
|
||||
super().__init__()
|
||||
|
||||
self.setupUi(self)
|
||||
|
||||
self.max_samples = self.DEFAULT_MAX_SAMPLES
|
||||
|
||||
self._set_up_context_menu()
|
||||
|
||||
self.fan_power_slider.valueChanged.connect(self.fan_set)
|
||||
@ -138,11 +148,24 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
|
||||
self._set_param_tree()
|
||||
|
||||
self.ch0_t_plot = LiveLinePlot()
|
||||
self.ch0_i_plot = LiveLinePlot()
|
||||
self.ch1_t_plot = LiveLinePlot()
|
||||
self.ch1_i_plot = LiveLinePlot()
|
||||
|
||||
self._set_up_graphs()
|
||||
|
||||
self.ch0_t_connector = DataConnector(self.ch0_t_plot, max_points=self.DEFAULT_MAX_SAMPLES)
|
||||
self.ch0_i_connector = DataConnector(self.ch0_i_plot, max_points=self.DEFAULT_MAX_SAMPLES)
|
||||
self.ch1_t_connector = DataConnector(self.ch1_t_plot, max_points=self.DEFAULT_MAX_SAMPLES)
|
||||
self.ch1_i_connector = DataConnector(self.ch1_i_plot, max_points=self.DEFAULT_MAX_SAMPLES)
|
||||
|
||||
self.fan_pwm_recommended = False
|
||||
|
||||
self.tec_client = Client()
|
||||
self.client_watcher = ClientWatcher(self, self.tec_client, self.report_refresh_spin.value())
|
||||
self.client_watcher.fan_update.connect(self.fan_update)
|
||||
self.client_watcher.report_update.connect(self.plot)
|
||||
self.client_watcher.report_update.connect(self.update_report)
|
||||
self.client_watcher.pid_update.connect(self.update_pid)
|
||||
self.client_watcher.pwm_update.connect(self.update_pwm)
|
||||
@ -175,6 +198,31 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
|
||||
self.thermostat_settings.setMenu(self.menu)
|
||||
|
||||
def _set_up_graphs(self):
|
||||
for graph in self.ch0_t_graph, self.ch0_i_graph, self.ch1_t_graph, self.ch1_i_graph:
|
||||
time_axis = LiveAxis('bottom', text="Time since Thermostat reset", **{Axis.TICK_FORMAT: Axis.DURATION})
|
||||
time_axis.showLabel()
|
||||
graph.setAxisItems({'bottom': time_axis})
|
||||
|
||||
for graph in self.ch0_t_graph, self.ch1_t_graph:
|
||||
temperature_axis = LiveAxis('left', text="Temperature", units="°C")
|
||||
temperature_axis.showLabel()
|
||||
graph.setAxisItems({'left': temperature_axis})
|
||||
|
||||
for graph in self.ch0_i_graph, self.ch1_i_graph:
|
||||
current_axis = LiveAxis('left', text="Current", units="A")
|
||||
current_axis.showLabel()
|
||||
graph.setAxisItems({'left': current_axis})
|
||||
|
||||
self.ch0_t_graph.addItem(self.ch0_t_plot)
|
||||
self.ch0_i_graph.addItem(self.ch0_i_plot)
|
||||
self.ch1_t_graph.addItem(self.ch1_t_plot)
|
||||
self.ch1_i_graph.addItem(self.ch1_i_plot)
|
||||
|
||||
def clear_graphs(self):
|
||||
for connector in self.ch0_t_connector, self.ch0_i_connector, self.ch1_t_connector, self.ch1_i_connector:
|
||||
connector.clear()
|
||||
|
||||
async def _on_connection_changed(self, result):
|
||||
self.graph_group.setEnabled(result)
|
||||
self.fan_group.setEnabled(result)
|
||||
@ -191,6 +239,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
self.status_lbl.setText("Disconnected")
|
||||
self.fan_pwm_warning.setPixmap(QtGui.QPixmap())
|
||||
self.fan_pwm_warning.setToolTip("")
|
||||
self.clear_graphs()
|
||||
self.client_watcher.stop_watching()
|
||||
|
||||
def _set_fan_pwm_warning(self):
|
||||
@ -270,6 +319,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
||||
await self._on_connection_changed(False)
|
||||
await self.tec_client.disconnect()
|
||||
|
||||
@pyqtSlot(list)
|
||||
def plot(self, report):
|
||||
for channel in range(2):
|
||||
temperature = report[channel]['temperature']
|
||||
current = report[channel]['tec_i']
|
||||
time = report[channel]['time']
|
||||
|
||||
if temperature is not None and current is not None:
|
||||
getattr(self, f'ch{channel}_t_connector').cb_append_data_point(temperature, time)
|
||||
getattr(self, f'ch{channel}_i_connector').cb_append_data_point(current, time)
|
||||
|
||||
@asyncSlot(object, object)
|
||||
async def send_command(self, param, changes):
|
||||
for param, change, data in changes:
|
||||
|
@ -104,28 +104,28 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="PlotWidget" name="ch1_t_graph" native="true">
|
||||
<widget class="LivePlotWidget" name="ch1_t_graph" native="true">
|
||||
<property name="title">
|
||||
<string>Channel 1 Temperature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="PlotWidget" name="ch0_t_graph" native="true">
|
||||
<widget class="LivePlotWidget" name="ch0_t_graph" native="true">
|
||||
<property name="title">
|
||||
<string>Channel 0 Temperature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="PlotWidget" name="ch0_i_graph" native="true">
|
||||
<widget class="LivePlotWidget" name="ch0_i_graph" native="true">
|
||||
<property name="title">
|
||||
<string>Channel 0 Current</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="PlotWidget" name="ch1_i_graph" native="true">
|
||||
<widget class="LivePlotWidget" name="ch1_i_graph" native="true">
|
||||
<property name="title">
|
||||
<string>Channel 1 Current</string>
|
||||
</property>
|
||||
@ -656,18 +656,18 @@
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PlotWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pyqtgraph</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ParameterTree</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pyqtgraph.parametertree</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LivePlotWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pglive.sources.live_plot_widget</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Form implementation generated from reading ui file 'tec_qt.ui'
|
||||
#
|
||||
# Created by: PyQt6 UI code generator 6.4.2
|
||||
# Created by: PyQt6 UI code generator 6.5.0
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@ -52,16 +52,16 @@ class Ui_MainWindow(object):
|
||||
self.ch0_tree.setHeaderHidden(True)
|
||||
self.ch0_tree.setObjectName("ch0_tree")
|
||||
self.graphs_layout.addWidget(self.ch0_tree, 0, 0, 1, 1)
|
||||
self.ch1_t_graph = PlotWidget(parent=self.graph_group)
|
||||
self.ch1_t_graph = LivePlotWidget(parent=self.graph_group)
|
||||
self.ch1_t_graph.setObjectName("ch1_t_graph")
|
||||
self.graphs_layout.addWidget(self.ch1_t_graph, 1, 1, 1, 1)
|
||||
self.ch0_t_graph = PlotWidget(parent=self.graph_group)
|
||||
self.ch0_t_graph = LivePlotWidget(parent=self.graph_group)
|
||||
self.ch0_t_graph.setObjectName("ch0_t_graph")
|
||||
self.graphs_layout.addWidget(self.ch0_t_graph, 0, 1, 1, 1)
|
||||
self.ch0_i_graph = PlotWidget(parent=self.graph_group)
|
||||
self.ch0_i_graph = LivePlotWidget(parent=self.graph_group)
|
||||
self.ch0_i_graph.setObjectName("ch0_i_graph")
|
||||
self.graphs_layout.addWidget(self.ch0_i_graph, 0, 2, 1, 1)
|
||||
self.ch1_i_graph = PlotWidget(parent=self.graph_group)
|
||||
self.ch1_i_graph = LivePlotWidget(parent=self.graph_group)
|
||||
self.ch1_i_graph.setObjectName("ch1_i_graph")
|
||||
self.graphs_layout.addWidget(self.ch1_i_graph, 1, 2, 1, 1)
|
||||
self.graphs_layout.setColumnMinimumWidth(0, 100)
|
||||
@ -306,7 +306,7 @@ class Ui_MainWindow(object):
|
||||
self.report_refresh_spin.setSuffix(_translate("MainWindow", " s"))
|
||||
self.report_box.setText(_translate("MainWindow", "Report"))
|
||||
self.report_apply_btn.setText(_translate("MainWindow", "Apply"))
|
||||
from pyqtgraph import PlotWidget
|
||||
from pglive.sources.live_plot_widget import LivePlotWidget
|
||||
from pyqtgraph.parametertree import ParameterTree
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user