diff --git a/pytec/pytec/aioclient.py b/pytec/pytec/aioclient.py index b67d15f..77b84a7 100644 --- a/pytec/pytec/aioclient.py +++ b/pytec/pytec/aioclient.py @@ -9,15 +9,51 @@ class Client: def __init__(self): self._reader = None self._writer = None + self._connecting_task = None self._command_lock = asyncio.Lock() async def connect(self, host='192.168.1.26', port=23, timeout=None): - self._reader, self._writer = await asyncio.open_connection(host, port) + """Connect to the TEC with host and port, throws TimeoutError if + unable to connect. Returns True if not cancelled with disconnect. + + Example:: + client = aioclient.Client() + connected = await client.connect() + if connected: + return + """ + self._connecting_task = asyncio.create_task(asyncio.open_connection(host, port)) + try: + self._reader, self._writer = await self._connecting_task + except asyncio.CancelledError: + return False + finally: + self._connecting_task = None + await self._check_zero_limits() + return True + + def is_connecting(self): + """Returns True if client is connecting""" + return self._connecting_task is not None + + def is_connected(self): + """Returns True if client is connected""" + return self._writer is not None async def disconnect(self): + """Disconnect the client if connected, cancel connection if connecting""" + if self._connecting_task is not None: + self._connecting_task.cancel() + + if self._writer is None: + return + + # Reader needn't be closed self._writer.close() await self._writer.wait_closed() + self._reader = None + self._writer = None async def _check_zero_limits(self): pwm_report = await self.get_pwm() @@ -145,11 +181,11 @@ class Client: """Set configuration parameters Examples:: - tec.set_param("pwm", 0, "max_v", 2.0) - tec.set_param("pid", 1, "output_max", 2.5) - tec.set_param("s-h", 0, "t0", 20.0) - tec.set_param("center", 0, "vref") - tec.set_param("postfilter", 1, 21) + await tec.set_param("pwm", 0, "max_v", 2.0) + await tec.set_param("pid", 1, "output_max", 2.5) + await tec.set_param("s-h", 0, "t0", 20.0) + await tec.set_param("center", 0, "vref") + await tec.set_param("postfilter", 1, 21) See the firmware's README.md for a full list. """ diff --git a/pytec/tec_qt.py b/pytec/tec_qt.py index ee41e4c..c786e3a 100644 --- a/pytec/tec_qt.py +++ b/pytec/tec_qt.py @@ -1,4 +1,4 @@ -from PyQt6 import QtWidgets, uic +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 @@ -14,15 +14,6 @@ from qasync import asyncSlot, asyncClose # 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 - -client_watcher = None -client_watcher_task = None -app: QtWidgets.QApplication = None - def get_argparser(): parser = argparse.ArgumentParser(description="ARTIQ master") @@ -38,130 +29,184 @@ def get_argparser(): class ClientWatcher(QObject): - fan_update = pyqtSignal(object) - pwm_update = pyqtSignal(object) - report_update = pyqtSignal(object) - pid_update = pyqtSignal(object) + fan_update = pyqtSignal(dict) + pwm_update = pyqtSignal(list) + report_update = pyqtSignal(list) + pid_update = pyqtSignal(list) - def __init__(self, parent, update_s): + def __init__(self, parent, client, update_s): self.update_s = update_s - self.running = True + self.client = client + self.watch_task = None super().__init__(parent) async def run(self): loop = asyncio.get_running_loop() - while self.running: + while True: time = loop.time() await self.update_params() await asyncio.sleep(self.update_s - (loop.time() - time)) async def update_params(self): - self.fan_update.emit(await tec_client.fan()) + self.fan_update.emit(await self.client.fan()) + + def start_watching(self): + self.watch_task = asyncio.create_task(self.run()) + + def is_watching(self): + return self.watch_task is not None @pyqtSlot() def stop_watching(self): - self.running = False + if self.watch_task is not None: + self.watch_task.cancel() + self.watch_task = None - @pyqtSlot() - def set_update_s(self): - self.update_s = ui.report_refresh_spin.value() + @pyqtSlot(float) + def set_update_s(self, update_s): + self.update_s = update_s -def on_connection_changed(result): - global client_watcher, client_watcher_task - ui.graph_group.setEnabled(result) - ui.hw_rev_lbl.setEnabled(result) - ui.fan_group.setEnabled(result) - ui.report_group.setEnabled(result) +class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): + def __init__(self, args): + super().__init__() - 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("") - if client_watcher: - client_watcher.stop_watching() - client_watcher = None - client_watcher_task = None + self.setupUi(self) + self._set_up_context_menu() -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") + self.fan_power_slider.valueChanged.connect(self.fan_set) + self.fan_auto_box.stateChanged.connect(self.fan_auto_set) + self.fan_pwm_recommended = False -def fan_update(fan_settings): - logging.debug(fan_settings) - if fan_settings is None: - return - with QSignalBlocker(ui.fan_power_slider): - 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): - ui.fan_auto_box.setChecked(fan_settings["auto_mode"]) + 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.report_apply_btn.clicked.connect( + lambda: self.client_watcher.set_update_s(self.report_refresh_spin.value()) + ) + if args.connect: + if args.IP: + self.ip_set_line.setText(args.IP) + if args.PORT: + self.port_set_spin.setValue(int(args.PORT)) + self.connect_btn.click() -@asyncSlot() -async def fan_set(_): - global tec_client - if tec_client is None or ui.fan_auto_box.isChecked(): - return - await tec_client.set_param("fan", ui.fan_power_slider.value()) + def _set_up_context_menu(self): + self.menu = QtWidgets.QMenu() + self.menu.setTitle('Thermostat settings') + port = QtWidgets.QWidgetAction(self.menu) + port.setDefaultWidget(self.port_set_spin) + self.menu.addAction(port) + self.menu.port = port -@asyncSlot() -async def fan_auto_set(enabled): - global tec_client - if tec_client is None: - return - ui.fan_power_slider.setEnabled(not enabled) - if enabled: - await tec_client.set_param("fan", "auto") - else: - await tec_client.set_param("fan", ui.fan_power_slider.value()) + fan = QtWidgets.QWidgetAction(self.menu) + fan.setDefaultWidget(self.fan_group) + self.menu.addAction(fan) + self.menu.fan = fan + self.thermostat_settings.setMenu(self.menu) -@asyncSlot() -async def connect(_): - global tec_client, client_watcher, client_watcher_task - ip, port = ui.ip_set_line.text(), ui.port_set_spin.value() - try: - if tec_client: - await tec_client.disconnect() - tec_client = None - on_connection_changed(False) + async def _on_connection_changed(self, result): + self.graph_group.setEnabled(result) + self.fan_group.setEnabled(result) + self.report_group.setEnabled(result) + + self.ip_set_line.setEnabled(not result) + self.port_set_spin.setEnabled(not result) + self.connect_btn.setText("Disconnect" if result else "Connect") + if result: + self.client_watcher.start_watching() + self._status(await self.tec_client.hw_rev()) + self.fan_update(await self.tec_client.fan()) else: - ui.status_lbl.setText("Connecting...") - tec_client = Client() - await tec_client.connect(host=ip, port=port, timeout=30) - on_connection_changed(True) - hw_rev(await tec_client.hw_rev()) - # fan_update(await tec_client.fan()) - if client_watcher is None: - client_watcher = ClientWatcher(ui.main_widget, ui.report_refresh_spin.value()) - client_watcher.fan_update.connect(fan_update) - ui.report_apply_btn.clicked.connect( - lambda: client_watcher.set_update_s(ui.report_refresh_spin.value()) - ) - app.aboutToQuit.connect(client_watcher.stop_watching) - client_watcher_task = asyncio.create_task(client_watcher.run()) - except Exception as e: - logging.error(f"Failed communicating to the {ip}:{port}: {e}") - on_connection_changed(False) + self.status_lbl.setText("Disconnected") + self.fan_pwm_warning.setPixmap(QtGui.QPixmap()) + self.fan_pwm_warning.setToolTip("") + self.client_watcher.stop_watching() + + def _set_fan_pwm_warning(self): + if self.fan_power_slider.value() != 100: + pixmapi = getattr(QtWidgets.QStyle.StandardPixmap, "SP_MessageBoxWarning") + icon = self.style().standardIcon(pixmapi) + self.fan_pwm_warning.setPixmap(icon.pixmap(16, 16)) + self.fan_pwm_warning.setToolTip("Throttling the fan (not recommended on this hardware rev)") + else: + self.fan_pwm_warning.setPixmap(QtGui.QPixmap()) + self.fan_pwm_warning.setToolTip("") + + def _status(self, hw_rev_d: dict): + logging.debug(hw_rev_d) + self.status_lbl.setText(f"Connected to Thermostat v{hw_rev_d['rev']['major']}.{hw_rev_d['rev']['minor']}") + self.fan_group.setEnabled(hw_rev_d["settings"]["fan_available"]) + self.fan_pwm_recommended = hw_rev_d["settings"]["fan_pwm_recommended"] + + @pyqtSlot(dict) + def fan_update(self, fan_settings: dict): + logging.debug(fan_settings) + if fan_settings is None: + return + with QSignalBlocker(self.fan_power_slider): + self.fan_power_slider.setValue(fan_settings["fan_pwm"] or 100) # 0 = PWM off = full strength + with QSignalBlocker(self.fan_auto_box): + self.fan_auto_box.setChecked(fan_settings["auto_mode"]) + if not self.fan_pwm_recommended: + self._set_fan_pwm_warning() + + @asyncSlot(int) + async def fan_set(self, value): + if not self.tec_client.is_connected(): + return + if self.fan_auto_box.isChecked(): + with QSignalBlocker(self.fan_auto_box): + self.fan_auto_box.setChecked(False) + await self.tec_client.set_param("fan", value) + if not self.fan_pwm_recommended: + self._set_fan_pwm_warning() + + @asyncSlot(int) + async def fan_auto_set(self, enabled): + if not self.tec_client.is_connected(): + return + if enabled: + await self.tec_client.set_param("fan", "auto") + self.fan_update(await self.tec_client.fan()) + else: + await self.tec_client.set_param("fan", self.fan_power_slider.value()) + + @asyncClose + async def closeEvent(self, event): + self.client_watcher.stop_watching() + await self.tec_client.disconnect() + + @asyncSlot() + async def on_connect_btn_clicked(self): + ip, port = self.ip_set_line.text(), self.port_set_spin.value() + try: + if not (self.tec_client.is_connecting() or self.tec_client.is_connected()): + self.status_lbl.setText("Connecting...") + self.connect_btn.setText("Stop") + self.ip_set_line.setEnabled(False) + self.port_set_spin.setEnabled(False) + + connected = await self.tec_client.connect(host=ip, port=port, timeout=30) + if not connected: + return + await self._on_connection_changed(True) + else: + await self._on_connection_changed(False) + await self.tec_client.disconnect() + + except (OSError, TimeoutError) as e: + logging.error(f"Failed communicating to {ip}:{port}: {e}") + await self._on_connection_changed(False) + await self.tec_client.disconnect() async def coro_main(): - global ui, app - args = get_argparser().parse_args() if args.logLevel: logging.basicConfig(level=getattr(logging, args.logLevel)) @@ -171,22 +216,7 @@ async def coro_main(): app = QtWidgets.QApplication.instance() app.aboutToQuit.connect(app_quit_event.set) - main_window = QtWidgets.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(main_window) - # ui = uic.loadUi('tec_qt.ui', main_window) - - ui.connect_btn.clicked.connect(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 = MainWindow(args) main_window.show() await app_quit_event.wait() diff --git a/pytec/tec_qt.ui b/pytec/tec_qt.ui index 072befa..4916d43 100644 --- a/pytec/tec_qt.ui +++ b/pytec/tec_qt.ui @@ -273,7 +273,7 @@ - 120 + 240 0 @@ -294,6 +294,16 @@ + + + + + + + QToolButton::InstantPopup + + + @@ -307,6 +317,165 @@ + + + + false + + + + 40 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 9 + + + + + + 16 + 0 + + + + + + + + + + + + 0 + 0 + + + + + 40 + 0 + + + + + 40 + 16777215 + + + + + 40 + 0 + + + + Adjust the fan + + + Fan: + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + 200 + 0 + + + + 1 + + + 100 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 70 + 0 + + + + + 70 + 16777215 + + + + Auto + + + + + + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + @@ -341,7 +510,7 @@ 0 - + 6 @@ -351,6 +520,16 @@ 0 + + + + Poll every: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -377,6 +556,9 @@ 0 + + s + 1 @@ -455,193 +637,6 @@ - - - - - 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 - - - diff --git a/pytec/ui_tec_qt.py b/pytec/ui_tec_qt.py index 9e502d5..138cbfe 100644 --- a/pytec/ui_tec_qt.py +++ b/pytec/ui_tec_qt.py @@ -131,11 +131,16 @@ class Ui_MainWindow(object): 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.setMinimumSize(QtCore.QSize(240, 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.thermostat_settings = QtWidgets.QToolButton(parent=self.bottom_settings_group) + self.thermostat_settings.setText("⚙") + self.thermostat_settings.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup) + self.thermostat_settings.setObjectName("thermostat_settings") + self.settings_layout.addWidget(self.thermostat_settings) self.line_0 = QtWidgets.QFrame(parent=self.bottom_settings_group) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) sizePolicy.setHorizontalStretch(0) @@ -146,6 +151,69 @@ class Ui_MainWindow(object): self.line_0.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) self.line_0.setObjectName("line_0") self.settings_layout.addWidget(self.line_0) + 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_pwm_warning = QtWidgets.QLabel(parent=self.fan_group) + self.fan_pwm_warning.setMinimumSize(QtCore.QSize(16, 0)) + self.fan_pwm_warning.setText("") + self.fan_pwm_warning.setObjectName("fan_pwm_warning") + self.gan_layout.addWidget(self.fan_pwm_warning) + 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.setMinimum(1) + 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_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.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) @@ -164,6 +232,10 @@ class Ui_MainWindow(object): self.report_layout.setContentsMargins(0, -1, -1, -1) self.report_layout.setSpacing(6) self.report_layout.setObjectName("report_layout") + self.report_lbl = QtWidgets.QLabel(parent=self.report_group) + self.report_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter) + self.report_lbl.setObjectName("report_lbl") + self.report_layout.addWidget(self.report_lbl) self.report_refresh_spin = QtWidgets.QDoubleSpinBox(parent=self.report_group) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(0) @@ -201,90 +273,11 @@ class Ui_MainWindow(object): 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.report_layout.setStretch(3, 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) @@ -304,11 +297,13 @@ class Ui_MainWindow(object): 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.setToolTip(_translate("MainWindow", "Adjust the fan")) 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")) + self.report_lbl.setText(_translate("MainWindow", "Poll every: ")) + 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 pyqtgraph.parametertree import ParameterTree