1 Commits

Author SHA1 Message Date
3c408ebcbd add standby functionality to table wgt 2023-06-01 12:14:26 +02:00
2 changed files with 303 additions and 62 deletions

View File

@@ -121,6 +121,8 @@ class PVGateway(QWidget):
self.color_mode = None
self.check_rtyp = False
if color_mode is not None:
if color_mode in (self.ACT_ON_BEAM,
self.NOT_ACT_ON_BEAM,
@@ -148,9 +150,13 @@ class PVGateway(QWidget):
self.cafe = self.parent.cafe
self.cyca = self.parent.cyca
self.url_archiver = None
self.url_databuffer = None
if self.parent.settings is not None:
self.url_archiver = self.parent.settings.data["url"]["archiver"]
self.url_databuffer = self.parent.settings.data["url"]["databuffer"]
if "databuffer" in self.parent.settings.data["url"]:
self.url_databuffer = self.parent.settings.data["url"]["databuffer"]
self.bg_readback = self.parent.settings.data["StyleGuide"][
"bgReadback"]
self.fg_alarm_major = self.parent.settings.data["StyleGuide"][
@@ -163,10 +169,10 @@ class PVGateway(QWidget):
"fgAlarmNoAlarm"]
else:
#self.settings = ReadJSON(self.parent.appname)
self.url_archiver = ("https://ui-data-api.psi.ch/prepare?channel=" +
self.url_archiver = ("https://data-ui.psi.ch/preselect?c1=" +
"sf-archiverappliance/")
self.url_databuffer \
= "https://ui-data-api.psi.ch/prepare?channel=sf-databuffer/"
= "https://data-ui.psi.ch/preselect?c1=sf-databuffer/"
self.daq_group_name = self._DAQ_CAFE_SG_NAME
self.desc = None
@@ -272,23 +278,20 @@ class PVGateway(QWidget):
action1 = QAction("Text Info", self)
action1.triggered.connect(self.pv_status_text)
self.context_menu.addAction(action1)
action2 = QAction("Lookup in Archiver", self)
action2.triggered.connect(self.lookup_archiver)
action3 = QAction("Lookup in Databuffer", self)
action3.triggered.connect(self.lookup_databuffer)
self.context_menu.addAction(action2)
if self.url_databuffer is not None:
action3 = QAction("Lookup in Databuffer", self)
action3.triggered.connect(self.lookup_databuffer)
self.context_menu.addAction(action3)
action4 = QAction("Strip Chart (PShell)", self)
action4.triggered.connect(self.strip_chart)
self.context_menu.addAction(action4)
action6 = QAction("Configure Display Parameters", self)
action6.triggered.connect(self.display_parameters)
self.context_menu.addAction(action1)
self.context_menu.addAction(action2)
self.context_menu.addAction(action3)
self.context_menu.addAction(action4)
action5 = QAction("Reconnect: {0}".format(self.pv_name), self)
action5.triggered.connect(self.reconnect_channel)
@@ -385,7 +388,7 @@ class PVGateway(QWidget):
if self.pv_info is None:
self.pv_info = self.cafe.getChannelInfo(self.pv_name)
if "Not Supported" in self.pv_info.className:
if "Not Supported" in self.pv_info.className and self.check_rtyp:
_rtype = self.cafe.get(self.pv_name.split(".")[0] + ".RTYP")
self.record_type = _rtype if _rtype is not None else \
self.pv_info.className
@@ -418,8 +421,9 @@ class PVGateway(QWidget):
_max_control_abs = 0
if self.pv_ctrl is not None:
_lower_control_abs = abs(int(self.pv_ctrl.lowerControlLimit))
_upper_control_abs = abs(int(self.pv_ctrl.upperControlLimit))
#DisplayLimit preferred over ControlLimit as latter n/a for ao
_lower_control_abs = abs(int(self.pv_ctrl.lowerDisplayLimit))
_upper_control_abs = abs(int(self.pv_ctrl.upperDisplayLimit))
_max_control_abs = max(_lower_control_abs, _upper_control_abs)
if _max_control_abs is None:
_max_control_abs = 0
@@ -494,7 +498,7 @@ class PVGateway(QWidget):
time.sleep(0.01)
self.initialize_meta_data()
icount += 1
if icount > 50:
if icount > 5: #50
return False
return True
@@ -583,7 +587,7 @@ class PVGateway(QWidget):
self.pv_ctrl = self.cafe.getCtrlCache(self.handle)
self.pv_info = self.cafe.getChannelInfo(self.handle)
if self.pv_info is not None and self.record_type is None:
if "Not Supported" in self.pv_info.className:
if "Not Supported" in self.pv_info.className and self.check_rtyp:
_rtype = self.cafe.get(self.pv_name.split(".")[0] + ".RTYP")
self.record_type = _rtype if _rtype is not None else \
self.pv_info.className
@@ -1342,7 +1346,7 @@ class PVGateway(QWidget):
if self.pv_info is None:
self.pv_info = self.cafe.getChannelInfo(self.handle)
if self.pv_info is not None and self.record_type is None:
if "Not Supported" in self.pv_info.className:
if "Not Supported" in self.pv_info.className and self.check_rtyp:
_rtype = self.cafe.get(self.pv_name.split(".")[0] + ".RTYP")
self.record_type = _rtype if _rtype is not None else \
self.pv_info.className
@@ -1386,7 +1390,7 @@ class PVGateway(QWidget):
if self.pv_info is None:
self.pv_info = self.cafe.getChannelInfo(self.handle)
if self.pv_info is not None and self.record_type is None:
if "Not Supported" in self.pv_info.className:
if "Not Supported" in self.pv_info.className and self.check_rtyp:
_rtype = self.cafe.get(self.pv_name.split(".")[0] + ".RTYP")
self.record_type = _rtype if _rtype is not None else \
self.pv_info.className
@@ -1491,8 +1495,7 @@ class PVGateway(QWidget):
self.pv_status_text_display_limits())
self.pv_message_in_a_box.setText(
self.pv_status_text_header(source=_source) + _text_data
)
self.pv_status_text_header(source=_source) + _text_data)
QApplication.processEvents()
self.pv_message_in_a_box.exec()

