mirror of
https://github.com/naruxde/revpicommander.git
synced 2025-11-09 00:53:53 +01:00
Configure and start ssh tunneled connections
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -6,14 +6,184 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>508</height>
|
||||
<width>496</width>
|
||||
<height>569</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Revolution Pi connections</string>
|
||||
</property>
|
||||
<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">
|
||||
<widget class="QTreeWidget" name="tre_connections">
|
||||
<property name="editTriggers">
|
||||
@@ -92,116 +262,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
</widget>
|
||||
<resources>
|
||||
|
||||
Reference in New Issue
Block a user