Configure and start ssh tunneled connections

This commit is contained in:
2023-01-07 10:36:53 +01:00
parent 2f595f66aa
commit 0c7192e1d4
5 changed files with 442 additions and 198 deletions

View File

@@ -14,9 +14,12 @@ from queue import Queue
from threading import Lock from threading import Lock
from xmlrpc.client import Binary, ServerProxy from xmlrpc.client import Binary, ServerProxy
from PyQt5 import QtCore from PyQt5 import QtCore, QtWidgets
from paramiko.ssh_exception import AuthenticationException
from . import proginit as pi from . import proginit as pi
from .ssh_tunneling.server import SSHLocalTunnel
from .sshauth import SSHAuth, SSHAuthType
class WidgetData(IntEnum): class WidgetData(IntEnum):
@@ -40,6 +43,9 @@ class WidgetData(IntEnum):
watch_files = 310 watch_files = 310
watch_path = 311 watch_path = 311
debug_geos = 312 debug_geos = 312
ssh_use_tunnel = 313
ssh_port = 315
ssh_user = 316
class ConnectionManager(QtCore.QThread): class ConnectionManager(QtCore.QThread):
@@ -55,6 +61,8 @@ class ConnectionManager(QtCore.QThread):
"""This will be triggered, if a connection error was detected.""" """This will be triggered, if a connection error was detected."""
status_changed = QtCore.pyqtSignal(str, str) status_changed = QtCore.pyqtSignal(str, str)
"""Status message and color suggestion.""" """Status message and color suggestion."""
connection_recovered = QtCore.pyqtSignal()
"""After errors the connection is established again, could have other port information (SSH)."""
def __init__(self, parent=None, cycle_time_ms=1000): def __init__(self, parent=None, cycle_time_ms=1000):
super(ConnectionManager, self).__init__(parent) super(ConnectionManager, self).__init__(parent)
@@ -71,6 +79,12 @@ class ConnectionManager(QtCore.QThread):
self.name = "" self.name = ""
self.port = 55123 self.port = 55123
self.ssh_tunnel_server = None # type: SSHLocalTunnel
self.ssh_use_tunnel = False
self.ssh_port = 22
self.ssh_user = "pi"
self.ssh_pass = ""
# Sync this with revpiplclist to preserve settings # Sync this with revpiplclist to preserve settings
self.program_last_dir_upload = "" self.program_last_dir_upload = ""
self.program_last_file_upload = "" self.program_last_file_upload = ""
@@ -167,6 +181,12 @@ class ConnectionManager(QtCore.QThread):
self.address = "" self.address = ""
self.name = "" self.name = ""
self.port = 55123 self.port = 55123
self.ssh_use_tunnel = False
self.ssh_port = 22
self.ssh_user = "pi"
self.ssh_pass = ""
self.pyload_version = (0, 0, 0) self.pyload_version = (0, 0, 0)
self.xml_funcs.clear() self.xml_funcs.clear()
self.xml_mode = -1 self.xml_mode = -1
@@ -207,11 +227,12 @@ class ConnectionManager(QtCore.QThread):
settings.endArray() settings.endArray()
def pyload_connect(self, settings_index: int): def pyload_connect(self, settings_index: int, parent=None) -> bool:
""" """
Create a new connection from settings object. Create a new connection from settings object.
:param settings_index: Index of settings array 'connections' :param settings_index: Index of settings array 'connections'
:param parent: Qt parent window for dialog positioning
:return: True, if the connection was successfully established :return: True, if the connection was successfully established
""" """
@@ -226,6 +247,13 @@ class ConnectionManager(QtCore.QThread):
port = settings.value("port", 55123, int) port = settings.value("port", 55123, int)
timeout = settings.value("timeout", 5, int) timeout = settings.value("timeout", 5, int)
ssh_tunnel_server = None
ssh_use_tunnel = settings.value("ssh_use_tunnel", False, bool)
ssh_port = settings.value("ssh_port", 22, int)
ssh_user = settings.value("ssh_user", "pi", str)
ssh_tunnel_port = 0
ssh_pass = ""
self.program_last_dir_upload = settings.value("last_dir_upload", ".", str) self.program_last_dir_upload = settings.value("last_dir_upload", ".", str)
self.program_last_file_upload = settings.value("last_file_upload", ".", str) self.program_last_file_upload = settings.value("last_file_upload", ".", str)
self.program_last_dir_pictory = settings.value("last_dir_pictory", ".", str) self.program_last_dir_pictory = settings.value("last_dir_pictory", ".", str)
@@ -241,6 +269,41 @@ class ConnectionManager(QtCore.QThread):
settings.endArray() settings.endArray()
socket.setdefaulttimeout(2) socket.setdefaulttimeout(2)
if ssh_use_tunnel:
while True:
diag_ssh_auth = SSHAuth(SSHAuthType.PASS, parent)
diag_ssh_auth.username = ssh_user
if not diag_ssh_auth.exec() == QtWidgets.QDialog.Accepted:
self._clear_settings()
return False
ssh_user = diag_ssh_auth.username
ssh_pass = diag_ssh_auth.password
ssh_tunnel_server = SSHLocalTunnel(port, address, ssh_port)
try:
ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(ssh_user, ssh_pass)
break
except AuthenticationException:
QtWidgets.QMessageBox.critical(
parent, self.tr("Error"), self.tr(
"The combination of username and password was rejected from the SSH server.\n\n"
"Try again."
)
)
except Exception as e:
# todo: Check some more kinds of exceptions and nice user info
self._clear_settings()
QtWidgets.QMessageBox.critical(
parent, self.tr("Error"), self.tr(
"Could not establish a SSH connection to server:\n\n{0}"
).format(str(e))
)
return False
sp = ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
else:
sp = ServerProxy("http://{0}:{1}".format(address, port)) sp = ServerProxy("http://{0}:{1}".format(address, port))
# Load values and test connection to Revolution Pi # Load values and test connection to Revolution Pi
@@ -251,19 +314,41 @@ class ConnectionManager(QtCore.QThread):
except Exception as e: except Exception as e:
pi.logger.exception(e) pi.logger.exception(e)
self.connection_error_observed.emit(str(e)) self.connection_error_observed.emit(str(e))
if not self.ssh_use_tunnel:
# todo: Change message, that user can use ssh
QtWidgets.QMessageBox.critical(
parent, self.tr("Error"), self.tr(
"Can not connect to RevPi XML-RPC Service! \n\n"
"This could have the following reasons: The RevPi is not "
"online, the XML-RPC service is not running / bind to "
"localhost or the ACL permission is not set for your "
"IP!!!\n\nRun 'sudo revpipyload_secure_installation' on "
"Revolution Pi to setup this function!"
)
)
return False return False
self.address = address self.address = address
self.name = name self.name = name
self.port = port self.port = port
self.ssh_use_tunnel = ssh_use_tunnel
self.ssh_port = ssh_port
self.ssh_user = ssh_user
self.ssh_pass = ssh_pass
self.pyload_version = pyload_version self.pyload_version = pyload_version
self.xml_funcs = xml_funcs self.xml_funcs = xml_funcs
self.xml_mode = xml_mode self.xml_mode = xml_mode
with self._lck_cli: with self._lck_cli:
socket.setdefaulttimeout(timeout) socket.setdefaulttimeout(timeout)
self.ssh_tunnel_server = ssh_tunnel_server
self._cli = sp self._cli = sp
self._cli_connect.put_nowait((address, port)) self._cli_connect.put_nowait((
"127.0.0.1" if ssh_use_tunnel else address,
ssh_tunnel_port if ssh_use_tunnel else port
))
self.connection_established.emit() self.connection_established.emit()
@@ -286,7 +371,7 @@ class ConnectionManager(QtCore.QThread):
elif self._cli is not None: elif self._cli is not None:
# Tell all widget, that we want do disconnect, to save the settings # Tell all widget, that we want to disconnect, to save the settings
self.connection_disconnecting.emit() self.connection_disconnecting.emit()
self._save_settings() self._save_settings()
@@ -299,6 +384,10 @@ class ConnectionManager(QtCore.QThread):
self._clear_settings() self._clear_settings()
self._cli = None self._cli = None
if self.ssh_tunnel_server:
self.ssh_tunnel_server.disconnect()
self.ssh_tunnel_server = None
self.connection_disconnected.emit() self.connection_disconnected.emit()
def pyload_simulate(self, configrsc: str, procimg: str, clean_existing: bool): def pyload_simulate(self, configrsc: str, procimg: str, clean_existing: bool):
@@ -383,6 +472,23 @@ class ConnectionManager(QtCore.QThread):
pi.logger.warning(e) pi.logger.warning(e)
self.status_changed.emit(self.tr("SERVER ERROR"), "red") self.status_changed.emit(self.tr("SERVER ERROR"), "red")
self.connection_error_observed.emit("{0} | {1}".format(e, type(e))) self.connection_error_observed.emit("{0} | {1}".format(e, type(e)))
if self.ssh_tunnel_server and not self.ssh_tunnel_server.connected:
self.ssh_tunnel_server.disconnect()
ssh_tunnel_server = SSHLocalTunnel(self.port, self.address, self.ssh_port)
try:
ssh_tunnel_port = self.ssh_tunnel_server.connect_by_credentials(
self.ssh_user,
self.ssh_pass
)
sp = ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
with self._lck_cli:
self.ssh_tunnel_server = ssh_tunnel_server
self._cli = sp
self.connection_recovered.emit()
except Exception:
pass
else: else:
if plc_exit_code == -1: if plc_exit_code == -1:
self.status_changed.emit(self.tr("RUNNING"), "green") self.status_changed.emit(self.tr("RUNNING"), "green")
@@ -452,10 +558,16 @@ class ConnectionManager(QtCore.QThread):
return default_value return default_value
def get_cli(self): def get_cli(self):
"""Connection proxy of actual connection.""" """
if self.address and self.port: Connection proxy of actual connection.
Use connection_recovered signal to figure out new parameters.
"""
if not self.ssh_use_tunnel and self.address and self.port:
return ServerProxy("http://{0}:{1}".format(self.address, self.port)) return ServerProxy("http://{0}:{1}".format(self.address, self.port))
else: if self.ssh_use_tunnel and self.ssh_tunnel_server and self.ssh_tunnel_server.connected:
return ServerProxy("http://127.0.0.1:{0}".format(self.ssh_tunnel_server.local_tunnel_port))
return None return None
@property @property