View File

@@ -12,18 +12,17 @@ from sklearn.linear_model import LinearRegression
from distutils.version import LooseVersion
from functools import reduce as func_reduce
from qtpy.QtCore import (QEventLoop, QMutex, QPoint, Qt, QThread, QTimer,
Signal, Slot)
from qtpy.QtGui import (QCloseEvent, QColor, QCursor, QFont, QFontMetricsF,
QIcon, QKeySequence)
from qtpy.QtCore import (
QEventLoop, QMutex, QPoint, Qt, QThread, QTimer, Signal, Slot)
from qtpy.QtGui import (
QCloseEvent, QColor, QCursor, QFont, QFontMetricsF, QIcon, QKeySequence)
from qtpy.QtCore import __version__ as QT_VERSION_STR
from qtpy.QtWidgets import (QAbstractItemView, QAbstractSpinBox, QAction,
QApplication, QBoxLayout, QCheckBox, QComboBox,
QDialog, QDockWidget, QDoubleSpinBox, QFrame,
QGroupBox, QHBoxLayout, QLabel, QLineEdit,
QListWidget, QMenu, QMessageBox, QPushButton,
QSpinBox, QStyle, QStyleOptionSpinBox, QTableWidget,
QTableWidgetItem, QVBoxLayout, QWidget)
from qtpy.QtWidgets import (
QAbstractItemView, QAbstractSpinBox, QAction, QApplication, QBoxLayout,
QCheckBox, QComboBox, QDialog, QDockWidget, QDoubleSpinBox, QFrame,
QGroupBox, QHBoxLayout, QLabel, QLineEdit, QListWidget, QMenu, QMessageBox,
QPushButton, QSpinBox, QStyle, QStyleOptionSpinBox, QTableWidget,
QTableWidgetItem, QVBoxLayout, QWidget)
import pyqtgraph as pg
from pyqtgraph import PlotWidget
@@ -132,6 +131,7 @@ class CAQLineEdit(QLineEdit, PVGateway):
_width_scaling_factor = 1.15
self.setFixedHeight((fm.lineSpacing()*1.8))
self.setFixedWidth(((qrect.width()) * _width_scaling_factor))
if self.pv_within_daq_group:
self.qt_property_initial_values(qt_object_name=self.PV_DAQ_CA)
@@ -287,7 +287,6 @@ class CAQMenu(QComboBox, PVGateway):
def post_display_value(self, value):
'''Convert value to index'''
if "setCurrentIndex" in dir(self):
@@ -297,7 +296,6 @@ class CAQMenu(QComboBox, PVGateway):
if isinstance(value, str):
self.setCurrentIndex(self.cafe.getEnumFromString(self.handle,
value))
elif isinstance(value, int):
self.setCurrentIndex(value)
#Should not happen
@@ -990,6 +988,8 @@ class CAQDoubleSpinBox(QDoubleSpinBox, PVGateway):
def mouseReleaseEvent(self, event):
self.clearFocus()
self.lineEdit().clearFocus()
def setValue(self, value):
self.currentValue = self.value()
@@ -1047,14 +1047,18 @@ class CAQDoubleSpinBox(QDoubleSpinBox, PVGateway):
self.qt_style_polish()
self.clearFocus()
self.lineEdit().clearFocus()
self.lineEdit().setFocusPolicy(Qt.NoFocus)
self.setFocusPolicy(Qt.NoFocus)
del event
def keyPressEvent(self, event):
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
QDoubleSpinBox.keyPressEvent(self, event)
self.clearFocus()
self.lineEdit().clearFocus()
self.lineEdit().setFocusPolicy(Qt.NoFocus)
self.setFocusPolicy(Qt.NoFocus)
elif event.key() in (Qt.Key_Up, Qt.Key_Down):
QDoubleSpinBox.keyPressEvent(self, event)
else:
@@ -1101,16 +1105,29 @@ class reconnectQPushButton(QPushButton, QThread):
def reconnect(self):
QApplication.processEvents()
print("Reconnect")
self.isdirty = True
if self._handles_to_reconnect:
print("handles to reconnect", self._handles_to_reconnect, flush=True)
self.parent.cafe.reconnect(self._handles_to_reconnect)
print("handles reconnected", self._handles_to_reconnect, flush=True)
self.isdirty = False
#Uncheck reconnected channels
for i in range(0, len(self.parent.pv_gateway)):
#print(i, len(self.parent.pv_gateway), flush=True)
#print( "ischecked", self.parent.item(
# i, self.parent.no_columns-1).checkState(), Qt.Checked )
#print("Connected", self.parent.cafe.isConnected(
# self.parent.pv_gateway[i].handle), flush=True)
if self.parent.item(
i, self.parent.no_columns-1).checkState() == Qt.Checked:
if self.parent.cafe.isConnected(
self.parent.pv_gateway[i].handle):
#print("isConnected", flush=True)
self.parent.item(
i, self.parent.no_columns-1).setCheckState(False)
else:
#Even if not connected - uncheck box as action is complete
self.parent.item(
i, self.parent.no_columns-1).setCheckState(False)
@@ -1196,7 +1213,6 @@ class CAQTableWidget(QTableWidget):
def widget_update(self):
for _row, pvgate in enumerate(self.pv_gateway):
if not pvgate.notify_unison:
@@ -1313,6 +1329,8 @@ class CAQTableWidget(QTableWidget):
color_mode=None, show_units: bool = True, prefix: str = "",
suffix: str = "", ts_res: str = "milli",
init_column: bool = False, init_list: list = [],
standby_column: bool = False, standby_list: list = [],
standby_values: list = [], set_delay: float = 0.0,
notify_freq_hz: int = 0, notify_unison: bool = True,
precision: int = 0, scale_factor: float = 1,
show_timestamp: bool = True, pv_list_show: list = None):
@@ -1332,6 +1350,10 @@ class CAQTableWidget(QTableWidget):
if init_column:
self.columns_dict['Init'] = _column_dict_value
_column_dict_value += 1
if standby_column:
self.columns_dict['Standby'] = _column_dict_value
_column_dict_value += 1
self.columns_dict['Value'] = _column_dict_value
@@ -1346,11 +1368,17 @@ class CAQTableWidget(QTableWidget):
self.no_columns = _column_dict_value + 1
self.init_column = init_column
self.init_list = init_list
if self.init_column and not self.init_list:
self.init_list = pv_list
self.standby_column = standby_column
self.standby_list = standby_list
if self.standby_column and not self.standby_list:
self.standby_list = pv_list
self.standby_values = standby_values
self.set_delay = set_delay
self.icount = 0
self.notify_freq_hz = abs(notify_freq_hz)
self.notify_freq_hz_default = self.notify_freq_hz
@@ -1434,6 +1462,7 @@ class CAQTableWidget(QTableWidget):
self.timer.singleShot(0, self.widget_update)
self.timer.start(self.notify_milliseconds)
self.reconnect_button = None
self.configure_widget()
#Connect only deals with colours - only helps on reconnect
@@ -1452,6 +1481,9 @@ class CAQTableWidget(QTableWidget):
if init_column:
self.update_init_values()
if standby_column:
self.update_standby_values()
self.configure_context_menu()
@@ -1473,7 +1505,108 @@ class CAQTableWidget(QTableWidget):
QApplication.processEvents()
def set_standby_values(self, pv_list: list = []):
if self.init_column:
self.init_value_button.setEnabled(False)
self.restore_value_button.setEnabled(False)
_text = self.standby_value_button.text()
self.standby_value_button.setText("Downing")
self.standby_value_button.setEnabled(False)
if self.reconnect_button is not None:
self.reconnect_button.setEnabled(False)
_set_values_dict = self.get_standby_values()
#return 1,2,3
if not pv_list:
_pvs_to_set, _values_to_set = zip(*_set_values_dict.items())
#zip returns tuples
_pvs_to_set = list(_pvs_to_set)
_values_to_set = list(_values_to_set)
else:
_pvs_to_set = []
_values_to_set = []
for pv in pv_list:
if pv in _set_values_dict.keys():
_pvs_to_set.append(pv)
_values_to_set.append(_set_values_dict[pv])
#self.standby_value_button.setEnabled(False)
#QApplication.processEvents(QEventLoop.AllEvents, 1.0)
if self.set_delay <= 0:
status, status_list = self.cafe.setScalarList(_pvs_to_set,
_values_to_set)
else:
status_list = [self.cyca.ICAFE_NORMAL] * len(_pvs_to_set)
status = self.cyca.ICAFE_NORMAL
for i, (pv, val) in enumerate(zip(_pvs_to_set, _values_to_set)):
status_list[i] = self.cafe.set(pv, val)
if status_list[i] != self.cyca.ICAFE_NORMAL:
status = status_list[i]
##self.standby_value_button.setEnabled(False)
##QApplication.processEvents(QEventLoop.AllEvents, 1.0)
time.sleep(self.set_delay)
##self.standby_value_button.setEnabled(False)
#QApplication.sendPostedEvents()
QApplication.processEvents(QEventLoop.AllEvents, 1.0)
if status != self.cyca.ICAFE_NORMAL:
_mess = ("The following devices reported an error " +
"in 'set' operation:")
for i, status_value in enumerate(status_list):
if status_value != self.cyca.ICAFE_NORMAL:
_mess += ("\n" + _pvs_to_set[i] + " has status = " +
str(status_value) + " " +
self.cafe.getStatusCodeAsString(status_value)
+ " " + self.cafe.getStatusInfo(status_value))
qm = QMessageBox()
qm.setText(_mess)
#qm.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
#qm.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
#qm.setFixedWidth(800)
##self.standby_value_button.setEnabled(False)
##QApplication.sendPostedEvents()
qm.exec()
#QApplication.processEvents()
if self.init_column:
self.init_value_button.setEnabled(True)
self.restore_value_button.setEnabled(True)
self.standby_value_button.setText(_text)
self.standby_value_button.setEnabled(True)
if self.reconnect_button is not None:
self.reconnect_button.setEnabled(True)
return status, status_list, _pvs_to_set
def restore_init_values(self, pv_list: list = []):
_text = self.restore_value_button.text()
self.restore_value_button.setText("Restoring")
self.restore_value_button.setEnabled(False)
if self.init_column:
self.init_value_button.setEnabled(False)
if self.standby_column:
self.standby_value_button.setEnabled(False)
if self.reconnect_button is not None:
self.reconnect_button.setEnabled(False)
_set_values_dict = self.get_init_values()
if not pv_list:
@@ -1489,8 +1622,19 @@ class CAQTableWidget(QTableWidget):
_pvs_to_set.append(pv)
_values_to_set.append(_set_values_dict[pv])
status, status_list = self.cafe.setScalarList(_pvs_to_set,
_values_to_set)
if self.set_delay <= 0:
status, status_list = self.cafe.setScalarList(_pvs_to_set,
_values_to_set)
else:
status_list = [self.cyca.ICAFE_NORMAL] * len(_pvs_to_set)
status = self.cyca.ICAFE_NORMAL
for i, (pv, val) in enumerate(zip(_pvs_to_set, _values_to_set)):
status_list[i] = self.cafe.set(pv, val)
if status_list[i] != self.cyca.ICAFE_NORMAL:
status = status_list[i]
time.sleep(self.set_delay)
QApplication.processEvents(QEventLoop.AllEvents, 1.0)
if status != self.cyca.ICAFE_NORMAL:
_mess = ("The following device(s) reported an error " +
@@ -1503,12 +1647,23 @@ class CAQTableWidget(QTableWidget):
" " + self.cafe.getStatusInfo(status_value))
qm = QMessageBox()
qm.setText(_mess)
qm.exec()
QApplication.processEvents()
self.init_value_button.setEnabled(True)
if self.init_column:
self.init_value_button.setEnabled(True)
self.restore_value_button.setText(_text)
self.restore_value_button.setEnabled(True)
if self.standby_column:
self.standby_value_button.setEnabled(True)
if self.reconnect_button is not None:
self.reconnect_button.setEnabled(True)
return status, status_list, _pvs_to_set
def is_same_as_init_values(self):
_init_values_dict = self.get_column_values(self.columns_dict['Init'])
@@ -1551,6 +1706,8 @@ class CAQTableWidget(QTableWidget):
if _pvs[_row] in self.pv_list_show:
_values_dict[self.pv_gateway[_row].pv_name] = _values[_row]
else:
_values_dict[_row] = _values[_row]
return _values_dict #_pvs_to_set, _values_to_set
@@ -1558,6 +1715,9 @@ class CAQTableWidget(QTableWidget):
def get_init_values(self):
return self.get_column_values(self.columns_dict['Init'])
def get_standby_values(self):
return self.get_column_values(self.columns_dict['Standby'])
def get_init_values_previous(self):
_set_values_dict = {}
_start = 0
@@ -1585,21 +1745,69 @@ class CAQTableWidget(QTableWidget):
_set_values_dict[
self.pv_gateway[_row].pv_name] = _values_to_set[_row]
return _set_values_dict
def update_init_values(self):
def update_standby_values(self):
_start = 0
_end = len(self.pv_gateway)
_end = len(self.standby_values)
if 'Standby' in self.columns_dict:
_column_no = self.columns_dict['Standby']
else:
return
for _row in range(_start, _end):
_value = self.standby_values[_row]
if self.scale_factor != 1:
_value = _value * self.scale_factor
_value = self.pv_gateway[_row].format_display_value(_value)
qtwi = QTableWidgetItem(str(_value)+ " ")
_f = qtwi.font()
_f.setPointSize(8)
qtwi.setFont(_f)
self.setItem(_row, _column_no, qtwi)
self.item(_row, _column_no).setTextAlignment(Qt.AlignRight |
Qt.AlignVCenter)
def set_init_values(self, values):
_start = 0
_end = min(len(self.pv_gateway), len(values))
if 'Init' in self.columns_dict:
_column_no = self.columns_dict['Init']
else:
return
for _row in range(_start, _end):
_value = self.pv_gateway[_row].format_display_value(values[_row])
qtwi = QTableWidgetItem(str(_value)+ " ")
_f = qtwi.font()
_f.setPointSize(8)
qtwi.setFont(_f)
self.setItem(_row, _column_no, qtwi)
self.item(_row, _column_no).setTextAlignment(Qt.AlignRight |
Qt.AlignVCenter)
def update_init_values(self):
_start = 0
_end = len(self.pv_gateway)
if 'Init' in self.columns_dict:
_column_no = self.columns_dict['Init']
else:
return
for _row in range(_start, _end):
_handle = self.pv_gateway[_row].handle
_value = self.pv_gateway[_row].cafe.getCache(_handle)
if _value is not None:
if self.scale_factor != 1:
_value = _value * self.scale_factor
@@ -1609,10 +1817,11 @@ class CAQTableWidget(QTableWidget):
_f = qtwi.font()
_f.setPointSize(8)
qtwi.setFont(_f)
self.setItem(_row, 1, qtwi)
self.setItem(_row, _column_no, qtwi)
self.item(_row, _column_no).setTextAlignment(Qt.AlignRight |
Qt.AlignVCenter)
def configure_widget(self):
@@ -1672,7 +1881,7 @@ class CAQTableWidget(QTableWidget):
if self.init_column:
self.init_widget = QWidget()
_init_layout = QHBoxLayout(self.init_widget)
self.init_layout = QHBoxLayout(self.init_widget)
self.init_value_button = QPushButton()
self.init_value_button.setText("Update")
_f = self.init_value_button.font()
@@ -1683,14 +1892,18 @@ class CAQTableWidget(QTableWidget):
self.init_value_button.setToolTip(
("Stores initial, pre-measurement value. Update is also " +
"typically executed automatically before analysis procedure."))
_init_layout.addWidget(self.init_value_button)
_init_layout.setAlignment(Qt.AlignCenter)
_init_layout.setContentsMargins(1, 1, 1, 0) #Required
self.init_widget.setLayout(_init_layout)
self.setCellWidget(len(self.pv_gateway), 1, self.init_widget)
self.init_layout.addWidget(self.init_value_button)
self.init_layout.setAlignment(Qt.AlignCenter)
self.init_layout.setContentsMargins(1, 1, 1, 0) #Required
self.init_widget.setLayout(self.init_layout)
if 'PV' in self.columns_dict:
self.setCellWidget(len(self.pv_gateway), 0, self.init_widget)
else:
self.setCellWidget(len(self.pv_gateway), self.columns_dict['Init'], self.init_widget)
_restore_widget = QWidget()
_restore_layout = QHBoxLayout(_restore_widget)
self.restore_widget = QWidget()
self.restore_layout = QHBoxLayout(self.restore_widget)
self.restore_value_button = QPushButton()
self.restore_value_button.setStyleSheet(
"QPushButton{background-color: rgb(212, 219, 157);}")
@@ -1702,11 +1915,36 @@ class CAQTableWidget(QTableWidget):
self.restore_value_button.clicked.connect(self.restore_init_values)
self.restore_value_button.setToolTip(
("Restore devices to their pre-measurement values"))
_restore_layout.addWidget(self.restore_value_button)
_restore_layout.setAlignment(Qt.AlignCenter)
_restore_layout.setContentsMargins(1, 1, 0, 0)
_restore_widget.setLayout(_restore_layout)
self.setCellWidget(len(self.pv_gateway), 2, _restore_widget)
self.restore_layout.addWidget(self.restore_value_button)
self.restore_layout.setAlignment(Qt.AlignCenter)
self.restore_layout.setContentsMargins(1, 1, 0, 0)
self.restore_widget.setLayout(self.restore_layout)
if 'PV' in self.columns_dict:
self.setCellWidget(len(self.pv_gateway),
self.columns_dict['Init'],
self.restore_widget)
if self.standby_column:
_standby_widget = QWidget()
_standby_layout = QHBoxLayout(_standby_widget)
self.standby_value_button = QPushButton()
self.standby_value_button.setStyleSheet(
"QPushButton{background-color: rgb(212, 219, 157);}")
self.standby_value_button.setText("Standby")
_f = self.standby_value_button.font()
_f.setPointSize(8)
self.standby_value_button.setFont(_f)
self.standby_value_button.setFixedWidth(80)
self.standby_value_button.clicked.connect(self.set_standby_values)
self.standby_value_button.setToolTip(
("Set devices to their pre-set standby values"))
_standby_layout.addWidget(self.standby_value_button)
_standby_layout.setAlignment(Qt.AlignCenter)
_standby_layout.setContentsMargins(1, 1, 0, 0)
_standby_widget.setLayout(_standby_layout)
self.setCellWidget(len(self.pv_gateway), self.columns_dict['Standby'], _standby_widget)
#Do not display no for last row (Reconnect button)
_row_digit_last_cell = QTableWidgetItem(str(""))
@@ -1729,7 +1967,7 @@ class CAQTableWidget(QTableWidget):
self.reconnect_button.setFont(f)
self.reconnect_button.setText("Reconnect")
self.reconnect_button.setToolTip("Reconnect selected/checked channels")
_layout = QHBoxLayout(_qwb)
_layout.addWidget(self.reconnect_button)
_layout.setAlignment(Qt.AlignCenter)