Create basic GUI, that would connect and control thermostat's fan

Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
Egor Savkin 2023-05-19 11:23:39 +08:00
parent 869f45a5cf
commit 7821aacabc
5 changed files with 1291 additions and 75 deletions

View File

@ -11,6 +11,10 @@ class Client:
self._lines = [""] self._lines = [""]
self._check_zero_limits() self._check_zero_limits()
def disconnect(self):
self._socket.shutdown(socket.SHUT_RDWR)
self._socket.close()
def _check_zero_limits(self): def _check_zero_limits(self):
pwm_report = self.get_pwm() pwm_report = self.get_pwm()
for pwm_channel in pwm_report: for pwm_channel in pwm_report:
@ -167,3 +171,11 @@ class Client:
def load_config(self): def load_config(self):
"""Load current configuration from EEPROM""" """Load current configuration from EEPROM"""
self._command("load") 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")

View File

@ -10,7 +10,7 @@ from autotune import PIDAutotune, PIDAutotuneState
rec_len = 1000 rec_len = 1000
refresh_period = 20 refresh_period = 20
TECparams = [ [ TECparams = [[
{'tag': 'report', 'type': 'parent', 'children': [ {'tag': 'report', 'type': 'parent', 'children': [
{'tag': 'pid_engaged', 'type': 'bool', 'value': False}, {'tag': 'pid_engaged', 'type': 'bool', 'value': False},
]}, ]},
@ -40,25 +40,30 @@ TECparams = [ [
GUIparams = [[ GUIparams = [[
{'name': 'Disable Output', 'type': 'action', 'tip': 'Disable Output'}, {'name': 'Disable Output', 'type': 'action', 'tip': 'Disable Output'},
{'name': 'Constant Current', 'type': 'group', 'children': [ {'name': 'Constant Current', 'type': 'group', 'children': [
{'name': 'Set Current', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (-3, 3), 'siPrefix': True, 'suffix': 'A'}, {'name': 'Set Current', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (-3, 3), 'siPrefix': True,
]}, 'suffix': 'A'},
{'name': 'Temperature PID', 'type': 'bool', 'value': False, 'children': [
{'name': 'Set Temperature', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-273, 300), 'siPrefix': True, 'suffix': 'C'},
]},
{'name': 'Output Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Max Current', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 3), 'siPrefix': True, 'suffix': 'A'},
{'name': 'Max Voltage', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 5), 'siPrefix': True, 'suffix': 'V'},
]}, ]},
{'name': 'Thermistor Config', 'expanded': False, 'type': 'group', 'children': [ {'name': 'Temperature PID', 'type': 'bool', 'value': False, 'children': [
{'name': 'T0', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-100, 100), 'siPrefix': True, 'suffix': 'C'}, {'name': 'Set Temperature', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-273, 300), 'siPrefix': True,
'suffix': 'C'},
]},
{'name': 'Output Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Max Current', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 3), 'siPrefix': True,
'suffix': 'A'},
{'name': 'Max Voltage', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 5), 'siPrefix': True,
'suffix': 'V'},
]},
{'name': 'Thermistor Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'T0', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-100, 100), 'siPrefix': True,
'suffix': 'C'},
{'name': 'R0', 'type': 'float', 'value': 10000, 'step': 1, 'siPrefix': True, 'suffix': 'Ohm'}, {'name': 'R0', 'type': 'float', 'value': 10000, 'step': 1, 'siPrefix': True, 'suffix': 'Ohm'},
{'name': 'Beta', 'type': 'float', 'value': 3950, 'step': 1}, {'name': 'Beta', 'type': 'float', 'value': 3950, 'step': 1},
]}, ]},
{'name': 'PID Config', 'expanded': False, 'type': 'group', 'children': [ {'name': 'PID Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'kP', 'type': 'float', 'value': 0, 'step': 0.1}, {'name': 'kP', 'type': 'float', 'value': 0, 'step': 0.1},
{'name': 'kI', 'type': 'float', 'value': 0, 'step': 0.1}, {'name': 'kI', 'type': 'float', 'value': 0, 'step': 0.1},
{'name': 'kD', 'type': 'float', 'value': 0, 'step': 0.1}, {'name': 'kD', 'type': 'float', 'value': 0, 'step': 0.1},
{'name': 'PID Auto Tune', 'expanded': False, 'type': 'group', 'children': [ {'name': 'PID Auto Tune', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Target Temperature', 'type': 'float', 'value': 20, 'step': 0.1, 'siPrefix': True, 'suffix': 'C'}, {'name': 'Target Temperature', 'type': 'float', 'value': 20, 'step': 0.1, 'siPrefix': True, 'suffix': 'C'},
{'name': 'Test Current', 'type': 'float', 'value': 1, 'step': 0.1, 'siPrefix': True, 'suffix': 'A'}, {'name': 'Test Current', 'type': 'float', 'value': 1, 'step': 0.1, 'siPrefix': True, 'suffix': 'A'},
{'name': 'Temperature Swing', 'type': 'float', 'value': 1.5, 'step': 0.1, 'siPrefix': True, 'suffix': 'C'}, {'name': 'Temperature Swing', 'type': 'float', 'value': 1.5, 'step': 0.1, 'siPrefix': True, 'suffix': 'C'},
@ -71,7 +76,8 @@ GUIparams = [[
autoTuner = [PIDAutotune(20, 1, 1, 1.5, refresh_period / 1000), autoTuner = [PIDAutotune(20, 1, 1, 1.5, refresh_period / 1000),
PIDAutotune(20, 1, 1, 1.5, refresh_period / 1000)] PIDAutotune(20, 1, 1, 1.5, refresh_period / 1000)]
## If anything changes in the tree, print a message
# If anything changes in the tree, print a message
def change(param, changes, ch): def change(param, changes, ch):
print("tree changes:") print("tree changes:")
for param, change, data in changes: for param, change, data in changes:
@ -80,75 +86,79 @@ def change(param, changes, ch):
childName = '.'.join(path) childName = '.'.join(path)
else: else:
childName = param.name() childName = param.name()
print(' parameter: %s'% childName) print(' parameter: %s' % childName)
print(' change: %s'% change) print(' change: %s' % change)
print(' data: %s'% str(data)) print(' data: %s' % str(data))
print(' ----------') print(' ----------')
if (childName == 'Disable Output'): if childName == 'Disable Output':
tec.set_param('pwm', ch, 'i_set', 0) tec.set_param('pwm', ch, 'i_set', 0)
paramList[ch].child('Constant Current').child('Set Current').setValue(0) paramList[ch].child('Constant Current').child('Set Current').setValue(0)
paramList[ch].child('Temperature PID').setValue(False) paramList[ch].child('Temperature PID').setValue(False)
autoTuner[ch].setOff() autoTuner[ch].setOff()
if (childName == 'Temperature PID'): if childName == 'Temperature PID':
if (data): if (data):
tec.set_param("pwm", ch, "pid") tec.set_param("pwm", ch, "pid")
else: else:
tec.set_param('pwm', ch, 'i_set', paramList[ch].child('Constant Current').child('Set Current').value()) tec.set_param('pwm', ch, 'i_set', paramList[ch].child('Constant Current').child('Set Current').value())
if (childName == 'Constant Current.Set Current'):
tec.set_param('pwm', ch, 'i_set', data)
paramList[ch].child('Temperature PID').setValue(False)
if (childName == 'Temperature PID.Set Temperature'): if childName == 'Constant Current.Set Current':
tec.set_param('pwm', ch, 'i_set', data)
paramList[ch].child('Temperature PID').setValue(False)
if childName == 'Temperature PID.Set Temperature':
tec.set_param('pid', ch, 'target', data) tec.set_param('pid', ch, 'target', data)
if (childName == 'Output Config.Max Current'): if childName == 'Output Config.Max Current':
tec.set_param('pwm', ch, 'max_i_pos', data) tec.set_param('pwm', ch, 'max_i_pos', data)
tec.set_param('pwm', ch, 'max_i_neg', data) tec.set_param('pwm', ch, 'max_i_neg', data)
tec.set_param('pid', ch, 'output_min', -data) tec.set_param('pid', ch, 'output_min', -data)
tec.set_param('pid', ch, 'output_max', data) tec.set_param('pid', ch, 'output_max', data)
if (childName == 'Output Config.Max Voltage'): if childName == 'Output Config.Max Voltage':
tec.set_param('pwm', ch, 'max_v', data) tec.set_param('pwm', ch, 'max_v', data)
if (childName == 'Thermistor Config.T0'): if childName == 'Thermistor Config.T0':
tec.set_param('s-h', ch, 't0', data) tec.set_param('s-h', ch, 't0', data)
if (childName == 'Thermistor Config.R0'): if childName == 'Thermistor Config.R0':
tec.set_param('s-h', ch, 'r0', data) tec.set_param('s-h', ch, 'r0', data)
if (childName == 'Thermistor Config.Beta'): if childName == 'Thermistor Config.Beta':
tec.set_param('s-h', ch, 'b', data) tec.set_param('s-h', ch, 'b', data)
if (childName == 'PID Config.kP'): if childName == 'PID Config.kP':
tec.set_param('pid', ch, 'kp', data) tec.set_param('pid', ch, 'kp', data)
if (childName == 'PID Config.kI'): if childName == 'PID Config.kI':
tec.set_param('pid', ch, 'ki', data) tec.set_param('pid', ch, 'ki', data)
if (childName == 'PID Config.kD'): if childName == 'PID Config.kD':
tec.set_param('pid', ch, 'kd', data) tec.set_param('pid', ch, 'kd', data)
if (childName == 'PID Config.PID Auto Tune.Run'): if childName == 'PID Config.PID Auto Tune.Run':
autoTuner[ch].setParam(paramList[ch].child('PID Config').child('PID Auto Tune').child('Target Temperature').value(), autoTuner[ch].setParam(
paramList[ch].child('PID Config').child('PID Auto Tune').child('Test Current').value(), paramList[ch].child('PID Config').child('PID Auto Tune').child('Target Temperature').value(),
paramList[ch].child('PID Config').child('PID Auto Tune').child('Temperature Swing').value(), paramList[ch].child('PID Config').child('PID Auto Tune').child('Test Current').value(),
refresh_period / 1000, paramList[ch].child('PID Config').child('PID Auto Tune').child('Temperature Swing').value(),
1) refresh_period / 1000,
1)
autoTuner[ch].setReady() autoTuner[ch].setReady()
paramList[ch].child('Temperature PID').setValue(False) paramList[ch].child('Temperature PID').setValue(False)
if (childName == 'Save to flash'): if childName == 'Save to flash':
tec.save_config() tec.save_config()
def change0(param, changes): def change0(param, changes):
change(param, changes, 0) change(param, changes, 0)
def change1(param, changes): def change1(param, changes):
change(param, changes, 1) change(param, changes, 1)
class Curves: class Curves:
def __init__(self, legend: str, key: str, channel: int, color: str, buffer_len: int, period: int): def __init__(self, legend: str, key: str, channel: int, color: str, buffer_len: int, period: int):
self.curveItem = pg.PlotCurveItem(pen=({'color': color, 'width': 1})) self.curveItem = pg.PlotCurveItem(pen=({'color': color, 'width': 1}))
@ -159,21 +169,22 @@ class Curves:
self.time_stamp = np.zeros(buffer_len) self.time_stamp = np.zeros(buffer_len)
self.buffLen = buffer_len self.buffLen = buffer_len
self.period = period self.period = period
def update(self, tec_data, cnt): def update(self, tec_data, cnt):
if cnt == 0: if cnt == 0:
np.copyto(self.data_buf, np.full(self.buffLen, tec_data[self.channel][self.keyStr])) np.copyto(self.data_buf, np.full(self.buffLen, tec_data[self.channel][self.keyStr]))
else: else:
self.data_buf[:-1] = self.data_buf[1:] self.data_buf[:-1] = self.data_buf[1:]
self.data_buf[-1] = tec_data[self.channel][self.keyStr] self.data_buf[-1] = tec_data[self.channel][self.keyStr]
self.time_stamp[:-1] = self.time_stamp[1:] self.time_stamp[:-1] = self.time_stamp[1:]
self.time_stamp[-1] = cnt * self.period / 1000 self.time_stamp[-1] = cnt * self.period / 1000
self.curveItem.setData(x = self.time_stamp, y = self.data_buf) self.curveItem.setData(x=self.time_stamp, y=self.data_buf)
class Graph: class Graph:
def __init__(self, parent: pg.LayoutWidget, title: str, row: int, col: int, curves: list[Curves]): def __init__(self, parent: pg.LayoutWidget, title: str, row: int, col: int, curves: list[Curves]):
self.plotItem = pg.PlotWidget(title=title) self.plotItem = pg.PlotWidget(title=title)
self.legendItem = pg.LegendItem(offset=(75, 30), brush=(50,50,200,150)) self.legendItem = pg.LegendItem(offset=(75, 30), brush=(50, 50, 200, 150))
self.legendItem.setParentItem(self.plotItem.getPlotItem()) self.legendItem.setParentItem(self.plotItem.getPlotItem())
parent.addWidget(self.plotItem, row, col) parent.addWidget(self.plotItem, row, col)
self.curves = curves self.curves = curves
@ -184,7 +195,9 @@ class Graph:
def update(self, tec_data, cnt): def update(self, tec_data, cnt):
for curve in self.curves: for curve in self.curves:
curve.update(tec_data, cnt) curve.update(tec_data, cnt)
self.plotItem.setRange(xRange=[(cnt - self.curves[0].buffLen) * self.curves[0].period / 1000, cnt * self.curves[0].period / 1000]) self.plotItem.setRange(
xRange=[(cnt - self.curves[0].buffLen) * self.curves[0].period / 1000, cnt * self.curves[0].period / 1000])
def TECsync(): def TECsync():
global TECparams global TECparams
@ -209,22 +222,32 @@ def TECsync():
if parents['tag'] == 'PIDtarget': if parents['tag'] == 'PIDtarget':
for children in parents['children']: for children in parents['children']:
children['value'] = tec.get_pid()[channel]['target'] children['value'] = tec.get_pid()[channel]['target']
def refreshTreeParam(tempTree:dict, channel:int) -> dict:
tempTree['children']['Constant Current']['children']['Set Current']['value'] = TECparams[channel][1]['children'][3]['value'] def refreshTreeParam(tempTree: dict, channel: int) -> dict:
tempTree['children']['Constant Current']['children']['Set Current']['value'] = TECparams[channel][1]['children'][3][
'value']
tempTree['children']['Temperature PID']['value'] = TECparams[channel][0]['children'][0]['value'] tempTree['children']['Temperature PID']['value'] = TECparams[channel][0]['children'][0]['value']
tempTree['children']['Temperature PID']['children']['Set Temperature']['value'] = TECparams[channel][4]['children'][0]['value'] tempTree['children']['Temperature PID']['children']['Set Temperature']['value'] = \
tempTree['children']['Output Config']['children']['Max Current']['value'] = TECparams[channel][1]['children'][0]['value'] TECparams[channel][4]['children'][0]['value']
tempTree['children']['Output Config']['children']['Max Voltage']['value'] = TECparams[channel][1]['children'][2]['value'] tempTree['children']['Output Config']['children']['Max Current']['value'] = TECparams[channel][1]['children'][0][
tempTree['children']['Thermistor Config']['children']['T0']['value'] = TECparams[channel][3]['children'][0]['value'] - 273.15 'value']
tempTree['children']['Output Config']['children']['Max Voltage']['value'] = TECparams[channel][1]['children'][2][
'value']
tempTree['children']['Thermistor Config']['children']['T0']['value'] = TECparams[channel][3]['children'][0][
'value'] - 273.15
tempTree['children']['Thermistor Config']['children']['R0']['value'] = TECparams[channel][3]['children'][1]['value'] tempTree['children']['Thermistor Config']['children']['R0']['value'] = TECparams[channel][3]['children'][1]['value']
tempTree['children']['Thermistor Config']['children']['Beta']['value'] = TECparams[channel][3]['children'][2]['value'] tempTree['children']['Thermistor Config']['children']['Beta']['value'] = TECparams[channel][3]['children'][2][
'value']
tempTree['children']['PID Config']['children']['kP']['value'] = TECparams[channel][2]['children'][0]['value'] tempTree['children']['PID Config']['children']['kP']['value'] = TECparams[channel][2]['children'][0]['value']
tempTree['children']['PID Config']['children']['kI']['value'] = TECparams[channel][2]['children'][1]['value'] tempTree['children']['PID Config']['children']['kI']['value'] = TECparams[channel][2]['children'][1]['value']
tempTree['children']['PID Config']['children']['kD']['value'] = TECparams[channel][2]['children'][2]['value'] tempTree['children']['PID Config']['children']['kD']['value'] = TECparams[channel][2]['children'][2]['value']
return tempTree return tempTree
cnt = 0 cnt = 0
def updateData(): def updateData():
global cnt global cnt
for data in tec.report_mode(): for data in tec.report_mode():
@ -234,13 +257,13 @@ def updateData():
ch0currentGraph.update(data, cnt) ch0currentGraph.update(data, cnt)
ch1currentGraph.update(data, cnt) ch1currentGraph.update(data, cnt)
for channel in range (2): for channel in range(2):
if (autoTuner[channel].state() == PIDAutotuneState.STATE_READY or if (autoTuner[channel].state() == PIDAutotuneState.STATE_READY or
autoTuner[channel].state() == PIDAutotuneState.STATE_RELAY_STEP_UP or autoTuner[channel].state() == PIDAutotuneState.STATE_RELAY_STEP_UP or
autoTuner[channel].state() == PIDAutotuneState.STATE_RELAY_STEP_DOWN): autoTuner[channel].state() == PIDAutotuneState.STATE_RELAY_STEP_DOWN):
autoTuner[channel].run(data[channel]['temperature'], data[channel]['time']) autoTuner[channel].run(data[channel]['temperature'], data[channel]['time'])
tec.set_param('pwm', channel, 'i_set', autoTuner[channel].output()) tec.set_param('pwm', channel, 'i_set', autoTuner[channel].output())
elif (autoTuner[channel].state() == PIDAutotuneState.STATE_SUCCEEDED): elif autoTuner[channel].state() == PIDAutotuneState.STATE_SUCCEEDED:
kp, ki, kd = autoTuner[channel].get_tec_pid() kp, ki, kd = autoTuner[channel].get_tec_pid()
autoTuner[channel].setOff() autoTuner[channel].setOff()
paramList[channel].child('PID Config').child('kP').setValue(kp) paramList[channel].child('PID Config').child('kP').setValue(kp)
@ -249,14 +272,14 @@ def updateData():
tec.set_param('pid', channel, 'kp', kp) tec.set_param('pid', channel, 'kp', kp)
tec.set_param('pid', channel, 'ki', ki) tec.set_param('pid', channel, 'ki', ki)
tec.set_param('pid', channel, 'kd', kd) tec.set_param('pid', channel, 'kd', kd)
elif (autoTuner[channel].state() == PIDAutotuneState.STATE_FAILED): elif autoTuner[channel].state() == PIDAutotuneState.STATE_FAILED:
tec.set_param('pwm', channel, 'i_set', 0) tec.set_param('pwm', channel, 'i_set', 0)
autoTuner[channel].setOff() autoTuner[channel].setOff()
if quit: if quit:
break break
cnt += 1 cnt += 1
if __name__ == '__main__': if __name__ == '__main__':
tec = Client(host="192.168.1.26", port=23, timeout=None) tec = Client(host="192.168.1.26", port=23, timeout=None)
@ -265,7 +288,7 @@ if __name__ == '__main__':
pg.setConfigOptions(antialias=True) pg.setConfigOptions(antialias=True)
mw = QtGui.QMainWindow() mw = QtGui.QMainWindow()
mw.setWindowTitle('Thermostat Control Panel') mw.setWindowTitle('Thermostat Control Panel')
mw.resize(1920,1200) mw.resize(1920, 1200)
cw = QtGui.QWidget() cw = QtGui.QWidget()
mw.setCentralWidget(cw) mw.setCentralWidget(cw)
l = QtGui.QVBoxLayout() l = QtGui.QVBoxLayout()
@ -278,7 +301,7 @@ if __name__ == '__main__':
Parameter.create(name='GUIparams', type='group', children=GUIparams[1])] Parameter.create(name='GUIparams', type='group', children=GUIparams[1])]
ch0Tree = ParameterTree() ch0Tree = ParameterTree()
ch0Tree.setParameters(paramList[0], showTop=False) ch0Tree.setParameters(paramList[0], showTop=False)
ch1Tree = ParameterTree() ch1Tree = ParameterTree()
ch1Tree.setParameters(paramList[1], showTop=False) ch1Tree.setParameters(paramList[1], showTop=False)
@ -292,12 +315,16 @@ if __name__ == '__main__':
layout.addWidget(ch0Tree, 1, 1, 1, 1) layout.addWidget(ch0Tree, 1, 1, 1, 1)
layout.addWidget(ch1Tree, 2, 1, 1, 1) layout.addWidget(ch1Tree, 2, 1, 1, 1)
ch0tempGraph = Graph(layout, 'Channel 0 Temperature', 1, 2, [Curves('Feedback', 'temperature', 0, 'r', rec_len, refresh_period)]) ch0tempGraph = Graph(layout, 'Channel 0 Temperature', 1, 2,
ch1tempGraph = Graph(layout, 'Channel 1 Temperature', 2, 2, [Curves('Feedback', 'temperature', 1, 'r', rec_len, refresh_period)]) [Curves('Feedback', 'temperature', 0, 'r', rec_len, refresh_period)])
ch0currentGraph = Graph(layout, 'Channel 0 Current', 1, 3, [Curves('Feedback', 'tec_i', 0, 'r', rec_len, refresh_period), ch1tempGraph = Graph(layout, 'Channel 1 Temperature', 2, 2,
Curves('Setpoint', 'i_set', 0, 'g', rec_len, refresh_period)]) [Curves('Feedback', 'temperature', 1, 'r', rec_len, refresh_period)])
ch1currentGraph = Graph(layout, 'Channel 1 Current', 2, 3, [Curves('Feedback', 'tec_i', 1, 'r', rec_len, refresh_period), ch0currentGraph = Graph(layout, 'Channel 0 Current', 1, 3,
Curves('Setpoint', 'i_set', 1, 'g', rec_len, refresh_period)]) [Curves('Feedback', 'tec_i', 0, 'r', rec_len, refresh_period),
Curves('Setpoint', 'i_set', 0, 'g', rec_len, refresh_period)])
ch1currentGraph = Graph(layout, 'Channel 1 Current', 2, 3,
[Curves('Feedback', 'tec_i', 1, 'r', rec_len, refresh_period),
Curves('Setpoint', 'i_set', 1, 'g', rec_len, refresh_period)])
t = QtCore.QTimer() t = QtCore.QTimer()
t.timeout.connect(updateData) t.timeout.connect(updateData)
@ -305,4 +332,4 @@ if __name__ == '__main__':
mw.show() mw.show()
pg.exec() pg.exec()

183
pytec/tec_qt.py Normal file
View File

@ -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()

671
pytec/tec_qt.ui Normal file
View File

@ -0,0 +1,671 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1280</width>
<height>720</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>1280</width>
<height>720</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>3840</width>
<height>2160</height>
</size>
</property>
<property name="windowTitle">
<string>Control TEC</string>
</property>
<widget class="QWidget" name="main_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="1">
<layout class="QVBoxLayout" name="main_layout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="graph_group">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="graphs_layout" rowstretch="1,1" columnstretch="1,1,1" rowminimumheight="100,100" columnminimumwidth="100,100,100">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="spacing">
<number>2</number>
</property>
<item row="1" column="0">
<widget class="ParameterTree" name="ch1_tree" native="true"/>
</item>
<item row="0" column="0">
<widget class="ParameterTree" name="ch0_tree" native="true"/>
</item>
<item row="1" column="1">
<widget class="PlotWidget" 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">
<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">
<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">
<property name="title">
<string>Channel 1 Current</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="bottom_settings_group">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="settings_layout">
<item>
<widget class="QLineEdit" name="ip_set_line">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>160</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>192.168.1.26</string>
</property>
<property name="maxLength">
<number>15</number>
</property>
<property name="placeholderText">
<string>IP:port for the Thermostat</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="port_set_spin">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>23</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="connect_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="status_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>120</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>Disconnected</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_0">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="report_group" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="report_layout" stretch="1,1,1">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QDoubleSpinBox" name="report_refresh_spin">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::AdaptiveDecimalStepType</enum>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="report_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Report</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="report_apply_btn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_1">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="fan_group" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="gan_layout">
<property name="spacing">
<number>9</number>
</property>
<item>
<widget class="QLabel" name="fan_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Fan:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="fan_power_slider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="fan_auto_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="hw_rev_lbl">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Thermostat vX.Y</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</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>
</customwidgets>
<resources/>
<connections/>
</ui>

323
pytec/ui_tec_qt.py Normal file
View File

@ -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())