View File

@@ -5,7 +5,7 @@
__author__ = "Sven Sager" __author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager" __copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "GPLv3" __license__ = "GPLv3"
__version__ = "0.9.3" __version__ = "0.9.10rc1"
import webbrowser import webbrowser
from os.path import basename, dirname, join from os.path import basename, dirname, join
@@ -97,17 +97,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
# region # REGION: Connection management # region # REGION: Connection management
def _pyload_connect(self, settings_index: int) -> None: def _pyload_connect(self, settings_index: int) -> None:
if not helper.cm.pyload_connect(settings_index): helper.cm.pyload_connect(settings_index, self)
QtWidgets.QMessageBox.critical(
self, self.tr("Error"), self.tr(
"Can not connect to RevPi XML-RPC Service! \n\n"
"This could have the following reasons: The RevPi is not "
"online, the XML-RPC service is not running / bind to "
"localhost or the ACL permission is not set for your "
"IP!!!\n\nRun 'sudo revpipyload_secure_installation' on "
"Revolution Pi to setup this function!"
)
)
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
def on_cm_connection_error_observed(self, message: str): def on_cm_connection_error_observed(self, message: str):
@@ -192,8 +182,12 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
else: else:
parent_menu = self.men_connections parent_menu = self.men_connections
display_name = helper.settings.value("name")
if helper.settings.value("ssh_use_tunnel", False, bool):
display_name += " (SSH)"
act = QtWidgets.QAction(parent_menu) act = QtWidgets.QAction(parent_menu)
act.setText(helper.settings.value("name")) act.setText(display_name)
act.setData(i) act.setData(i)
act.setToolTip("{0}:{1}".format( act.setToolTip("{0}:{1}".format(
helper.settings.value("address"), helper.settings.value("address"),

View File

@@ -51,6 +51,10 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
con_item.setData(0, WidgetData.port, settings.value("port", self.__default_port, int)) con_item.setData(0, WidgetData.port, settings.value("port", self.__default_port, int))
con_item.setData(0, WidgetData.timeout, settings.value("timeout", 5, int)) con_item.setData(0, WidgetData.timeout, settings.value("timeout", 5, int))
con_item.setData(0, WidgetData.ssh_use_tunnel, settings.value("ssh_use_tunnel", False, bool))
con_item.setData(0, WidgetData.ssh_port, settings.value("ssh_port", 22, int))
con_item.setData(0, WidgetData.ssh_user, settings.value("ssh_user", "pi", str))
con_item.setData(0, WidgetData.last_dir_upload, settings.value("last_dir_upload")) con_item.setData(0, WidgetData.last_dir_upload, settings.value("last_dir_upload"))
con_item.setData(0, WidgetData.last_file_upload, settings.value("last_file_upload")) con_item.setData(0, WidgetData.last_file_upload, settings.value("last_file_upload"))
con_item.setData(0, WidgetData.last_dir_pictory, settings.value("last_dir_pictory")) con_item.setData(0, WidgetData.last_dir_pictory, settings.value("last_dir_pictory"))
@@ -61,7 +65,12 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
con_item.setData(0, WidgetData.last_zip_file, settings.value("last_zip_file")) con_item.setData(0, WidgetData.last_zip_file, settings.value("last_zip_file"))
con_item.setData(0, WidgetData.watch_files, settings.value("watch_files")) con_item.setData(0, WidgetData.watch_files, settings.value("watch_files"))
con_item.setData(0, WidgetData.watch_path, settings.value("watch_path")) con_item.setData(0, WidgetData.watch_path, settings.value("watch_path"))
try:
# Bytes with QSettings are a little difficult sometimes
con_item.setData(0, WidgetData.debug_geos, settings.value("debug_geos")) con_item.setData(0, WidgetData.debug_geos, settings.value("debug_geos"))
except Exception:
# Just drop the geos of IO windows
pass
folder = settings.value("folder", "", str) folder = settings.value("folder", "", str)
if folder: if folder:
@@ -95,6 +104,9 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
settings.setValue("name", node.text(0)) settings.setValue("name", node.text(0))
settings.setValue("port", node.data(0, WidgetData.port)) settings.setValue("port", node.data(0, WidgetData.port))
settings.setValue("timeout", node.data(0, WidgetData.timeout)) settings.setValue("timeout", node.data(0, WidgetData.timeout))
settings.setValue("ssh_use_tunnel", node.data(0, WidgetData.ssh_use_tunnel))
settings.setValue("ssh_port", node.data(0, WidgetData.ssh_port))
settings.setValue("ssh_user", node.data(0, WidgetData.ssh_user))
if node.data(0, WidgetData.last_dir_upload): if node.data(0, WidgetData.last_dir_upload):
settings.setValue("last_dir_upload", node.data(0, WidgetData.last_dir_upload)) settings.setValue("last_dir_upload", node.data(0, WidgetData.last_dir_upload))
@@ -195,6 +207,10 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.sbx_timeout.setEnabled(con_item) self.sbx_timeout.setEnabled(con_item)
self.cbb_folder.setEnabled(con_item or dir_item) self.cbb_folder.setEnabled(con_item or dir_item)
self.cbx_ssh_use_tunnel.setEnabled(con_item)
self.sbx_ssh_port.setEnabled(con_item)
self.txt_ssh_user.setEnabled(con_item)
def _get_folder_item(self, name: str): def _get_folder_item(self, name: str):
"""Find the folder entry by name.""" """Find the folder entry by name."""
for i in range(self.tre_connections.topLevelItemCount()): for i in range(self.tre_connections.topLevelItemCount()):
@@ -241,6 +257,11 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.cbb_folder.setCurrentIndex(0) self.cbb_folder.setCurrentIndex(0)
else: else:
self.cbb_folder.setCurrentText(current.parent().text(0)) self.cbb_folder.setCurrentText(current.parent().text(0))
self.cbx_ssh_use_tunnel.setChecked(current.data(0, WidgetData.ssh_use_tunnel))
self.sbx_ssh_port.setValue(current.data(0, WidgetData.ssh_port))
self.txt_ssh_user.setText(current.data(0, WidgetData.ssh_user))
elif current and current.type() == NodeType.DIR: elif current and current.type() == NodeType.DIR:
self.__current_item = current self.__current_item = current
self.cbb_folder.setCurrentText(current.text(0)) self.cbb_folder.setCurrentText(current.text(0))
@@ -278,6 +299,9 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.__current_item.setText(0, self.__default_name) self.__current_item.setText(0, self.__default_name)
self.__current_item.setData(0, WidgetData.port, self.__default_port) self.__current_item.setData(0, WidgetData.port, self.__default_port)
self.__current_item.setData(0, WidgetData.timeout, 5) self.__current_item.setData(0, WidgetData.timeout, 5)
self.__current_item.setData(0, WidgetData.ssh_use_tunnel, False)
self.__current_item.setData(0, WidgetData.ssh_port, 22)
self.__current_item.setData(0, WidgetData.ssh_user, "pi")
sub_folder = self._get_folder_item(self.cbb_folder.currentText()) sub_folder = self._get_folder_item(self.cbb_folder.currentText())
if sub_folder: if sub_folder:
sub_folder.addChild(self.__current_item) sub_folder.addChild(self.__current_item)
@@ -312,6 +336,24 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
return return
self.__current_item.setData(0, WidgetData.timeout, value) self.__current_item.setData(0, WidgetData.timeout, value)
@QtCore.pyqtSlot(int)
def on_cbx_ssh_use_tunnel_stateChanged(self, check_state: int):
if self.__current_item.type() != NodeType.CON:
return
self.__current_item.setData(0, WidgetData.ssh_use_tunnel, check_state == QtCore.Qt.CheckState.Checked)
@QtCore.pyqtSlot(int)
def on_sbx_ssh_port_valueChanged(self, value: int):
if self.__current_item.type() != NodeType.CON:
return
self.__current_item.setData(0, WidgetData.ssh_port, value)
@QtCore.pyqtSlot(str)
def on_txt_ssh_user_textEdited(self, text):
if self.__current_item.type() != NodeType.CON:
return
self.__current_item.setData(0, WidgetData.ssh_user, text)
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
def on_cbb_folder_editTextChanged(self, text: str): def on_cbb_folder_editTextChanged(self, text: str):
pi.logger.debug("RevPiPlcList.on_cbb_folder_editTextChanged({0})".format(text)) pi.logger.debug("RevPiPlcList.on_cbb_folder_editTextChanged({0})".format(text))

View File

@@ -14,9 +14,98 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_diag_connections(object): class Ui_diag_connections(object):
def setupUi(self, diag_connections): def setupUi(self, diag_connections):
diag_connections.setObjectName("diag_connections") diag_connections.setObjectName("diag_connections")
diag_connections.resize(520, 508) diag_connections.resize(496, 569)
self.gridLayout = QtWidgets.QGridLayout(diag_connections) self.gridLayout = QtWidgets.QGridLayout(diag_connections)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.tab_properties = QtWidgets.QTabWidget(diag_connections)
self.tab_properties.setObjectName("tab_properties")
self.tab_connection = QtWidgets.QWidget()
self.tab_connection.setObjectName("tab_connection")
self.formLayout_2 = QtWidgets.QFormLayout(self.tab_connection)
self.formLayout_2.setObjectName("formLayout_2")
self.lbl_name = QtWidgets.QLabel(self.tab_connection)
self.lbl_name.setObjectName("lbl_name")
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lbl_name)
self.txt_name = QtWidgets.QLineEdit(self.tab_connection)
self.txt_name.setObjectName("txt_name")
self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.txt_name)
self.lbl_address = QtWidgets.QLabel(self.tab_connection)
self.lbl_address.setObjectName("lbl_address")
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.lbl_address)
self.txt_address = QtWidgets.QLineEdit(self.tab_connection)
self.txt_address.setObjectName("txt_address")
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txt_address)
self.lbl_port = QtWidgets.QLabel(self.tab_connection)
self.lbl_port.setObjectName("lbl_port")
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_port)
self.sbx_port = QtWidgets.QSpinBox(self.tab_connection)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sbx_port.sizePolicy().hasHeightForWidth())
self.sbx_port.setSizePolicy(sizePolicy)
self.sbx_port.setMinimum(1)
self.sbx_port.setMaximum(65535)
self.sbx_port.setProperty("value", 55123)
self.sbx_port.setObjectName("sbx_port")
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.sbx_port)
self.lbl_timeout = QtWidgets.QLabel(self.tab_connection)
self.lbl_timeout.setObjectName("lbl_timeout")
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lbl_timeout)
self.sbx_timeout = QtWidgets.QSpinBox(self.tab_connection)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sbx_timeout.sizePolicy().hasHeightForWidth())
self.sbx_timeout.setSizePolicy(sizePolicy)
self.sbx_timeout.setMinimum(5)
self.sbx_timeout.setMaximum(30)
self.sbx_timeout.setObjectName("sbx_timeout")
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.sbx_timeout)
self.lbl_folder = QtWidgets.QLabel(self.tab_connection)
self.lbl_folder.setObjectName("lbl_folder")
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.lbl_folder)
self.cbb_folder = QtWidgets.QComboBox(self.tab_connection)
self.cbb_folder.setEditable(True)
self.cbb_folder.setObjectName("cbb_folder")
self.cbb_folder.addItem("")
self.cbb_folder.setItemText(0, "")
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.cbb_folder)
self.tab_properties.addTab(self.tab_connection, "")
self.tab_ssh = QtWidgets.QWidget()
self.tab_ssh.setObjectName("tab_ssh")
self.formLayout = QtWidgets.QFormLayout(self.tab_ssh)
self.formLayout.setObjectName("formLayout")
self.lbl_ssh_use_tunnel = QtWidgets.QLabel(self.tab_ssh)
self.lbl_ssh_use_tunnel.setObjectName("lbl_ssh_use_tunnel")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lbl_ssh_use_tunnel)
self.cbx_ssh_use_tunnel = QtWidgets.QCheckBox(self.tab_ssh)
self.cbx_ssh_use_tunnel.setText("")
self.cbx_ssh_use_tunnel.setChecked(True)
self.cbx_ssh_use_tunnel.setObjectName("cbx_ssh_use_tunnel")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.cbx_ssh_use_tunnel)
self.lbl_ssh_port = QtWidgets.QLabel(self.tab_ssh)
self.lbl_ssh_port.setObjectName("lbl_ssh_port")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.lbl_ssh_port)
self.sbx_ssh_port = QtWidgets.QSpinBox(self.tab_ssh)
self.sbx_ssh_port.setMaximum(65535)
self.sbx_ssh_port.setProperty("value", 22)
self.sbx_ssh_port.setObjectName("sbx_ssh_port")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.sbx_ssh_port)
self.lbl_ssh_user = QtWidgets.QLabel(self.tab_ssh)
self.lbl_ssh_user.setObjectName("lbl_ssh_user")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_ssh_user)
self.txt_ssh_user = QtWidgets.QLineEdit(self.tab_ssh)
self.txt_ssh_user.setText("pi")
self.txt_ssh_user.setObjectName("txt_ssh_user")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.txt_ssh_user)
self.tab_properties.addTab(self.tab_ssh, "")
self.gridLayout.addWidget(self.tab_properties, 1, 0, 1, 2)
self.btn_box = QtWidgets.QDialogButtonBox(diag_connections)
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Discard|QtWidgets.QDialogButtonBox.Save)
self.btn_box.setObjectName("btn_box")
self.gridLayout.addWidget(self.btn_box, 2, 0, 1, 2)
self.tre_connections = QtWidgets.QTreeWidget(diag_connections) self.tre_connections = QtWidgets.QTreeWidget(diag_connections)
self.tre_connections.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.tre_connections.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tre_connections.setObjectName("tre_connections") self.tre_connections.setObjectName("tre_connections")
@@ -54,66 +143,9 @@ class Ui_diag_connections(object):
self.btn_add.setObjectName("btn_add") self.btn_add.setObjectName("btn_add")
self.vl_edit.addWidget(self.btn_add) self.vl_edit.addWidget(self.btn_add)
self.gridLayout.addLayout(self.vl_edit, 0, 1, 1, 1) self.gridLayout.addLayout(self.vl_edit, 0, 1, 1, 1)
self.gb_properties = QtWidgets.QGroupBox(diag_connections)
self.gb_properties.setObjectName("gb_properties")
self.formLayout = QtWidgets.QFormLayout(self.gb_properties)
self.formLayout.setObjectName("formLayout")
self.lbl_name = QtWidgets.QLabel(self.gb_properties)
self.lbl_name.setObjectName("lbl_name")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lbl_name)
self.lbl_folder = QtWidgets.QLabel(self.gb_properties)
self.lbl_folder.setObjectName("lbl_folder")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.lbl_folder)
self.lbl_address = QtWidgets.QLabel(self.gb_properties)
self.lbl_address.setObjectName("lbl_address")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.lbl_address)
self.lbl_port = QtWidgets.QLabel(self.gb_properties)
self.lbl_port.setObjectName("lbl_port")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_port)
self.txt_name = QtWidgets.QLineEdit(self.gb_properties)
self.txt_name.setObjectName("txt_name")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.txt_name)
self.txt_address = QtWidgets.QLineEdit(self.gb_properties)
self.txt_address.setObjectName("txt_address")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txt_address)
self.sbx_port = QtWidgets.QSpinBox(self.gb_properties)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sbx_port.sizePolicy().hasHeightForWidth())
self.sbx_port.setSizePolicy(sizePolicy)
self.sbx_port.setMinimum(1)
self.sbx_port.setMaximum(65535)
self.sbx_port.setProperty("value", 55123)
self.sbx_port.setObjectName("sbx_port")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.sbx_port)
self.cbb_folder = QtWidgets.QComboBox(self.gb_properties)
self.cbb_folder.setEditable(True)
self.cbb_folder.setObjectName("cbb_folder")
self.cbb_folder.addItem("")
self.cbb_folder.setItemText(0, "")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.cbb_folder)
self.lbl_timeout = QtWidgets.QLabel(self.gb_properties)
self.lbl_timeout.setObjectName("lbl_timeout")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lbl_timeout)
self.sbx_timeout = QtWidgets.QSpinBox(self.gb_properties)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.sbx_timeout.sizePolicy().hasHeightForWidth())
self.sbx_timeout.setSizePolicy(sizePolicy)
self.sbx_timeout.setMinimum(5)
self.sbx_timeout.setMaximum(30)
self.sbx_timeout.setObjectName("sbx_timeout")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.sbx_timeout)
self.gridLayout.addWidget(self.gb_properties, 1, 0, 1, 2)
self.btn_box = QtWidgets.QDialogButtonBox(diag_connections)
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Discard|QtWidgets.QDialogButtonBox.Save)
self.btn_box.setObjectName("btn_box")
self.gridLayout.addWidget(self.btn_box, 2, 0, 1, 2)
self.retranslateUi(diag_connections) self.retranslateUi(diag_connections)
self.tab_properties.setCurrentIndex(0)
self.btn_box.accepted.connect(diag_connections.accept) # type: ignore self.btn_box.accepted.connect(diag_connections.accept) # type: ignore
self.btn_box.rejected.connect(diag_connections.reject) # type: ignore self.btn_box.rejected.connect(diag_connections.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(diag_connections) QtCore.QMetaObject.connectSlotsByName(diag_connections)
@@ -121,15 +153,19 @@ class Ui_diag_connections(object):
def retranslateUi(self, diag_connections): def retranslateUi(self, diag_connections):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
diag_connections.setWindowTitle(_translate("diag_connections", "Revolution Pi connections")) diag_connections.setWindowTitle(_translate("diag_connections", "Revolution Pi connections"))
self.tre_connections.headerItem().setText(0, _translate("diag_connections", "Connection name"))
self.tre_connections.headerItem().setText(1, _translate("diag_connections", "Address"))
self.gb_properties.setTitle(_translate("diag_connections", "Connection properties"))
self.lbl_name.setText(_translate("diag_connections", "Display name:")) self.lbl_name.setText(_translate("diag_connections", "Display name:"))
self.lbl_folder.setText(_translate("diag_connections", "Sub folder:"))
self.lbl_address.setText(_translate("diag_connections", "Address (DNS/IP):")) self.lbl_address.setText(_translate("diag_connections", "Address (DNS/IP):"))
self.lbl_port.setText(_translate("diag_connections", "Port (Default {0}):")) self.lbl_port.setText(_translate("diag_connections", "Port (Default {0}):"))
self.lbl_timeout.setText(_translate("diag_connections", "Connection timeout:")) self.lbl_timeout.setText(_translate("diag_connections", "Connection timeout:"))
self.sbx_timeout.setSuffix(_translate("diag_connections", " sec.")) self.sbx_timeout.setSuffix(_translate("diag_connections", " sec."))
self.lbl_folder.setText(_translate("diag_connections", "Sub folder:"))
self.tab_properties.setTabText(self.tab_properties.indexOf(self.tab_connection), _translate("diag_connections", "Connection"))
self.lbl_ssh_use_tunnel.setText(_translate("diag_connections", "Connect over SSH tunnel:"))
self.lbl_ssh_port.setText(_translate("diag_connections", "SSH port:"))
self.lbl_ssh_user.setText(_translate("diag_connections", "SSH user name:"))
self.tab_properties.setTabText(self.tab_properties.indexOf(self.tab_ssh), _translate("diag_connections", "Over SSH"))
self.tre_connections.headerItem().setText(0, _translate("diag_connections", "Connection name"))
self.tre_connections.headerItem().setText(1, _translate("diag_connections", "Address"))
from . import ressources_rc from . import ressources_rc

