From 3a72ddc89992047a1aba590df41bfa9c09c066b3 Mon Sep 17 00:00:00 2001 From: Egor Savkin Date: Fri, 19 May 2023 11:23:39 +0800 Subject: [PATCH] Create basic GUI, that would connect and control thermostat's fan Signed-off-by: Egor Savkin --- flake.nix | 2 +- pytec/pytec/client.py | 12 + pytec/tec_qt.py | 183 ++++++++++++ pytec/tec_qt.ui | 671 ++++++++++++++++++++++++++++++++++++++++++ pytec/ui_tec_qt.py | 323 ++++++++++++++++++++ 5 files changed, 1190 insertions(+), 1 deletion(-) create mode 100644 pytec/tec_qt.py create mode 100644 pytec/tec_qt.ui create mode 100644 pytec/ui_tec_qt.py diff --git a/flake.nix b/flake.nix index 203351d..77dbec9 100644 --- a/flake.nix +++ b/flake.nix @@ -62,7 +62,7 @@ buildInputs = with pkgs; [ rust openocd dfu-util ] ++ (with python3Packages; [ - numpy matplotlib + numpy matplotlib pyqtgraph setuptools pyqt6 ]); }; defaultPackage.x86_64-linux = thermostat; diff --git a/pytec/pytec/client.py b/pytec/pytec/client.py index 062d8dc..32b05f5 100644 --- a/pytec/pytec/client.py +++ b/pytec/pytec/client.py @@ -11,6 +11,10 @@ class Client: self._lines = [""] self._check_zero_limits() + def disconnect(self): + self._socket.shutdown(socket.SHUT_RDWR) + self._socket.close() + def _check_zero_limits(self): pwm_report = self.get_pwm() for pwm_channel in pwm_report: @@ -167,3 +171,11 @@ class Client: def load_config(self): """Load current configuration from EEPROM""" self._command("load") + + def hw_rev(self): + """Get Thermostat hardware revision""" + return self._command("hwrev") + + def fan(self): + """Get Thermostat current fan settings""" + return self._command("fan") diff --git a/pytec/tec_qt.py b/pytec/tec_qt.py new file mode 100644 index 0000000..8ee039a --- /dev/null +++ b/pytec/tec_qt.py @@ -0,0 +1,183 @@ +from PyQt6 import QtWidgets, uic +from PyQt6.QtCore import QThread, QThreadPool, pyqtSignal, QRunnable, QObject, QSignalBlocker, pyqtSlot +from pyqtgraph import PlotWidget +from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType +import pyqtgraph as pg +import sys +import argparse +import logging +from pytec.client import Client + +# pyuic6 -x tec_qt.ui -o ui_tec_qt.py +from ui_tec_qt import Ui_MainWindow + +tec_client: Client = None + +# ui = None +ui: Ui_MainWindow = None + +thread_pool = QThreadPool.globalInstance() +connection_watcher = None + + +def get_argparser(): + parser = argparse.ArgumentParser(description="ARTIQ master") + + parser.add_argument("--connect", default=None, action="store_true", + help="Automatically connect to the specified Thermostat in IP:port format") + parser.add_argument('IP', metavar="ip", default=None, nargs='?') + parser.add_argument('PORT', metavar="port", default=None, nargs='?') + parser.add_argument("-l", "--log", dest="logLevel", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], + help="Set the logging level") + + return parser + + +class WatchConnectTask(QThread): + connected = pyqtSignal(bool) + hw_rev = pyqtSignal(dict) + connecting = pyqtSignal() + fan_update = pyqtSignal(object) + + def __init__(self, ip, port, parent): + self.ip = ip + self.port = port + super().__init__(parent) + + def run(self): + global tec_client + try: + if tec_client: + tec_client.disconnect() + tec_client = None + self.connected.emit(False) + else: + self.connecting.emit() + tec_client = Client(host=self.ip, port=self.port, timeout=30) + self.connected.emit(True) + self.hw_rev.emit(tec_client.hw_rev()) + self.fan_update.emit(tec_client.fan()) + except Exception as e: + logging.error(f"Failed communicating to the {self.ip}:{self.port}: {e}") + self.connected.emit(False) + + @pyqtSlot() + def client_disconnected(self): + global tec_client + if tec_client: + tec_client.disconnect() + tec_client = None + self.connected.emit(False) + + +class ClientTask(QRunnable): + def __init__(self, func, *args, **kwargs): + self.func = func + self.args = args + self.kwargs = kwargs + super().__init__() + + def run(self) -> None: + try: + self.func(*self.args, **self.kwargs) + except (TimeoutError, OSError): + logging.warning("Client connection error, disconnecting", exc_info=True) + if connection_watcher: + connection_watcher.client_disconnected() + + +def connected(result): + ui.graph_group.setEnabled(result) + ui.hw_rev_lbl.setEnabled(result) + ui.fan_group.setEnabled(result) + ui.report_group.setEnabled(result) + + ui.ip_set_line.setEnabled(not result) + ui.port_set_spin.setEnabled(not result) + ui.status_lbl.setText("Connected" if result else "Disconnected") + ui.connect_btn.setText("Disconnect" if result else "Connect") + if not result: + ui.hw_rev_lbl.setText("Thermostat vX.Y") + ui.fan_group.setStyleSheet("") + + +def hw_rev(hw_rev_d: dict): + logging.debug(hw_rev_d) + ui.hw_rev_lbl.setText(f"Thermostat v{hw_rev_d['rev']['major']}.{hw_rev_d['rev']['major']}") + ui.fan_group.setEnabled(hw_rev_d["settings"]["fan_available"]) + if hw_rev_d["settings"]["fan_pwm_recommended"]: + ui.fan_group.setStyleSheet("") + ui.fan_group.setToolTip("") + else: + ui.fan_group.setStyleSheet("background-color: yellow") + ui.fan_group.setToolTip("Changing the fan settings of not recommended") + + +def fan_update(fan_settings): + logging.debug(fan_settings) + if fan_settings is None: + return + with QSignalBlocker(ui.fan_power_slider) as _: + ui.fan_power_slider.setValue(fan_settings["fan_pwm"]) + ui.fan_power_slider.setEnabled(not fan_settings["auto_mode"]) + with QSignalBlocker(ui.fan_auto_box) as _: + ui.fan_auto_box.setChecked(fan_settings["auto_mode"]) + + +def fan_set(): + global tec_client + if tec_client is None or ui.fan_auto_box.isChecked(): + return + thread_pool.start(ClientTask(lambda: tec_client.set_param("fan", ui.fan_power_slider.value()))) + + +def fan_auto_set(enabled): + global tec_client + if tec_client is None: + return + ui.fan_power_slider.setEnabled(not enabled) + if enabled: + thread_pool.start(ClientTask(lambda: tec_client.set_param("fan", "auto"))) + else: + thread_pool.start(ClientTask(lambda: tec_client.set_param("fan", ui.fan_power_slider.value()))) + + +def connect(): + global connection_watcher + connection_watcher = WatchConnectTask(ui.ip_set_line.text(), ui.port_set_spin.value(), ui.main_widget) + connection_watcher.connected.connect(connected) + connection_watcher.connecting.connect(lambda: ui.status_lbl.setText("Connecting...")) + connection_watcher.hw_rev.connect(hw_rev) + connection_watcher.fan_update.connect(fan_update) + connection_watcher.start() + + +def main(): + global ui + args = get_argparser().parse_args() + if args.logLevel: + logging.basicConfig(level=getattr(logging, args.logLevel)) + + app = QtWidgets.QApplication(sys.argv) + main_window = QtWidgets.QMainWindow() + #ui = Ui_MainWindow() + #ui.setupUi(main_window) + ui = uic.loadUi('tec_qt.ui', main_window) + + ui.connect_btn.clicked.connect(lambda _checked: connect()) + ui.fan_power_slider.valueChanged.connect(fan_set) + ui.fan_auto_box.stateChanged.connect(fan_auto_set) + + if args.connect: + if args.IP: + ui.ip_set_line.setText(args.IP) + if args.PORT: + ui.port_set_spin.setValue(int(args.PORT)) + ui.connect_btn.click() + + main_window.show() + sys.exit(app.exec()) + + +if __name__ == '__main__': + main() diff --git a/pytec/tec_qt.ui b/pytec/tec_qt.ui new file mode 100644 index 0000000..58f5849 --- /dev/null +++ b/pytec/tec_qt.ui @@ -0,0 +1,671 @@ + + + MainWindow + + + + 0 + 0 + 1280 + 720 + + + + + 1280 + 720 + + + + + 3840 + 2160 + + + + Control TEC + + + + + 1 + 1 + + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + 0 + + + + + false + + + + 1 + 1 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QLayout::SetDefaultConstraint + + + 3 + + + 3 + + + 3 + + + 3 + + + 2 + + + + + + + + + + + Channel 1 Temperature + + + + + + + Channel 0 Temperature + + + + + + + Channel 0 Current + + + + + + + Channel 1 Current + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 16777215 + 40 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + + 160 + 16777215 + + + + 192.168.1.26 + + + 15 + + + IP:port for the Thermostat + + + true + + + + + + + + 0 + 0 + + + + + 70 + 0 + + + + + 70 + 16777215 + + + + 65535 + + + 23 + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + 100 + 0 + + + + Connect + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 120 + 16777215 + + + + + 120 + 50 + + + + Disconnected + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + + + + + false + + + + 0 + 0 + + + + + 40 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + QLayout::SetDefaultConstraint + + + 0 + + + + + + 0 + 0 + + + + + 70 + 0 + + + + + 70 + 16777215 + + + + + 70 + 0 + + + + 1 + + + 0.100000000000000 + + + 0.100000000000000 + + + QAbstractSpinBox::AdaptiveDecimalStepType + + + 1.000000000000000 + + + + + + + + 0 + 0 + + + + + 80 + 16777215 + + + + + 80 + 0 + + + + Report + + + + + + + + 0 + 0 + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + + 80 + 0 + + + + Apply + + + + + + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + + + + + false + + + + 40 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 9 + + + + + + 0 + 0 + + + + + 40 + 0 + + + + + 40 + 16777215 + + + + + 40 + 0 + + + + Fan: + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + 200 + 0 + + + + 100 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 70 + 0 + + + + + 70 + 16777215 + + + + Auto + + + + + + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + + + + + false + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + + 150 + 0 + + + + Thermostat vX.Y + + + + + + + + + + + + + + + + PlotWidget + QWidget +
pyqtgraph
+ 1 +
+ + ParameterTree + QWidget +
pyqtgraph.parametertree
+ 1 +
+
+ + +
diff --git a/pytec/ui_tec_qt.py b/pytec/ui_tec_qt.py new file mode 100644 index 0000000..b26d878 --- /dev/null +++ b/pytec/ui_tec_qt.py @@ -0,0 +1,323 @@ +# Form implementation generated from reading ui file 'tec_qt.ui' +# +# Created by: PyQt6 UI code generator 6.4.2 +# +# 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. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(1280, 720) + MainWindow.setMinimumSize(QtCore.QSize(1280, 720)) + MainWindow.setMaximumSize(QtCore.QSize(3840, 2160)) + self.main_widget = QtWidgets.QWidget(parent=MainWindow) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(1) + sizePolicy.setVerticalStretch(1) + sizePolicy.setHeightForWidth(self.main_widget.sizePolicy().hasHeightForWidth()) + self.main_widget.setSizePolicy(sizePolicy) + self.main_widget.setObjectName("main_widget") + self.gridLayout_2 = QtWidgets.QGridLayout(self.main_widget) + self.gridLayout_2.setContentsMargins(3, 3, 3, 3) + self.gridLayout_2.setSpacing(3) + self.gridLayout_2.setObjectName("gridLayout_2") + self.main_layout = QtWidgets.QVBoxLayout() + self.main_layout.setSpacing(0) + self.main_layout.setObjectName("main_layout") + self.graph_group = QtWidgets.QFrame(parent=self.main_widget) + self.graph_group.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(1) + sizePolicy.setVerticalStretch(1) + sizePolicy.setHeightForWidth(self.graph_group.sizePolicy().hasHeightForWidth()) + self.graph_group.setSizePolicy(sizePolicy) + self.graph_group.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.graph_group.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.graph_group.setObjectName("graph_group") + self.graphs_layout = QtWidgets.QGridLayout(self.graph_group) + self.graphs_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint) + self.graphs_layout.setContentsMargins(3, 3, 3, 3) + self.graphs_layout.setSpacing(2) + self.graphs_layout.setObjectName("graphs_layout") + self.ch1_tree = ParameterTree(parent=self.graph_group) + self.ch1_tree.setObjectName("ch1_tree") + self.graphs_layout.addWidget(self.ch1_tree, 1, 0, 1, 1) + self.ch0_tree = ParameterTree(parent=self.graph_group) + 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.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.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.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.setObjectName("ch1_i_graph") + self.graphs_layout.addWidget(self.ch1_i_graph, 1, 2, 1, 1) + self.graphs_layout.setColumnMinimumWidth(0, 100) + self.graphs_layout.setColumnMinimumWidth(1, 100) + self.graphs_layout.setColumnMinimumWidth(2, 100) + self.graphs_layout.setRowMinimumHeight(0, 100) + self.graphs_layout.setRowMinimumHeight(1, 100) + self.graphs_layout.setColumnStretch(0, 1) + self.graphs_layout.setColumnStretch(1, 1) + self.graphs_layout.setColumnStretch(2, 1) + self.graphs_layout.setRowStretch(0, 1) + self.graphs_layout.setRowStretch(1, 1) + self.main_layout.addWidget(self.graph_group) + self.bottom_settings_group = QtWidgets.QFrame(parent=self.main_widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.bottom_settings_group.sizePolicy().hasHeightForWidth()) + self.bottom_settings_group.setSizePolicy(sizePolicy) + self.bottom_settings_group.setMinimumSize(QtCore.QSize(0, 40)) + self.bottom_settings_group.setMaximumSize(QtCore.QSize(16777215, 40)) + self.bottom_settings_group.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel) + self.bottom_settings_group.setFrameShadow(QtWidgets.QFrame.Shadow.Raised) + self.bottom_settings_group.setObjectName("bottom_settings_group") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.bottom_settings_group) + self.horizontalLayout_2.setContentsMargins(3, 3, 3, 3) + self.horizontalLayout_2.setSpacing(3) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.settings_layout = QtWidgets.QHBoxLayout() + self.settings_layout.setObjectName("settings_layout") + self.ip_set_line = QtWidgets.QLineEdit(parent=self.bottom_settings_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.ip_set_line.sizePolicy().hasHeightForWidth()) + self.ip_set_line.setSizePolicy(sizePolicy) + self.ip_set_line.setMinimumSize(QtCore.QSize(160, 0)) + self.ip_set_line.setMaximumSize(QtCore.QSize(160, 16777215)) + self.ip_set_line.setMaxLength(15) + self.ip_set_line.setClearButtonEnabled(True) + self.ip_set_line.setObjectName("ip_set_line") + self.settings_layout.addWidget(self.ip_set_line) + self.port_set_spin = QtWidgets.QSpinBox(parent=self.bottom_settings_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.port_set_spin.sizePolicy().hasHeightForWidth()) + self.port_set_spin.setSizePolicy(sizePolicy) + self.port_set_spin.setMinimumSize(QtCore.QSize(70, 0)) + self.port_set_spin.setMaximumSize(QtCore.QSize(70, 16777215)) + self.port_set_spin.setMaximum(65535) + self.port_set_spin.setProperty("value", 23) + self.port_set_spin.setObjectName("port_set_spin") + self.settings_layout.addWidget(self.port_set_spin) + self.connect_btn = QtWidgets.QPushButton(parent=self.bottom_settings_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.connect_btn.sizePolicy().hasHeightForWidth()) + self.connect_btn.setSizePolicy(sizePolicy) + self.connect_btn.setMinimumSize(QtCore.QSize(100, 0)) + self.connect_btn.setMaximumSize(QtCore.QSize(100, 16777215)) + self.connect_btn.setBaseSize(QtCore.QSize(100, 0)) + self.connect_btn.setObjectName("connect_btn") + self.settings_layout.addWidget(self.connect_btn) + self.status_lbl = QtWidgets.QLabel(parent=self.bottom_settings_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.status_lbl.sizePolicy().hasHeightForWidth()) + self.status_lbl.setSizePolicy(sizePolicy) + self.status_lbl.setMinimumSize(QtCore.QSize(120, 0)) + self.status_lbl.setMaximumSize(QtCore.QSize(120, 16777215)) + self.status_lbl.setBaseSize(QtCore.QSize(120, 50)) + self.status_lbl.setObjectName("status_lbl") + self.settings_layout.addWidget(self.status_lbl) + self.line_0 = QtWidgets.QFrame(parent=self.bottom_settings_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.line_0.sizePolicy().hasHeightForWidth()) + self.line_0.setSizePolicy(sizePolicy) + self.line_0.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.line_0.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_0.setObjectName("line_0") + self.settings_layout.addWidget(self.line_0) + self.report_group = QtWidgets.QWidget(parent=self.bottom_settings_group) + self.report_group.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.report_group.sizePolicy().hasHeightForWidth()) + self.report_group.setSizePolicy(sizePolicy) + self.report_group.setMinimumSize(QtCore.QSize(40, 0)) + self.report_group.setObjectName("report_group") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.report_group) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setSpacing(0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.report_layout = QtWidgets.QHBoxLayout() + self.report_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint) + self.report_layout.setContentsMargins(0, -1, -1, -1) + self.report_layout.setSpacing(6) + self.report_layout.setObjectName("report_layout") + self.report_refresh_spin = QtWidgets.QDoubleSpinBox(parent=self.report_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.report_refresh_spin.sizePolicy().hasHeightForWidth()) + self.report_refresh_spin.setSizePolicy(sizePolicy) + self.report_refresh_spin.setMinimumSize(QtCore.QSize(70, 0)) + self.report_refresh_spin.setMaximumSize(QtCore.QSize(70, 16777215)) + self.report_refresh_spin.setBaseSize(QtCore.QSize(70, 0)) + self.report_refresh_spin.setDecimals(1) + self.report_refresh_spin.setMinimum(0.1) + self.report_refresh_spin.setSingleStep(0.1) + self.report_refresh_spin.setStepType(QtWidgets.QAbstractSpinBox.StepType.AdaptiveDecimalStepType) + self.report_refresh_spin.setProperty("value", 1.0) + self.report_refresh_spin.setObjectName("report_refresh_spin") + self.report_layout.addWidget(self.report_refresh_spin) + self.report_box = QtWidgets.QCheckBox(parent=self.report_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.report_box.sizePolicy().hasHeightForWidth()) + self.report_box.setSizePolicy(sizePolicy) + self.report_box.setMaximumSize(QtCore.QSize(80, 16777215)) + self.report_box.setBaseSize(QtCore.QSize(80, 0)) + self.report_box.setObjectName("report_box") + self.report_layout.addWidget(self.report_box) + self.report_apply_btn = QtWidgets.QPushButton(parent=self.report_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.report_apply_btn.sizePolicy().hasHeightForWidth()) + self.report_apply_btn.setSizePolicy(sizePolicy) + self.report_apply_btn.setMinimumSize(QtCore.QSize(80, 0)) + self.report_apply_btn.setMaximumSize(QtCore.QSize(80, 16777215)) + self.report_apply_btn.setBaseSize(QtCore.QSize(80, 0)) + self.report_apply_btn.setObjectName("report_apply_btn") + self.report_layout.addWidget(self.report_apply_btn) + self.report_layout.setStretch(0, 1) + self.report_layout.setStretch(1, 1) + self.report_layout.setStretch(2, 1) + self.horizontalLayout_4.addLayout(self.report_layout) + self.settings_layout.addWidget(self.report_group) + self.line_1 = QtWidgets.QFrame(parent=self.bottom_settings_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.line_1.sizePolicy().hasHeightForWidth()) + self.line_1.setSizePolicy(sizePolicy) + self.line_1.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.line_1.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_1.setObjectName("line_1") + self.settings_layout.addWidget(self.line_1) + self.fan_group = QtWidgets.QWidget(parent=self.bottom_settings_group) + self.fan_group.setEnabled(False) + self.fan_group.setMinimumSize(QtCore.QSize(40, 0)) + self.fan_group.setObjectName("fan_group") + self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.fan_group) + self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_6.setSpacing(0) + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.gan_layout = QtWidgets.QHBoxLayout() + self.gan_layout.setSpacing(9) + self.gan_layout.setObjectName("gan_layout") + self.fan_lbl = QtWidgets.QLabel(parent=self.fan_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fan_lbl.sizePolicy().hasHeightForWidth()) + self.fan_lbl.setSizePolicy(sizePolicy) + self.fan_lbl.setMinimumSize(QtCore.QSize(40, 0)) + self.fan_lbl.setMaximumSize(QtCore.QSize(40, 16777215)) + self.fan_lbl.setBaseSize(QtCore.QSize(40, 0)) + self.fan_lbl.setObjectName("fan_lbl") + self.gan_layout.addWidget(self.fan_lbl) + self.fan_power_slider = QtWidgets.QSlider(parent=self.fan_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fan_power_slider.sizePolicy().hasHeightForWidth()) + self.fan_power_slider.setSizePolicy(sizePolicy) + self.fan_power_slider.setMinimumSize(QtCore.QSize(200, 0)) + self.fan_power_slider.setMaximumSize(QtCore.QSize(200, 16777215)) + self.fan_power_slider.setBaseSize(QtCore.QSize(200, 0)) + self.fan_power_slider.setMaximum(100) + self.fan_power_slider.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.fan_power_slider.setObjectName("fan_power_slider") + self.gan_layout.addWidget(self.fan_power_slider) + self.fan_auto_box = QtWidgets.QCheckBox(parent=self.fan_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fan_auto_box.sizePolicy().hasHeightForWidth()) + self.fan_auto_box.setSizePolicy(sizePolicy) + self.fan_auto_box.setMinimumSize(QtCore.QSize(70, 0)) + self.fan_auto_box.setMaximumSize(QtCore.QSize(70, 16777215)) + self.fan_auto_box.setObjectName("fan_auto_box") + self.gan_layout.addWidget(self.fan_auto_box) + self.horizontalLayout_6.addLayout(self.gan_layout) + self.settings_layout.addWidget(self.fan_group) + self.line_3 = QtWidgets.QFrame(parent=self.bottom_settings_group) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.line_3.sizePolicy().hasHeightForWidth()) + self.line_3.setSizePolicy(sizePolicy) + self.line_3.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.line_3.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_3.setObjectName("line_3") + self.settings_layout.addWidget(self.line_3) + self.hw_rev_lbl = QtWidgets.QLabel(parent=self.bottom_settings_group) + self.hw_rev_lbl.setEnabled(False) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.hw_rev_lbl.sizePolicy().hasHeightForWidth()) + self.hw_rev_lbl.setSizePolicy(sizePolicy) + self.hw_rev_lbl.setMinimumSize(QtCore.QSize(150, 0)) + self.hw_rev_lbl.setMaximumSize(QtCore.QSize(150, 16777215)) + self.hw_rev_lbl.setBaseSize(QtCore.QSize(150, 0)) + self.hw_rev_lbl.setObjectName("hw_rev_lbl") + self.settings_layout.addWidget(self.hw_rev_lbl) + self.horizontalLayout_2.addLayout(self.settings_layout) + self.main_layout.addWidget(self.bottom_settings_group) + self.gridLayout_2.addLayout(self.main_layout, 0, 1, 1, 1) + MainWindow.setCentralWidget(self.main_widget) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Control TEC")) + self.ch1_t_graph.setTitle(_translate("MainWindow", "Channel 1 Temperature")) + self.ch0_t_graph.setTitle(_translate("MainWindow", "Channel 0 Temperature")) + self.ch0_i_graph.setTitle(_translate("MainWindow", "Channel 0 Current")) + self.ch1_i_graph.setTitle(_translate("MainWindow", "Channel 1 Current")) + self.ip_set_line.setText(_translate("MainWindow", "192.168.1.26")) + self.ip_set_line.setPlaceholderText(_translate("MainWindow", "IP:port for the Thermostat")) + self.connect_btn.setText(_translate("MainWindow", "Connect")) + self.status_lbl.setText(_translate("MainWindow", "Disconnected")) + self.report_box.setText(_translate("MainWindow", "Report")) + self.report_apply_btn.setText(_translate("MainWindow", "Apply")) + self.fan_lbl.setText(_translate("MainWindow", "Fan:")) + self.fan_auto_box.setText(_translate("MainWindow", "Auto")) + self.hw_rev_lbl.setText(_translate("MainWindow", "Thermostat vX.Y")) +from pyqtgraph import PlotWidget +from pyqtgraph.parametertree import ParameterTree + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + MainWindow = QtWidgets.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + sys.exit(app.exec())