diff --git a/src/revpicommander/helper.py b/src/revpicommander/helper.py
index aa63738..d2f83c4 100644
--- a/src/revpicommander/helper.py
+++ b/src/revpicommander/helper.py
@@ -14,9 +14,12 @@ from queue import Queue
from threading import Lock
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 .ssh_tunneling.server import SSHLocalTunnel
+from .sshauth import SSHAuth, SSHAuthType
class WidgetData(IntEnum):
@@ -40,6 +43,9 @@ class WidgetData(IntEnum):
watch_files = 310
watch_path = 311
debug_geos = 312
+ ssh_use_tunnel = 313
+ ssh_port = 315
+ ssh_user = 316
class ConnectionManager(QtCore.QThread):
@@ -55,6 +61,8 @@ class ConnectionManager(QtCore.QThread):
"""This will be triggered, if a connection error was detected."""
status_changed = QtCore.pyqtSignal(str, str)
"""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):
super(ConnectionManager, self).__init__(parent)
@@ -71,6 +79,12 @@ class ConnectionManager(QtCore.QThread):
self.name = ""
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
self.program_last_dir_upload = ""
self.program_last_file_upload = ""
@@ -167,6 +181,12 @@ class ConnectionManager(QtCore.QThread):
self.address = ""
self.name = ""
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.xml_funcs.clear()
self.xml_mode = -1
@@ -207,11 +227,12 @@ class ConnectionManager(QtCore.QThread):
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.
:param settings_index: Index of settings array 'connections'
+ :param parent: Qt parent window for dialog positioning
:return: True, if the connection was successfully established
"""
@@ -226,6 +247,13 @@ class ConnectionManager(QtCore.QThread):
port = settings.value("port", 55123, 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_file_upload = settings.value("last_file_upload", ".", str)
self.program_last_dir_pictory = settings.value("last_dir_pictory", ".", str)
@@ -241,7 +269,42 @@ class ConnectionManager(QtCore.QThread):
settings.endArray()
socket.setdefaulttimeout(2)
- sp = ServerProxy("http://{0}:{1}".format(address, port))
+
+ 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))
# Load values and test connection to Revolution Pi
try:
@@ -251,19 +314,41 @@ class ConnectionManager(QtCore.QThread):
except Exception as e:
pi.logger.exception(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
self.address = address
self.name = name
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.xml_funcs = xml_funcs
self.xml_mode = xml_mode
with self._lck_cli:
socket.setdefaulttimeout(timeout)
+ self.ssh_tunnel_server = ssh_tunnel_server
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()
@@ -286,7 +371,7 @@ class ConnectionManager(QtCore.QThread):
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._save_settings()
@@ -299,6 +384,10 @@ class ConnectionManager(QtCore.QThread):
self._clear_settings()
self._cli = None
+ if self.ssh_tunnel_server:
+ self.ssh_tunnel_server.disconnect()
+ self.ssh_tunnel_server = None
+
self.connection_disconnected.emit()
def pyload_simulate(self, configrsc: str, procimg: str, clean_existing: bool):
@@ -383,6 +472,23 @@ class ConnectionManager(QtCore.QThread):
pi.logger.warning(e)
self.status_changed.emit(self.tr("SERVER ERROR"), "red")
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:
if plc_exit_code == -1:
self.status_changed.emit(self.tr("RUNNING"), "green")
@@ -452,11 +558,17 @@ class ConnectionManager(QtCore.QThread):
return default_value
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))
- else:
- return None
+ 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
@property
def connected(self) -> bool:
diff --git a/src/revpicommander/revpicommander.py b/src/revpicommander/revpicommander.py
index c6496d9..77a28b5 100644
--- a/src/revpicommander/revpicommander.py
+++ b/src/revpicommander/revpicommander.py
@@ -5,7 +5,7 @@
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "GPLv3"
-__version__ = "0.9.3"
+__version__ = "0.9.10rc1"
import webbrowser
from os.path import basename, dirname, join
@@ -97,17 +97,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
# region # REGION: Connection management
def _pyload_connect(self, settings_index: int) -> None:
- if not helper.cm.pyload_connect(settings_index):
- 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!"
- )
- )
+ helper.cm.pyload_connect(settings_index, self)
@QtCore.pyqtSlot(str)
def on_cm_connection_error_observed(self, message: str):
@@ -192,8 +182,12 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
else:
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.setText(helper.settings.value("name"))
+ act.setText(display_name)
act.setData(i)
act.setToolTip("{0}:{1}".format(
helper.settings.value("address"),
diff --git a/src/revpicommander/revpiplclist.py b/src/revpicommander/revpiplclist.py
index 029eeb1..2df72bc 100644
--- a/src/revpicommander/revpiplclist.py
+++ b/src/revpicommander/revpiplclist.py
@@ -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.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_file_upload, settings.value("last_file_upload"))
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.watch_files, settings.value("watch_files"))
con_item.setData(0, WidgetData.watch_path, settings.value("watch_path"))
- con_item.setData(0, WidgetData.debug_geos, settings.value("debug_geos"))
+ try:
+ # Bytes with QSettings are a little difficult sometimes
+ 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)
if folder:
@@ -95,6 +104,9 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
settings.setValue("name", node.text(0))
settings.setValue("port", node.data(0, WidgetData.port))
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):
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.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):
"""Find the folder entry by name."""
for i in range(self.tre_connections.topLevelItemCount()):
@@ -241,6 +257,11 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.cbb_folder.setCurrentIndex(0)
else:
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:
self.__current_item = current
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.setData(0, WidgetData.port, self.__default_port)
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())
if sub_folder:
sub_folder.addChild(self.__current_item)
@@ -312,6 +336,24 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
return
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)
def on_cbb_folder_editTextChanged(self, text: str):
pi.logger.debug("RevPiPlcList.on_cbb_folder_editTextChanged({0})".format(text))
diff --git a/src/revpicommander/ui/revpiplclist_ui.py b/src/revpicommander/ui/revpiplclist_ui.py
index b3ead82..dfc30cd 100644
--- a/src/revpicommander/ui/revpiplclist_ui.py
+++ b/src/revpicommander/ui/revpiplclist_ui.py
@@ -14,9 +14,98 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_diag_connections(object):
def setupUi(self, 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.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.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tre_connections.setObjectName("tre_connections")
@@ -54,66 +143,9 @@ class Ui_diag_connections(object):
self.btn_add.setObjectName("btn_add")
self.vl_edit.addWidget(self.btn_add)
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.tab_properties.setCurrentIndex(0)
self.btn_box.accepted.connect(diag_connections.accept) # type: ignore
self.btn_box.rejected.connect(diag_connections.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(diag_connections)
@@ -121,15 +153,19 @@ class Ui_diag_connections(object):
def retranslateUi(self, diag_connections):
_translate = QtCore.QCoreApplication.translate
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_folder.setText(_translate("diag_connections", "Sub folder:"))
self.lbl_address.setText(_translate("diag_connections", "Address (DNS/IP):"))
self.lbl_port.setText(_translate("diag_connections", "Port (Default {0}):"))
self.lbl_timeout.setText(_translate("diag_connections", "Connection timeout:"))
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
diff --git a/ui_dev/revpiplclist.ui b/ui_dev/revpiplclist.ui
index 1f9b2ea..d75954d 100644
--- a/ui_dev/revpiplclist.ui
+++ b/ui_dev/revpiplclist.ui
@@ -6,14 +6,184 @@
0
0
- 520
- 508
+ 496
+ 569
Revolution Pi connections
+ -
+
+
+ 0
+
+
+
+ Connection
+
+
+
-
+
+
+ Display name:
+
+
+
+ -
+
+
+ -
+
+
+ Address (DNS/IP):
+
+
+
+ -
+
+
+ -
+
+
+ Port (Default {0}):
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1
+
+
+ 65535
+
+
+ 55123
+
+
+
+ -
+
+
+ Connection timeout:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ sec.
+
+
+ 5
+
+
+ 30
+
+
+
+ -
+
+
+ Sub folder:
+
+
+
+ -
+
+
+ true
+
+
-
+
+
+
+
+
+
+
+
+
+
+ Over SSH
+
+
+ -
+
+
+ Connect over SSH tunnel:
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ SSH port:
+
+
+
+ -
+
+
+ 65535
+
+
+ 22
+
+
+
+ -
+
+
+ SSH user name:
+
+
+
+ -
+
+
+ pi
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Discard|QDialogButtonBox::Save
+
+
+
-
@@ -92,116 +262,6 @@
- -
-
-
- Connection properties
-
-
-
-
-
-
- Display name:
-
-
-
- -
-
-
- Sub folder:
-
-
-
- -
-
-
- Address (DNS/IP):
-
-
-
- -
-
-
- Port (Default {0}):
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 1
-
-
- 65535
-
-
- 55123
-
-
-
- -
-
-
- true
-
-
-
-
-
-
-
-
-
- -
-
-
- Connection timeout:
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- sec.
-
-
- 5
-
-
- 30
-
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::Discard|QDialogButtonBox::Save
-
-
-