mirror of
https://github.com/naruxde/revpicommander.git
synced 2025-11-08 16:43:53 +01:00
New async connection management with user feedback
The fist connection uses the configured timeout of the revpi settings object. While the first connection, the user will see a connecting dialog, to show we are alive and are working. The old behavior was a freezing UI.
This commit is contained in:
@@ -18,7 +18,8 @@ class BackgroundWorker(QtCore.QThread):
|
|||||||
steps_done = QtCore.pyqtSignal(int)
|
steps_done = QtCore.pyqtSignal(int)
|
||||||
status_message = QtCore.pyqtSignal(str)
|
status_message = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None, interruption_text: str = None):
|
||||||
|
self._interruption_text = interruption_text or self.tr("User requested cancellation...")
|
||||||
super(BackgroundWorker, self).__init__(parent)
|
super(BackgroundWorker, self).__init__(parent)
|
||||||
|
|
||||||
def check_cancel(self) -> bool:
|
def check_cancel(self) -> bool:
|
||||||
@@ -28,33 +29,67 @@ class BackgroundWorker(QtCore.QThread):
|
|||||||
:return: True, if interruption was requested
|
:return: True, if interruption was requested
|
||||||
"""
|
"""
|
||||||
if self.isInterruptionRequested():
|
if self.isInterruptionRequested():
|
||||||
self.status_message.emit(self.tr("User requested cancellation..."))
|
self.status_message.emit(self._interruption_text)
|
||||||
self.msleep(750)
|
self.msleep(750)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def exec_dialog(self) -> int:
|
def exec_dialog(self, window_title="", can_cancel=True) -> int:
|
||||||
|
"""
|
||||||
|
Show dialog with progress bar.
|
||||||
|
|
||||||
|
:param window_title: Title of Dialog window
|
||||||
|
:param can_cancel: If False, the cancel button is deactivated
|
||||||
|
:return: Dialog result
|
||||||
|
"""
|
||||||
diag = WorkerDialog(self, self.parent())
|
diag = WorkerDialog(self, self.parent())
|
||||||
|
diag.setWindowTitle(window_title)
|
||||||
|
diag.btn_box.setEnabled(can_cancel)
|
||||||
rc = diag.exec()
|
rc = diag.exec()
|
||||||
diag.deleteLater()
|
diag.deleteLater()
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
def wait_interruptable(self, seconds=-1) -> None:
|
def wait_interruptable(self, seconds=-1) -> bool:
|
||||||
"""Save function to wait and get the cancel buttons."""
|
"""
|
||||||
|
Save function to wait and get the cancel buttons.
|
||||||
|
|
||||||
|
:param seconds: Wait this amount of seconds
|
||||||
|
:return: True, if interruption was requested
|
||||||
|
"""
|
||||||
counter = seconds * 4
|
counter = seconds * 4
|
||||||
while counter != 0:
|
while counter != 0:
|
||||||
counter -= 1
|
counter -= 1
|
||||||
self.msleep(250)
|
self.msleep(250)
|
||||||
if self.check_cancel():
|
if self.check_cancel():
|
||||||
break
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Worker thread to import pictures from camera."""
|
"""Override this function with your logic."""
|
||||||
log.debug("BackgroundWorker.run")
|
raise NotImplementedError()
|
||||||
self.status_message.emit("Started dummy thread...")
|
|
||||||
self.wait_interruptable(5)
|
|
||||||
self.status_message.emit("Completed dummy thread.")
|
class BackgroundWaiter(BackgroundWorker):
|
||||||
self.wait_interruptable(2)
|
"""Just wait an amount of time and show progress bar."""
|
||||||
|
|
||||||
|
def __init__(self, seconds: int, status_message: str, parent=None, interruption_text: str = None):
|
||||||
|
self._status_message = status_message
|
||||||
|
self._wait_steps = seconds * 4
|
||||||
|
super().__init__(parent, interruption_text)
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
log.debug("BackgroundWaiter.run")
|
||||||
|
self.steps_todo.emit(self._wait_steps)
|
||||||
|
self.status_message.emit(self._status_message)
|
||||||
|
counter = 0
|
||||||
|
while counter <= self._wait_steps:
|
||||||
|
counter += 1
|
||||||
|
self.msleep(250)
|
||||||
|
if self.isInterruptionRequested():
|
||||||
|
self.steps_done.emit(self._wait_steps)
|
||||||
|
if self.check_cancel():
|
||||||
|
return
|
||||||
|
self.steps_done.emit(counter)
|
||||||
|
|
||||||
|
|
||||||
class WorkerDialog(QtWidgets.QDialog, Ui_diag_backgroundworker):
|
class WorkerDialog(QtWidgets.QDialog, Ui_diag_backgroundworker):
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ from threading import Lock
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from xmlrpc.client import Binary, ServerProxy
|
from xmlrpc.client import Binary, ServerProxy
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
from PyQt5 import QtCore
|
||||||
from paramiko.ssh_exception import AuthenticationException
|
from paramiko.ssh_exception import AuthenticationException
|
||||||
|
|
||||||
from . import proginit as pi
|
from . import proginit as pi
|
||||||
from .ssh_tunneling.server import SSHLocalTunnel
|
from .ssh_tunneling.server import SSHLocalTunnel
|
||||||
from .sshauth import SSHAuth
|
|
||||||
|
|
||||||
settings = QtCore.QSettings("revpimodio.org", "RevPiCommander")
|
settings = QtCore.QSettings("revpimodio.org", "RevPiCommander")
|
||||||
"""Global application settings."""
|
"""Global application settings."""
|
||||||
@@ -29,6 +28,12 @@ homedir = environ.get("HOME", "") or environ.get("APPDATA", "")
|
|||||||
"""Home dir of user."""
|
"""Home dir of user."""
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionFail(IntEnum):
|
||||||
|
NO_XML_RPC = 1
|
||||||
|
SSH_CONNECT = 2
|
||||||
|
SSH_AUTH = 4
|
||||||
|
|
||||||
|
|
||||||
class WidgetData(IntEnum):
|
class WidgetData(IntEnum):
|
||||||
address = 260
|
address = 260
|
||||||
acl_level = 262
|
acl_level = 262
|
||||||
@@ -194,6 +199,8 @@ class RevPiSettings:
|
|||||||
class ConnectionManager(QtCore.QThread):
|
class ConnectionManager(QtCore.QThread):
|
||||||
"""Check connection and status for PLC program on Revolution Pi."""
|
"""Check connection and status for PLC program on Revolution Pi."""
|
||||||
|
|
||||||
|
connect_error = QtCore.pyqtSignal(str, str, ConnectionFail, RevPiSettings)
|
||||||
|
"""Error header, message and reason (ConnectionFail) of a new connection after pyload_connect call."""
|
||||||
connection_established = QtCore.pyqtSignal()
|
connection_established = QtCore.pyqtSignal()
|
||||||
"""New connection established successfully with <class 'ServerProxy'>."""
|
"""New connection established successfully with <class 'ServerProxy'>."""
|
||||||
connection_disconnected = QtCore.pyqtSignal()
|
connection_disconnected = QtCore.pyqtSignal()
|
||||||
@@ -311,12 +318,12 @@ class ConnectionManager(QtCore.QThread):
|
|||||||
self.xml_funcs.clear()
|
self.xml_funcs.clear()
|
||||||
self.xml_mode = -1
|
self.xml_mode = -1
|
||||||
|
|
||||||
def pyload_connect(self, revpi_settings: RevPiSettings, parent=None) -> bool:
|
def pyload_connect(self, revpi_settings: RevPiSettings, ssh_pass="") -> bool:
|
||||||
"""
|
"""
|
||||||
Create a new connection from settings object.
|
Create a new connection from settings object.
|
||||||
|
|
||||||
:param revpi_settings: Revolution Pi saved connection settings
|
:param revpi_settings: Revolution Pi saved connection settings
|
||||||
:param parent: Qt parent window for dialog positioning
|
:param ssh_pass: Use this ssh password, if revpi_settings.ssh_use_tunnel is true
|
||||||
:return: True, if the connection was successfully established
|
:return: True, if the connection was successfully established
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -325,52 +332,38 @@ class ConnectionManager(QtCore.QThread):
|
|||||||
|
|
||||||
ssh_tunnel_server = None
|
ssh_tunnel_server = None
|
||||||
ssh_tunnel_port = 0
|
ssh_tunnel_port = 0
|
||||||
ssh_pass = ""
|
|
||||||
|
|
||||||
socket.setdefaulttimeout(2)
|
socket.setdefaulttimeout(revpi_settings.timeout)
|
||||||
|
|
||||||
if revpi_settings.ssh_use_tunnel:
|
if revpi_settings.ssh_use_tunnel:
|
||||||
while True:
|
ssh_tunnel_server = SSHLocalTunnel(
|
||||||
diag_ssh_auth = SSHAuth(
|
revpi_settings.port,
|
||||||
revpi_settings.ssh_user,
|
revpi_settings.address,
|
||||||
"{0}.{1}_{2}".format(
|
revpi_settings.ssh_port
|
||||||
settings.applicationName(),
|
)
|
||||||
settings.organizationName(),
|
try:
|
||||||
revpi_settings.internal_id),
|
ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(revpi_settings.ssh_user, ssh_pass)
|
||||||
parent,
|
except AuthenticationException:
|
||||||
|
self.connect_error.emit(
|
||||||
|
self.tr("Error"), self.tr(
|
||||||
|
"The combination of username and password was rejected from the SSH server.\n\n"
|
||||||
|
"Try again."
|
||||||
|
),
|
||||||
|
ConnectionFail.SSH_AUTH,
|
||||||
|
revpi_settings,
|
||||||
)
|
)
|
||||||
if not diag_ssh_auth.exec() == QtWidgets.QDialog.Accepted:
|
return False
|
||||||
self._clear_settings()
|
except Exception as e:
|
||||||
return False
|
# todo: Check some more kinds of exceptions and nice user info
|
||||||
|
self._clear_settings()
|
||||||
ssh_user = diag_ssh_auth.username
|
self.connect_error.emit(
|
||||||
ssh_pass = diag_ssh_auth.password
|
self.tr("Error"), self.tr(
|
||||||
ssh_tunnel_server = SSHLocalTunnel(
|
"Could not establish a SSH connection to server:\n\n{0}"
|
||||||
revpi_settings.port,
|
).format(str(e)),
|
||||||
revpi_settings.address,
|
ConnectionFail.SSH_CONNECT,
|
||||||
revpi_settings.ssh_port
|
revpi_settings,
|
||||||
)
|
)
|
||||||
revpi_settings.ssh_saved_password = diag_ssh_auth.in_keyring
|
return False
|
||||||
try:
|
|
||||||
ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(ssh_user, ssh_pass)
|
|
||||||
break
|
|
||||||
except AuthenticationException:
|
|
||||||
diag_ssh_auth.remove_saved_password()
|
|
||||||
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))
|
sp = ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
|
||||||
|
|
||||||
@@ -386,17 +379,30 @@ class ConnectionManager(QtCore.QThread):
|
|||||||
pi.logger.exception(e)
|
pi.logger.exception(e)
|
||||||
self.connection_error_observed.emit(str(e))
|
self.connection_error_observed.emit(str(e))
|
||||||
|
|
||||||
if not revpi_settings.ssh_use_tunnel:
|
if revpi_settings.ssh_use_tunnel:
|
||||||
|
self.connect_error.emit(
|
||||||
|
self.tr("Error"), self.tr(
|
||||||
|
"Can not connect to RevPi XML-RPC Service through SSH tunnel! \n\n"
|
||||||
|
"This could have the following reasons: The XML-RPC service is not "
|
||||||
|
"running / not bind to localhost or the ACL permission is not set for "
|
||||||
|
"127.0.0.1!!!"
|
||||||
|
),
|
||||||
|
ConnectionFail.NO_XML_RPC,
|
||||||
|
revpi_settings,
|
||||||
|
)
|
||||||
|
else:
|
||||||
# todo: Change message, that user can use ssh
|
# todo: Change message, that user can use ssh
|
||||||
QtWidgets.QMessageBox.critical(
|
self.connect_error.emit(
|
||||||
parent, self.tr("Error"), self.tr(
|
self.tr("Error"), self.tr(
|
||||||
"Can not connect to RevPi XML-RPC Service! \n\n"
|
"Can not connect to RevPi XML-RPC Service! \n\n"
|
||||||
"This could have the following reasons: The RevPi is not "
|
"This could have the following reasons: The RevPi is not "
|
||||||
"online, the XML-RPC service is not running / bind to "
|
"online, the XML-RPC service is not running / bind to "
|
||||||
"localhost or the ACL permission is not set for your "
|
"localhost or the ACL permission is not set for your "
|
||||||
"IP!!!\n\nRun 'sudo revpipyload_secure_installation' on "
|
"IP!!!\n\nRun 'sudo revpipyload_secure_installation' on "
|
||||||
"Revolution Pi to setup this function!"
|
"Revolution Pi to setup this function!"
|
||||||
)
|
),
|
||||||
|
ConnectionFail.NO_XML_RPC,
|
||||||
|
revpi_settings,
|
||||||
)
|
)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@@ -408,7 +414,6 @@ class ConnectionManager(QtCore.QThread):
|
|||||||
self.xml_mode = xml_mode
|
self.xml_mode = xml_mode
|
||||||
|
|
||||||
with self._lck_cli:
|
with self._lck_cli:
|
||||||
socket.setdefaulttimeout(revpi_settings.timeout)
|
|
||||||
self.ssh_tunnel_server = ssh_tunnel_server
|
self.ssh_tunnel_server = ssh_tunnel_server
|
||||||
self._cli = sp
|
self._cli = sp
|
||||||
self._cli_connect.put_nowait((
|
self._cli_connect.put_nowait((
|
||||||
@@ -686,7 +691,8 @@ def import_old_settings():
|
|||||||
revpi_setting = RevPiSettings(i, settings_storage=old_settings)
|
revpi_setting = RevPiSettings(i, settings_storage=old_settings)
|
||||||
revpi_setting._settings = settings
|
revpi_setting._settings = settings
|
||||||
revpi_setting.save_settings()
|
revpi_setting.save_settings()
|
||||||
except Exception as e:
|
except Exception:
|
||||||
pi.logger.warning("Could not import saved connection {0}".format(i))
|
pi.logger.warning("Could not import saved connection {0}".format(i))
|
||||||
|
|
||||||
|
|
||||||
import_old_settings()
|
import_old_settings()
|
||||||
|
|||||||
Binary file not shown.
@@ -93,70 +93,198 @@ Nicht gespeicherte Änderunen gehen verloren</translation>
|
|||||||
<translation type="obsolete">Der ausgewählte RevPi ist schon in der Verbindungsliste als '{0}'.</translation>
|
<translation type="obsolete">Der ausgewählte RevPi ist schon in der Verbindungsliste als '{0}'.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../avahisearch.py" line="283"/>
|
<location filename="../avahisearch.py" line="285"/>
|
||||||
<source> over SSH</source>
|
<source> over SSH</source>
|
||||||
<translation> über SSH</translation>
|
<translation> über SSH</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ConnectingPyload</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="299"/>
|
||||||
|
<source>Simulator started...</source>
|
||||||
|
<translation type="obsolete">Simulator gestartet...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="299"/>
|
||||||
|
<source>The simulator is running!
|
||||||
|
|
||||||
|
You can work with this simulator if your call RevPiModIO with this additional parameters:
|
||||||
|
procimg={0}
|
||||||
|
configrsc={1}
|
||||||
|
|
||||||
|
You can copy that from header textbox.</source>
|
||||||
|
<translation type="obsolete">Der Simulator läuft!
|
||||||
|
|
||||||
|
Du kannst mit der Simulation arbeiten, wenn du RevPiModIO mit diesen zusätzlichen Parametern instantiierst:
|
||||||
|
procimg={0}
|
||||||
|
configrsc={1}
|
||||||
|
|
||||||
|
Dies kann aus der Textbox oben kopiert werden.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="308"/>
|
||||||
|
<source>Can not start...</source>
|
||||||
|
<translation type="obsolete">Kann nicht gestartet werden...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="308"/>
|
||||||
|
<source>Can not start the simulator! Maybe the piCtory file is corrupt or you have no write permissions for '{0}'.</source>
|
||||||
|
<translation type="obsolete">Kann Simulator nicht starten! Vielleicht ist die piCtory Datei defekt oder es gibt keine Schreibberechtigung für '{0}'.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="501"/>
|
||||||
|
<source>Warning</source>
|
||||||
|
<translation type="obsolete">Warnung</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="324"/>
|
||||||
|
<source>This version of Logviewer ist not supported in version {0} of RevPiPyLoad on your RevPi! You need at least version 0.4.1.</source>
|
||||||
|
<translation type="obsolete">Diese Version vom Logbetrachter wird in RevPiPyLoad Version {0} nicht unterstützt! Es wird mindestens Version 0.4.1 benötigt.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="374"/>
|
||||||
|
<source>XML-RPC access mode in the RevPiPyLoad configuration is too small to access this dialog!</source>
|
||||||
|
<translation type="obsolete">XML-RPC Zugriffsberechtigung in der RevPiPyLoad Konfiguraiton ist zu klein für diese Einstellungen!</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="528"/>
|
||||||
|
<source>Error</source>
|
||||||
|
<translation type="obsolete">Fehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="354"/>
|
||||||
|
<source>The Version of RevPiPyLoad on your Revolution Pi ({0}) is to old. This Version of RevPiCommander require at least version 0.6.0 of RevPiPyLoad. Please update your Revolution Pi!</source>
|
||||||
|
<translation type="obsolete">Die Version von RevPiPyLoad ({0}) auf dem Revolution Pi ist zu alt. Diese Version vom RevPiCommander braucht mindestens Version 0.6.0. Bitte aktualisiere deinen Revolution Pi!</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="407"/>
|
||||||
|
<source>Question</source>
|
||||||
|
<translation type="obsolete">Frage</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="407"/>
|
||||||
|
<source>Are you sure to reset piControl?
|
||||||
|
The pictory configuration will be reloaded. During that time the process image will be interrupted and could rise errors on running control programs!</source>
|
||||||
|
<translation type="obsolete">Soll piControl wirklich zurückgesetzt werden?
|
||||||
|
Die piCtory Konfiguration wird neu geladen. Das Prozessabbild wird in dieser Zeit nicht verfügbar sein und es könnten Fehler in Steuerungsprogrammen ausgelöst werden!</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="420"/>
|
||||||
|
<source>Success</source>
|
||||||
|
<translation type="obsolete">Erfolgreich</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="420"/>
|
||||||
|
<source>piControl reset executed successfully</source>
|
||||||
|
<translation type="obsolete">piControl wurde erfolgreich zurückgesetzt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="427"/>
|
||||||
|
<source>piControl reset could not be executed successfully</source>
|
||||||
|
<translation type="obsolete">piControl konnte nicht zurückgesetzt werden</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="475"/>
|
||||||
|
<source>Reset to piCtory defaults...</source>
|
||||||
|
<translation type="obsolete">Standardwerte von piCtory laden...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="475"/>
|
||||||
|
<source>Do you want to reset your process image to {0} values?
|
||||||
|
You have to stop other RevPiModIO programs before doing that, because they could reset the outputs.</source>
|
||||||
|
<translation type="obsolete">Soll das virtuelle Prozessabbild auf {0} zurückgesetzt werden?
|
||||||
|
Es sollten alle RevPiModIO Programme vorher beendet werden, da diese ihre IO Werte sofort wieder schreiben würden.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="475"/>
|
||||||
|
<source>zero</source>
|
||||||
|
<translation type="obsolete">null</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="475"/>
|
||||||
|
<source>piCtory default</source>
|
||||||
|
<translation type="obsolete">piCtory Standardwerte</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="501"/>
|
||||||
|
<source>The watch mode ist not supported in version {0} of RevPiPyLoad on your RevPi! You need at least version 0.5.3! Maybe the python3-revpimodio2 module is not installed on your RevPi at least version 2.0.0.</source>
|
||||||
|
<translation type="obsolete">Der SPS Betrachter ist in Version {0} von RevPiPyLoad auf dem Rev Pi nicht unterstützt! Es muss mindestens Version 0.5.3 installiert sein! Vielleicht fehlt auch das python3-revpimodio2 Modul, welches mindestens Version 2.0.0 haben muss.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="513"/>
|
||||||
|
<source>Can not load this function, because your ACL level is to low!
|
||||||
|
You need at least level 1 to read or level 3 to write.</source>
|
||||||
|
<translation type="obsolete">Für diese Funktion ist das Berechtigungslevel zu gering!
|
||||||
|
Es muss mindestens Level 1 zum Lesen oder Level 3 zu Schreiben sein.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="528"/>
|
||||||
|
<source>Can not load piCtory configuration.
|
||||||
|
Did you create a hardware configuration? Please check this in piCtory!</source>
|
||||||
|
<translation type="obsolete">Kann piCtory Konfiguration nicht laden.
|
||||||
|
Wurde eine Hardwarekonfiguration in piCtory erzeugt?</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ConnectionManager</name>
|
<name>ConnectionManager</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="512"/>
|
<location filename="../helper.py" line="524"/>
|
||||||
<source>SIMULATING</source>
|
<source>SIMULATING</source>
|
||||||
<translation>SIMULATION</translation>
|
<translation>SIMULATION</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="515"/>
|
<location filename="../helper.py" line="527"/>
|
||||||
<source>NOT CONNECTED</source>
|
<source>NOT CONNECTED</source>
|
||||||
<translation>NICHT VERBUNDEN</translation>
|
<translation>NICHT VERBUNDEN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="532"/>
|
<location filename="../helper.py" line="544"/>
|
||||||
<source>SERVER ERROR</source>
|
<source>SERVER ERROR</source>
|
||||||
<translation>SERVER FEHLER</translation>
|
<translation>SERVER FEHLER</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="557"/>
|
<location filename="../helper.py" line="569"/>
|
||||||
<source>RUNNING</source>
|
<source>RUNNING</source>
|
||||||
<translation>LÄUFT</translation>
|
<translation>LÄUFT</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="559"/>
|
<location filename="../helper.py" line="571"/>
|
||||||
<source>PLC FILE NOT FOUND</source>
|
<source>PLC FILE NOT FOUND</source>
|
||||||
<translation>SPS PROGRAMM NICHT GEFUNDEN</translation>
|
<translation>SPS PROGRAMM NICHT GEFUNDEN</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="561"/>
|
<location filename="../helper.py" line="573"/>
|
||||||
<source>NOT RUNNING (NO STATUS)</source>
|
<source>NOT RUNNING (NO STATUS)</source>
|
||||||
<translation>LÄUFT NICHT (KEIN STATUS)</translation>
|
<translation>LÄUFT NICHT (KEIN STATUS)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="563"/>
|
<location filename="../helper.py" line="575"/>
|
||||||
<source>PROGRAM KILLED</source>
|
<source>PROGRAM KILLED</source>
|
||||||
<translation>PROGRAMM GETÖTET</translation>
|
<translation>PROGRAMM GETÖTET</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="565"/>
|
<location filename="../helper.py" line="577"/>
|
||||||
<source>PROGRAM TERMED</source>
|
<source>PROGRAM TERMED</source>
|
||||||
<translation>PROGRAMM BEENDET</translation>
|
<translation>PROGRAMM BEENDET</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="567"/>
|
<location filename="../helper.py" line="579"/>
|
||||||
<source>NOT RUNNING</source>
|
<source>NOT RUNNING</source>
|
||||||
<translation>LÄUFT NICHT</translation>
|
<translation>LÄUFT NICHT</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="569"/>
|
<location filename="../helper.py" line="581"/>
|
||||||
<source>FINISHED WITH CODE {0}</source>
|
<source>FINISHED WITH CODE {0}</source>
|
||||||
<translation>BEENDET MIT CODE {0}</translation>
|
<translation>BEENDET MIT CODE {0}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="384"/>
|
<location filename="../helper.py" line="395"/>
|
||||||
<source>Error</source>
|
<source>Error</source>
|
||||||
<translation>Fehler</translation>
|
<translation>Fehler</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="352"/>
|
<location filename="../helper.py" line="347"/>
|
||||||
<source>The combination of username and password was rejected from the SSH server.
|
<source>The combination of username and password was rejected from the SSH server.
|
||||||
|
|
||||||
Try again.</source>
|
Try again.</source>
|
||||||
@@ -165,7 +293,7 @@ Try again.</source>
|
|||||||
Bitte erneut versuchen.</translation>
|
Bitte erneut versuchen.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="361"/>
|
<location filename="../helper.py" line="359"/>
|
||||||
<source>Could not establish a SSH connection to server:
|
<source>Could not establish a SSH connection to server:
|
||||||
|
|
||||||
{0}</source>
|
{0}</source>
|
||||||
@@ -174,7 +302,7 @@ Bitte erneut versuchen.</translation>
|
|||||||
{0}</translation>
|
{0}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../helper.py" line="384"/>
|
<location filename="../helper.py" line="395"/>
|
||||||
<source>Can not connect to RevPi XML-RPC Service!
|
<source>Can not connect to RevPi XML-RPC Service!
|
||||||
|
|
||||||
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!!!
|
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!!!
|
||||||
@@ -186,6 +314,24 @@ Das kann eine der folgenden Ursachen haben: Der Rev Pi ist nicht online, der XML
|
|||||||
|
|
||||||
Führe 'sudo revpipyload_secure_installation' auf dem Revolution Pi aus um diese Funktion zu konfigurieren!</translation>
|
Führe 'sudo revpipyload_secure_installation' auf dem Revolution Pi aus um diese Funktion zu konfigurieren!</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../helper.py" line="383"/>
|
||||||
|
<source>Can not connect to RevPi XML-RPC Service through SSH tunnel!
|
||||||
|
|
||||||
|
This could have the following reasons: The XML-RPC service is not running / bind to localhost or the ACL permission is not set for 127.0.0.1!!!</source>
|
||||||
|
<translation type="obsolete">Kann keine Verbindung zum RevPi XML-RPC Dienst herstellen!
|
||||||
|
|
||||||
|
Das kann eine der folgenden Ursachen haben: Der XML-RPC Dienst läuft nicht / ist nicht an localhost gebunden order die Berechtigungen sind nicht für 127.0.0.1 gesetzt!!!</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../helper.py" line="383"/>
|
||||||
|
<source>Can not connect to RevPi XML-RPC Service through SSH tunnel!
|
||||||
|
|
||||||
|
This could have the following reasons: The XML-RPC service is not running / not bind to localhost or the ACL permission is not set for 127.0.0.1!!!</source>
|
||||||
|
<translation>Kann keine Verbindung zum RevPi XML-RPC Dienst über SSH herstellen!
|
||||||
|
|
||||||
|
Das kann eine der folgenden Ursachen haben: Der XML-RPC Dienst läuft nicht / ist nicht an localhost gebunden order die Berechtigungen sind nicht für 127.0.0.1 gesetzt!!!</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>DebugControl</name>
|
<name>DebugControl</name>
|
||||||
@@ -283,90 +429,90 @@ Ungesicherte Änderungen gehen verloren.</translation>
|
|||||||
<context>
|
<context>
|
||||||
<name>RevPiCommander</name>
|
<name>RevPiCommander</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="222"/>
|
<location filename="../revpicommander.py" line="306"/>
|
||||||
<source>Simulator started...</source>
|
<source>Simulator started...</source>
|
||||||
<translation>Simulator gestartet...</translation>
|
<translation>Simulator gestartet...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="231"/>
|
<location filename="../revpicommander.py" line="315"/>
|
||||||
<source>Can not start...</source>
|
<source>Can not start...</source>
|
||||||
<translation>Kann nicht gestartet werden...</translation>
|
<translation>Kann nicht gestartet werden...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="424"/>
|
<location filename="../revpicommander.py" line="508"/>
|
||||||
<source>Warning</source>
|
<source>Warning</source>
|
||||||
<translation>Warnung</translation>
|
<translation>Warnung</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="247"/>
|
<location filename="../revpicommander.py" line="331"/>
|
||||||
<source>This version of Logviewer ist not supported in version {0} of RevPiPyLoad on your RevPi! You need at least version 0.4.1.</source>
|
<source>This version of Logviewer ist not supported in version {0} of RevPiPyLoad on your RevPi! You need at least version 0.4.1.</source>
|
||||||
<translation>Diese Version vom Logbetrachter wird in RevPiPyLoad Version {0} nicht unterstützt! Es wird mindestens Version 0.4.1 benötigt.</translation>
|
<translation>Diese Version vom Logbetrachter wird in RevPiPyLoad Version {0} nicht unterstützt! Es wird mindestens Version 0.4.1 benötigt.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="297"/>
|
<location filename="../revpicommander.py" line="381"/>
|
||||||
<source>XML-RPC access mode in the RevPiPyLoad configuration is too small to access this dialog!</source>
|
<source>XML-RPC access mode in the RevPiPyLoad configuration is too small to access this dialog!</source>
|
||||||
<translation>XML-RPC Zugriffsberechtigung in der RevPiPyLoad Konfiguraiton ist zu klein für diese Einstellungen!</translation>
|
<translation>XML-RPC Zugriffsberechtigung in der RevPiPyLoad Konfiguraiton ist zu klein für diese Einstellungen!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="451"/>
|
<location filename="../revpicommander.py" line="535"/>
|
||||||
<source>Error</source>
|
<source>Error</source>
|
||||||
<translation>Fehler</translation>
|
<translation>Fehler</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="277"/>
|
<location filename="../revpicommander.py" line="361"/>
|
||||||
<source>The Version of RevPiPyLoad on your Revolution Pi ({0}) is to old. This Version of RevPiCommander require at least version 0.6.0 of RevPiPyLoad. Please update your Revolution Pi!</source>
|
<source>The Version of RevPiPyLoad on your Revolution Pi ({0}) is to old. This Version of RevPiCommander require at least version 0.6.0 of RevPiPyLoad. Please update your Revolution Pi!</source>
|
||||||
<translation>Die Version von RevPiPyLoad ({0}) auf dem Revolution Pi ist zu alt. Diese Version vom RevPiCommander braucht mindestens Version 0.6.0. Bitte aktualisiere deinen Revolution Pi!</translation>
|
<translation>Die Version von RevPiPyLoad ({0}) auf dem Revolution Pi ist zu alt. Diese Version vom RevPiCommander braucht mindestens Version 0.6.0. Bitte aktualisiere deinen Revolution Pi!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="330"/>
|
<location filename="../revpicommander.py" line="414"/>
|
||||||
<source>Question</source>
|
<source>Question</source>
|
||||||
<translation>Frage</translation>
|
<translation>Frage</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="330"/>
|
<location filename="../revpicommander.py" line="414"/>
|
||||||
<source>Are you sure to reset piControl?
|
<source>Are you sure to reset piControl?
|
||||||
The pictory configuration will be reloaded. During that time the process image will be interrupted and could rise errors on running control programs!</source>
|
The pictory configuration will be reloaded. During that time the process image will be interrupted and could rise errors on running control programs!</source>
|
||||||
<translation>Soll piControl wirklich zurückgesetzt werden?
|
<translation>Soll piControl wirklich zurückgesetzt werden?
|
||||||
Die piCtory Konfiguration wird neu geladen. Das Prozessabbild wird in dieser Zeit nicht verfügbar sein und es könnten Fehler in Steuerungsprogrammen ausgelöst werden!</translation>
|
Die piCtory Konfiguration wird neu geladen. Das Prozessabbild wird in dieser Zeit nicht verfügbar sein und es könnten Fehler in Steuerungsprogrammen ausgelöst werden!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="343"/>
|
<location filename="../revpicommander.py" line="427"/>
|
||||||
<source>Success</source>
|
<source>Success</source>
|
||||||
<translation>Erfolgreich</translation>
|
<translation>Erfolgreich</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="343"/>
|
<location filename="../revpicommander.py" line="427"/>
|
||||||
<source>piControl reset executed successfully</source>
|
<source>piControl reset executed successfully</source>
|
||||||
<translation>piControl wurde erfolgreich zurückgesetzt</translation>
|
<translation>piControl wurde erfolgreich zurückgesetzt</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="350"/>
|
<location filename="../revpicommander.py" line="434"/>
|
||||||
<source>piControl reset could not be executed successfully</source>
|
<source>piControl reset could not be executed successfully</source>
|
||||||
<translation>piControl konnte nicht zurückgesetzt werden</translation>
|
<translation>piControl konnte nicht zurückgesetzt werden</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="398"/>
|
<location filename="../revpicommander.py" line="482"/>
|
||||||
<source>Reset to piCtory defaults...</source>
|
<source>Reset to piCtory defaults...</source>
|
||||||
<translation>Standardwerte von piCtory laden...</translation>
|
<translation>Standardwerte von piCtory laden...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="424"/>
|
<location filename="../revpicommander.py" line="508"/>
|
||||||
<source>The watch mode ist not supported in version {0} of RevPiPyLoad on your RevPi! You need at least version 0.5.3! Maybe the python3-revpimodio2 module is not installed on your RevPi at least version 2.0.0.</source>
|
<source>The watch mode ist not supported in version {0} of RevPiPyLoad on your RevPi! You need at least version 0.5.3! Maybe the python3-revpimodio2 module is not installed on your RevPi at least version 2.0.0.</source>
|
||||||
<translation>Der SPS Betrachter ist in Version {0} von RevPiPyLoad auf dem Rev Pi nicht unterstützt! Es muss mindestens Version 0.5.3 installiert sein! Vielleicht fehlt auch das python3-revpimodio2 Modul, welches mindestens Version 2.0.0 haben muss.</translation>
|
<translation>Der SPS Betrachter ist in Version {0} von RevPiPyLoad auf dem Rev Pi nicht unterstützt! Es muss mindestens Version 0.5.3 installiert sein! Vielleicht fehlt auch das python3-revpimodio2 Modul, welches mindestens Version 2.0.0 haben muss.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="436"/>
|
<location filename="../revpicommander.py" line="520"/>
|
||||||
<source>Can not load this function, because your ACL level is to low!
|
<source>Can not load this function, because your ACL level is to low!
|
||||||
You need at least level 1 to read or level 3 to write.</source>
|
You need at least level 1 to read or level 3 to write.</source>
|
||||||
<translation>Für diese Funktion ist das Berechtigungslevel zu gering!
|
<translation>Für diese Funktion ist das Berechtigungslevel zu gering!
|
||||||
Es muss mindestens Level 1 zum Lesen oder Level 3 zu Schreiben sein.</translation>
|
Es muss mindestens Level 1 zum Lesen oder Level 3 zu Schreiben sein.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="451"/>
|
<location filename="../revpicommander.py" line="535"/>
|
||||||
<source>Can not load piCtory configuration.
|
<source>Can not load piCtory configuration.
|
||||||
Did you create a hardware configuration? Please check this in piCtory!</source>
|
Did you create a hardware configuration? Please check this in piCtory!</source>
|
||||||
<translation>Kann piCtory Konfiguration nicht laden.
|
<translation>Kann piCtory Konfiguration nicht laden.
|
||||||
Wurde eine Hardwarekonfiguration in piCtory erzeugt?</translation>
|
Wurde eine Hardwarekonfiguration in piCtory erzeugt? Bitte prüfe dies in piCtory!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="101"/>
|
<location filename="../revpicommander.py" line="101"/>
|
||||||
@@ -382,7 +528,7 @@ Das kann eine der folgenden Ursachen haben: Der Rev Pi ist nicht online, der XML
|
|||||||
Führe 'sudo revpipyload_secure_installation' auf dem Revolution Pi aus um diese Funktion zu konfigurieren!</translation>
|
Führe 'sudo revpipyload_secure_installation' auf dem Revolution Pi aus um diese Funktion zu konfigurieren!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="222"/>
|
<location filename="../revpicommander.py" line="306"/>
|
||||||
<source>The simulator is running!
|
<source>The simulator is running!
|
||||||
|
|
||||||
You can work with this simulator if your call RevPiModIO with this additional parameters:
|
You can work with this simulator if your call RevPiModIO with this additional parameters:
|
||||||
@@ -399,27 +545,42 @@ configrsc={1}
|
|||||||
Dies kann aus der Textbox oben kopiert werden.</translation>
|
Dies kann aus der Textbox oben kopiert werden.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="231"/>
|
<location filename="../revpicommander.py" line="315"/>
|
||||||
<source>Can not start the simulator! Maybe the piCtory file is corrupt or you have no write permissions for '{0}'.</source>
|
<source>Can not start the simulator! Maybe the piCtory file is corrupt or you have no write permissions for '{0}'.</source>
|
||||||
<translation>Kann Simulator nicht starten! Vielleicht ist die piCtory Datei defekt oder es gibt keine Schreibberechtigung für '{0}'.</translation>
|
<translation>Kann Simulator nicht starten! Vielleicht ist die piCtory Datei defekt oder es gibt keine Schreibberechtigung für '{0}'.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="398"/>
|
<location filename="../revpicommander.py" line="482"/>
|
||||||
<source>Do you want to reset your process image to {0} values?
|
<source>Do you want to reset your process image to {0} values?
|
||||||
You have to stop other RevPiModIO programs before doing that, because they could reset the outputs.</source>
|
You have to stop other RevPiModIO programs before doing that, because they could reset the outputs.</source>
|
||||||
<translation>Soll das virtuelle Prozessabbild auf {0} zurückgesetzt werden?
|
<translation>Soll das virtuelle Prozessabbild auf {0} zurückgesetzt werden?
|
||||||
Es sollten alle RevPiModIO Programme vorher beendet werden, da diese ihre IO Werte sofort wieder schreiben würden.</translation>
|
Es sollten alle RevPiModIO Programme vorher beendet werden, da diese ihre IO Werte sofort wieder schreiben würden.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="398"/>
|
<location filename="../revpicommander.py" line="482"/>
|
||||||
<source>zero</source>
|
<source>zero</source>
|
||||||
<translation>null</translation>
|
<translation>null</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpicommander.py" line="398"/>
|
<location filename="../revpicommander.py" line="482"/>
|
||||||
<source>piCtory default</source>
|
<source>piCtory default</source>
|
||||||
<translation>piCtory Standardwerte</translation>
|
<translation>piCtory Standardwerte</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="236"/>
|
||||||
|
<source>Revolution Pi connected!</source>
|
||||||
|
<translation>Revolution Pi verbunden!</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="273"/>
|
||||||
|
<source>Connecting...</source>
|
||||||
|
<translation>Verbinde...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpicommander.py" line="236"/>
|
||||||
|
<source>Establish a connection to the Revolution Pi...</source>
|
||||||
|
<translation>Baue eine Verbindung zum Revolution Pi auf...</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>RevPiFiles</name>
|
<name>RevPiFiles</name>
|
||||||
@@ -547,6 +708,11 @@ Wählen Sie 'Ja' zum Überschreiben, 'Nein' um nur fehlende
|
|||||||
<source>Choose a local directory first.</source>
|
<source>Choose a local directory first.</source>
|
||||||
<translation>Lokales Verzeichnis wählen.</translation>
|
<translation>Lokales Verzeichnis wählen.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../revpifiles.py" line="127"/>
|
||||||
|
<source>File transfer...</source>
|
||||||
|
<translation>Dateiübertragung...</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>RevPiInfo</name>
|
<name>RevPiInfo</name>
|
||||||
@@ -668,12 +834,12 @@ Ungesicherte Änderungen gehen verloren.</translation>
|
|||||||
<translation type="obsolete">Neue Verbindung</translation>
|
<translation type="obsolete">Neue Verbindung</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpiplclist.py" line="102"/>
|
<location filename="../revpiplclist.py" line="117"/>
|
||||||
<source>Question</source>
|
<source>Question</source>
|
||||||
<translation>Frage</translation>
|
<translation>Frage</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../revpiplclist.py" line="102"/>
|
<location filename="../revpiplclist.py" line="117"/>
|
||||||
<source>Do you really want to quit?
|
<source>Do you really want to quit?
|
||||||
Unsaved changes will be lost.</source>
|
Unsaved changes will be lost.</source>
|
||||||
<translation>Soll das Fenster wirklich geschlossen werden?
|
<translation>Soll das Fenster wirklich geschlossen werden?
|
||||||
@@ -955,12 +1121,12 @@ Dies ist kein Fehler, wenn das SPS Startprogramm bereits auf dem Rev Pi ist. Pr
|
|||||||
<context>
|
<context>
|
||||||
<name>SSHAuth</name>
|
<name>SSHAuth</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../sshauth.py" line="49"/>
|
<location filename="../sshauth.py" line="51"/>
|
||||||
<source>Could not save password</source>
|
<source>Could not save password</source>
|
||||||
<translation>Konnte Kennwort nicht speichern</translation>
|
<translation>Konnte Kennwort nicht speichern</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../sshauth.py" line="49"/>
|
<location filename="../sshauth.py" line="51"/>
|
||||||
<source>Could not save password to operating systems password save.
|
<source>Could not save password to operating systems password save.
|
||||||
|
|
||||||
Maybe your operating system does not support saving passwords. This could be due to missing libraries or programs.
|
Maybe your operating system does not support saving passwords. This could be due to missing libraries or programs.
|
||||||
@@ -1049,7 +1215,7 @@ Dies ist kein Fehler von RevPi Commander.</translation>
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../../../ui_dev/backgroundworker.ui" line="14"/>
|
<location filename="../../../ui_dev/backgroundworker.ui" line="14"/>
|
||||||
<source>File transfer...</source>
|
<source>File transfer...</source>
|
||||||
<translation>Dateiübertragung...</translation>
|
<translation type="obsolete">Dateiübertragung...</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@@ -1684,25 +1850,30 @@ applicable law.
|
|||||||
<translation>SSH Authentifizierung</translation>
|
<translation>SSH Authentifizierung</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../../ui_dev/sshauth.ui" line="26"/>
|
<location filename="../../../ui_dev/sshauth.ui" line="29"/>
|
||||||
<source>SSH username:</source>
|
<source>SSH username:</source>
|
||||||
<translation>SSH Benutzername:</translation>
|
<translation>SSH Benutzername:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../../ui_dev/sshauth.ui" line="33"/>
|
<location filename="../../../ui_dev/sshauth.ui" line="36"/>
|
||||||
<source>SSH password:</source>
|
<source>SSH password:</source>
|
||||||
<translation>SSH Passwort:</translation>
|
<translation>SSH Passwort:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../../ui_dev/sshauth.ui" line="53"/>
|
<location filename="../../../ui_dev/sshauth.ui" line="56"/>
|
||||||
<source>Username and password will be saved in secured operating systems's password storage.</source>
|
<source>Username and password will be saved in secured operating systems's password storage.</source>
|
||||||
<translation>Benutzername und Kennwort werden im Passwortspeicher vom Betriebssystem gesichert.</translation>
|
<translation>Benutzername und Kennwort werden im Passwortspeicher vom Betriebssystem gesichert.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../../ui_dev/sshauth.ui" line="56"/>
|
<location filename="../../../ui_dev/sshauth.ui" line="59"/>
|
||||||
<source>Save username and password</source>
|
<source>Save username and password</source>
|
||||||
<translation>Benutzername und Kennwort merken</translation>
|
<translation>Benutzername und Kennwort merken</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../../../ui_dev/sshauth.ui" line="82"/>
|
||||||
|
<source>Note: The default user for SSH is "pi" which differs from the web configuration. You can find the password on the sticker on the device.</source>
|
||||||
|
<translation>Hinweis: Der Standardbenutzer für SSH ist "pi" dies weicht von der Web-Konfiguration ab. Das Kennwort finden sie auf dem Aufkleber am Gerät.</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>wid_debugcontrol</name>
|
<name>wid_debugcontrol</name>
|
||||||
|
|||||||
@@ -11,22 +11,42 @@ from os.path import dirname, join
|
|||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
from revpicommander.backgroundworker import BackgroundWaiter
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from . import helper
|
from . import helper
|
||||||
from . import proginit as pi
|
from . import proginit as pi
|
||||||
from . import revpilogfile
|
from . import revpilogfile
|
||||||
from .avahisearch import AvahiSearch
|
from .avahisearch import AvahiSearch
|
||||||
from .debugcontrol import DebugControl
|
from .debugcontrol import DebugControl
|
||||||
from .helper import RevPiSettings
|
from .helper import ConnectionFail, RevPiSettings
|
||||||
from .revpifiles import RevPiFiles
|
from .revpifiles import RevPiFiles
|
||||||
from .revpiinfo import RevPiInfo
|
from .revpiinfo import RevPiInfo
|
||||||
from .revpioption import RevPiOption
|
from .revpioption import RevPiOption
|
||||||
from .revpiplclist import RevPiPlcList
|
from .revpiplclist import RevPiPlcList
|
||||||
from .revpiprogram import RevPiProgram
|
from .revpiprogram import RevPiProgram
|
||||||
from .simulator import Simulator
|
from .simulator import Simulator
|
||||||
|
from .sshauth import SSHAuth
|
||||||
from .ui.revpicommander_ui import Ui_win_revpicommander
|
from .ui.revpicommander_ui import Ui_win_revpicommander
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectingPyload(QtCore.QThread):
|
||||||
|
"""
|
||||||
|
Try to establish a connection in background.
|
||||||
|
|
||||||
|
The pyload_connect function will emit signals for error or successful connect. This
|
||||||
|
signals will be used to show error messages and return to this function, if the
|
||||||
|
authentication failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, revpi_settings: RevPiSettings, ssh_password="", parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._revpi_settings = revpi_settings
|
||||||
|
self._ssh_password = ssh_password
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
helper.cm.pyload_connect(self._revpi_settings, self._ssh_password)
|
||||||
|
|
||||||
|
|
||||||
class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||||
"""Main application of RevPiCommander."""
|
"""Main application of RevPiCommander."""
|
||||||
|
|
||||||
@@ -56,6 +76,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
|||||||
|
|
||||||
self.btn_plc_logs.clicked.connect(self.on_act_logs_triggered)
|
self.btn_plc_logs.clicked.connect(self.on_act_logs_triggered)
|
||||||
|
|
||||||
|
helper.cm.connect_error.connect(self.on_cm_connect_error)
|
||||||
helper.cm.connection_disconnected.connect(self.on_cm_connection_disconnected)
|
helper.cm.connection_disconnected.connect(self.on_cm_connection_disconnected)
|
||||||
helper.cm.connection_disconnecting.connect(self.on_cm_connection_disconnecting)
|
helper.cm.connection_disconnecting.connect(self.on_cm_connection_disconnecting)
|
||||||
helper.cm.connection_established.connect(self.on_cm_connection_established)
|
helper.cm.connection_established.connect(self.on_cm_connection_established)
|
||||||
@@ -97,6 +118,21 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
|||||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
# region # REGION: Connection management
|
# region # REGION: Connection management
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str, str, ConnectionFail, RevPiSettings)
|
||||||
|
def on_cm_connect_error(self, title: str, text: str, fail_code: ConnectionFail, revpi_settings: RevPiSettings):
|
||||||
|
"""
|
||||||
|
Slot to get information of pyload_connect connection errors.
|
||||||
|
|
||||||
|
:param title: Title of error message
|
||||||
|
:param text: Text of error message
|
||||||
|
:param fail_code: Type of error
|
||||||
|
:param revpi_settings: Settings of the revpi with the error
|
||||||
|
"""
|
||||||
|
QtWidgets.QMessageBox.critical(self, title, text)
|
||||||
|
if fail_code is ConnectionFail.SSH_AUTH:
|
||||||
|
# On failed credentials, we try to connect again and remove password form keychain, if exists
|
||||||
|
self._pyload_connect(revpi_settings, True)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def on_cm_connection_error_observed(self, message: str):
|
def on_cm_connection_error_observed(self, message: str):
|
||||||
"""
|
"""
|
||||||
@@ -188,6 +224,54 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
|||||||
act.setToolTip("{0}:{1}".format(settings.address, settings.port))
|
act.setToolTip("{0}:{1}".format(settings.address, settings.port))
|
||||||
parent_menu.addAction(act)
|
parent_menu.addAction(act)
|
||||||
|
|
||||||
|
def _pyload_connect(self, revpi_settings: RevPiSettings, remove_saved_ssh_password=False):
|
||||||
|
"""
|
||||||
|
Try to async establish a connection to Revolution Pi.
|
||||||
|
|
||||||
|
:param revpi_settings: RevPi settings object
|
||||||
|
:param remove_saved_ssh_password: Remove password from keychain
|
||||||
|
"""
|
||||||
|
ssh_password = ""
|
||||||
|
|
||||||
|
diag_connecting = BackgroundWaiter(
|
||||||
|
revpi_settings.timeout,
|
||||||
|
self.tr("Establish a connection to the Revolution Pi..."),
|
||||||
|
self,
|
||||||
|
self.tr("Revolution Pi connected!"),
|
||||||
|
)
|
||||||
|
helper.cm.connection_established.connect(diag_connecting.requestInterruption)
|
||||||
|
|
||||||
|
if revpi_settings.ssh_use_tunnel:
|
||||||
|
diag_ssh_auth = SSHAuth(
|
||||||
|
revpi_settings.ssh_user,
|
||||||
|
"{0}.{1}_{2}".format(
|
||||||
|
helper.settings.applicationName(),
|
||||||
|
helper.settings.organizationName(),
|
||||||
|
revpi_settings.internal_id),
|
||||||
|
self,
|
||||||
|
)
|
||||||
|
|
||||||
|
if remove_saved_ssh_password and revpi_settings.ssh_saved_password:
|
||||||
|
diag_ssh_auth.remove_saved_password()
|
||||||
|
revpi_settings.ssh_saved_password = False
|
||||||
|
|
||||||
|
# Doesn't matter what user selects, we have to save settings to sync keychain and values
|
||||||
|
if diag_ssh_auth.exec() != QtWidgets.QDialog.Accepted:
|
||||||
|
revpi_settings.save_settings()
|
||||||
|
return
|
||||||
|
|
||||||
|
revpi_settings.ssh_saved_password = diag_ssh_auth.in_keyring
|
||||||
|
revpi_settings.ssh_user = diag_ssh_auth.username
|
||||||
|
ssh_password = diag_ssh_auth.password
|
||||||
|
revpi_settings.save_settings()
|
||||||
|
|
||||||
|
# Connect in background and show the connecting dialog to user
|
||||||
|
th_connecting = ConnectingPyload(revpi_settings, ssh_password, self)
|
||||||
|
th_connecting.finished.connect(diag_connecting.requestInterruption)
|
||||||
|
th_connecting.start()
|
||||||
|
|
||||||
|
diag_connecting.exec_dialog(self.tr("Connecting..."), False)
|
||||||
|
|
||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def on_act_connections_triggered(self):
|
def on_act_connections_triggered(self):
|
||||||
"""Edit saved connections to Revolution Pi devices."""
|
"""Edit saved connections to Revolution Pi devices."""
|
||||||
@@ -202,7 +286,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
|||||||
if self.diag_search.just_save:
|
if self.diag_search.just_save:
|
||||||
self.diag_connections.exec_with_presets(self.diag_search.connect_settings)
|
self.diag_connections.exec_with_presets(self.diag_search.connect_settings)
|
||||||
else:
|
else:
|
||||||
helper.cm.pyload_connect(self.diag_search.connect_settings, self)
|
self._pyload_connect(self.diag_search.connect_settings)
|
||||||
|
|
||||||
self._load_men_connections()
|
self._load_men_connections()
|
||||||
|
|
||||||
@@ -361,7 +445,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
|||||||
@QtCore.pyqtSlot(QtWidgets.QAction)
|
@QtCore.pyqtSlot(QtWidgets.QAction)
|
||||||
def on_men_connections_triggered(self, action: QtWidgets.QAction):
|
def on_men_connections_triggered(self, action: QtWidgets.QAction):
|
||||||
"""A connection is selected in the men_connections menu."""
|
"""A connection is selected in the men_connections menu."""
|
||||||
helper.cm.pyload_connect(action.data(), self)
|
self._pyload_connect(action.data())
|
||||||
|
|
||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def on_act_webpage_triggered(self):
|
def on_act_webpage_triggered(self):
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
|||||||
return
|
return
|
||||||
|
|
||||||
uploader = UploadFiles(self.file_list_local(), self)
|
uploader = UploadFiles(self.file_list_local(), self)
|
||||||
if uploader.exec_dialog() == QtWidgets.QDialog.Rejected:
|
if uploader.exec_dialog(self.tr("File transfer...")) == QtWidgets.QDialog.Rejected:
|
||||||
return
|
return
|
||||||
|
|
||||||
if uploader.ec == 0:
|
if uploader.ec == 0:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
|||||||
class Ui_diag_backgroundworker(object):
|
class Ui_diag_backgroundworker(object):
|
||||||
def setupUi(self, diag_backgroundworker):
|
def setupUi(self, diag_backgroundworker):
|
||||||
diag_backgroundworker.setObjectName("diag_backgroundworker")
|
diag_backgroundworker.setObjectName("diag_backgroundworker")
|
||||||
diag_backgroundworker.resize(418, 97)
|
diag_backgroundworker.resize(424, 104)
|
||||||
diag_backgroundworker.setModal(True)
|
diag_backgroundworker.setModal(True)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(diag_backgroundworker)
|
self.verticalLayout = QtWidgets.QVBoxLayout(diag_backgroundworker)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
@@ -37,8 +37,7 @@ class Ui_diag_backgroundworker(object):
|
|||||||
QtCore.QMetaObject.connectSlotsByName(diag_backgroundworker)
|
QtCore.QMetaObject.connectSlotsByName(diag_backgroundworker)
|
||||||
|
|
||||||
def retranslateUi(self, diag_backgroundworker):
|
def retranslateUi(self, diag_backgroundworker):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
pass
|
||||||
diag_backgroundworker.setWindowTitle(_translate("diag_backgroundworker", "File transfer..."))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ class Ui_diag_sshauth(object):
|
|||||||
def setupUi(self, diag_sshauth):
|
def setupUi(self, diag_sshauth):
|
||||||
diag_sshauth.setObjectName("diag_sshauth")
|
diag_sshauth.setObjectName("diag_sshauth")
|
||||||
diag_sshauth.setWindowModality(QtCore.Qt.ApplicationModal)
|
diag_sshauth.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||||
diag_sshauth.resize(363, 163)
|
diag_sshauth.resize(331, 225)
|
||||||
self.verticalLayout = QtWidgets.QVBoxLayout(diag_sshauth)
|
self.verticalLayout = QtWidgets.QVBoxLayout(diag_sshauth)
|
||||||
|
self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
|
||||||
self.verticalLayout.setObjectName("verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.wid_password = QtWidgets.QWidget(diag_sshauth)
|
self.wid_password = QtWidgets.QWidget(diag_sshauth)
|
||||||
self.wid_password.setObjectName("wid_password")
|
self.wid_password.setObjectName("wid_password")
|
||||||
@@ -44,6 +45,16 @@ class Ui_diag_sshauth(object):
|
|||||||
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
self.btn_box.setObjectName("btn_box")
|
self.btn_box.setObjectName("btn_box")
|
||||||
self.verticalLayout.addWidget(self.btn_box)
|
self.verticalLayout.addWidget(self.btn_box)
|
||||||
|
self.lbl_info = QtWidgets.QLabel(diag_sshauth)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.lbl_info.sizePolicy().hasHeightForWidth())
|
||||||
|
self.lbl_info.setSizePolicy(sizePolicy)
|
||||||
|
self.lbl_info.setWordWrap(True)
|
||||||
|
self.lbl_info.setObjectName("lbl_info")
|
||||||
|
self.verticalLayout.addWidget(self.lbl_info)
|
||||||
|
self.verticalLayout.setStretch(3, 1)
|
||||||
|
|
||||||
self.retranslateUi(diag_sshauth)
|
self.retranslateUi(diag_sshauth)
|
||||||
self.btn_box.accepted.connect(diag_sshauth.accept) # type: ignore
|
self.btn_box.accepted.connect(diag_sshauth.accept) # type: ignore
|
||||||
@@ -57,6 +68,7 @@ class Ui_diag_sshauth(object):
|
|||||||
self.lbl_password.setText(_translate("diag_sshauth", "SSH password:"))
|
self.lbl_password.setText(_translate("diag_sshauth", "SSH password:"))
|
||||||
self.cbx_save_password.setToolTip(_translate("diag_sshauth", "Username and password will be saved in secured operating systems\'s password storage."))
|
self.cbx_save_password.setToolTip(_translate("diag_sshauth", "Username and password will be saved in secured operating systems\'s password storage."))
|
||||||
self.cbx_save_password.setText(_translate("diag_sshauth", "Save username and password"))
|
self.cbx_save_password.setText(_translate("diag_sshauth", "Save username and password"))
|
||||||
|
self.lbl_info.setText(_translate("diag_sshauth", "Note: The default user for SSH is \"pi\" which differs from the web configuration. You can find the password on the sticker on the device."))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -6,13 +6,10 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>418</width>
|
<width>424</width>
|
||||||
<height>97</height>
|
<height>104</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
|
||||||
<string>File transfer...</string>
|
|
||||||
</property>
|
|
||||||
<property name="modal">
|
<property name="modal">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
@@ -9,14 +9,17 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>363</width>
|
<width>331</width>
|
||||||
<height>163</height>
|
<height>225</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>SSH authentication</string>
|
<string>SSH authentication</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,1">
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SetFixedSize</enum>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="wid_password" native="true">
|
<widget class="QWidget" name="wid_password" native="true">
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
@@ -67,6 +70,22 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_info">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Note: The default user for SSH is "pi" which differs from the web configuration. You can find the password on the sticker on the device.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|||||||
Reference in New Issue
Block a user