View File

@@ -6,14 +6,184 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>520</width> <width>496</width>
<height>508</height> <height>569</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Revolution Pi connections</string> <string>Revolution Pi connections</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="2">
<widget class="QTabWidget" name="tab_properties">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_connection">
<attribute name="title">
<string>Connection</string>
</attribute>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="lbl_name">
<property name="text">
<string>Display name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txt_name"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbl_address">
<property name="text">
<string>Address (DNS/IP):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txt_address"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_port">
<property name="text">
<string>Port (Default {0}):</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="sbx_port">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>55123</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lbl_timeout">
<property name="text">
<string>Connection timeout:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="sbx_timeout">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> sec.</string>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>30</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lbl_folder">
<property name="text">
<string>Sub folder:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cbb_folder">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string/>
</property>
</item>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_ssh">
<attribute name="title">
<string>Over SSH</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="lbl_ssh_use_tunnel">
<property name="text">
<string>Connect over SSH tunnel:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="cbx_ssh_use_tunnel">
<property name="text">
<string notr="true"/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbl_ssh_port">
<property name="text">
<string>SSH port:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="sbx_ssh_port">
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>22</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_ssh_user">
<property name="text">
<string>SSH user name:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="txt_ssh_user">
<property name="text">
<string notr="true">pi</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QDialogButtonBox" name="btn_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Discard|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QTreeWidget" name="tre_connections"> <widget class="QTreeWidget" name="tre_connections">
<property name="editTriggers"> <property name="editTriggers">
@@ -92,116 +262,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="gb_properties">
<property name="title">
<string>Connection properties</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="lbl_name">
<property name="text">
<string>Display name:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lbl_folder">
<property name="text">
<string>Sub folder:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lbl_address">
<property name="text">
<string>Address (DNS/IP):</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lbl_port">
<property name="text">
<string>Port (Default {0}):</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txt_name"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txt_address"/>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="sbx_port">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>55123</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cbb_folder">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string/>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lbl_timeout">
<property name="text">
<string>Connection timeout:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="sbx_timeout">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> sec.</string>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>30</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QDialogButtonBox" name="btn_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Discard|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources> <resources>