mirror of
https://github.com/naruxde/revpicommander.git
synced 2025-11-09 08:58:04 +01:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9202aedf7f | |||
| 8e2b8311ef | |||
| 15058feb7e | |||
| d14b6dccdd | |||
| feb55fe2b5 |
@@ -2,7 +2,7 @@ recursive-include data *
|
||||
recursive-include src/revpicommander *.py *.qm
|
||||
recursive-include ui_dev *
|
||||
include LICENSE.txt
|
||||
include make_installer_win.bat
|
||||
include make.bat
|
||||
include Makefile
|
||||
include MANIFEST.in
|
||||
include README.md
|
||||
|
||||
63
Makefile
63
Makefile
@@ -4,9 +4,12 @@ MAKEFLAGS = --no-print-directory --no-builtin-rules
|
||||
|
||||
# Variables
|
||||
PACKAGE = revpicommander
|
||||
APP_NAME = RevPi\ Commander
|
||||
APP_IDENT = org.revpimodio.revpicommander
|
||||
APPLE_SIG = "Developer ID Application: Sven Sager (U3N5843D9K)"
|
||||
|
||||
# If virtualenv exists, use it. If not, use PATH to find
|
||||
SYSTEM_PYTHON = $(or $(shell which python3), $(shell which python))
|
||||
# If virtualenv exists, use it. If not, use PATH to find, except python3
|
||||
SYSTEM_PYTHON = /usr/bin/python3
|
||||
PYTHON = $(or $(wildcard venv/bin/python), $(SYSTEM_PYTHON))
|
||||
SYSTEM_PYUIC5 = $(shell which pyuic5)
|
||||
PYUIC5 = $(or $(wildcard venv/bin/pyuic5), $(SYSTEM_PYUIC5))
|
||||
@@ -15,10 +18,22 @@ PYRCC5 = $(or $(wildcard venv/bin/pyrcc5), $(SYSTEM_PYRCC5))
|
||||
SYSTEM_PYLUP5 = $(shell which pylupdate5)
|
||||
PYLUP5 = $(or $(wildcard venv/bin/pylupdate5), $(SYSTEM_PYLUP5))
|
||||
|
||||
APP_VERSION = $(shell $(PYTHON) src/$(PACKAGE) --version)
|
||||
|
||||
all: build_ui build_rc build
|
||||
|
||||
.PHONY: all
|
||||
|
||||
## Environment
|
||||
venv:
|
||||
$(SYSTEM_PYTHON) -m venv venv
|
||||
source venv/bin/activate && \
|
||||
python3 -m pip install --upgrade pip && \
|
||||
python3 -m pip install -r requirements.txt
|
||||
exit 0
|
||||
|
||||
.PHONY: venv
|
||||
|
||||
## Compile Qt UI files to python code
|
||||
build_ui:
|
||||
cd ui_dev && for ui_file in *.ui; do \
|
||||
@@ -39,16 +54,6 @@ update_translation:
|
||||
|
||||
.PHONY: build_ui build_rc update_translation
|
||||
|
||||
## Environment
|
||||
venv:
|
||||
rm -rf venv
|
||||
$(SYSTEM_PYTHON) -m venv venv
|
||||
|
||||
deps:
|
||||
$(PYTHON) -m pip install --upgrade pip -r requirements.txt
|
||||
|
||||
.PHONY: venv deps
|
||||
|
||||
## Build, install
|
||||
build: build_ui build_rc
|
||||
$(PYTHON) -m setup sdist
|
||||
@@ -60,24 +65,44 @@ install:
|
||||
.PHONY: build install
|
||||
|
||||
## PyInstaller
|
||||
installer_mac: build
|
||||
$(PYTHON) -m PyInstaller -n "RevPi Commander" \
|
||||
--add-data="src/$(PACKAGE)/locale:./revpicommander/locale" \
|
||||
installer_mac: build_ui build_rc
|
||||
$(PYTHON) -m PyInstaller -n $(APP_NAME) \
|
||||
--add-data="src/$(PACKAGE)/locale:./$(PACKAGE)/locale" \
|
||||
--add-data="data/$(PACKAGE).icns:." \
|
||||
--icon=data/$(PACKAGE).icns \
|
||||
--noconfirm \
|
||||
--clean \
|
||||
--onedir \
|
||||
--windowed \
|
||||
--osx-bundle-identifier $APP_IDENT \
|
||||
--codesign-identity $(APPLE_SIG) \
|
||||
src/$(PACKAGE)/__main__.py
|
||||
|
||||
installer_win: all
|
||||
make_installer_win.bat
|
||||
installer_mac_dmg: installer_mac
|
||||
mkdir dist/dmg
|
||||
mv dist/$(APP_NAME).app dist/dmg
|
||||
create-dmg \
|
||||
--volname $(APP_NAME) \
|
||||
--background data/dmg_background.png \
|
||||
--window-pos 200 120 \
|
||||
--window-size 480 300 \
|
||||
--icon-size 64 \
|
||||
--icon $(APP_NAME).app 64 64 \
|
||||
--hide-extension $(APP_NAME).app \
|
||||
--app-drop-link 288 64 \
|
||||
--add-file LICENSE.txt LICENSE.txt 192 180 \
|
||||
--codesign $(APPLE_SIG) \
|
||||
--notarize AC_PASSWORD \
|
||||
dist/$(APP_NAME)\ $(APP_VERSION).dmg \
|
||||
dist/dmg
|
||||
|
||||
.PHONY: installer_mac installer_win
|
||||
.PHONY: installer_mac installer_mac_dmg
|
||||
|
||||
## Clean
|
||||
clean:
|
||||
rm -rf build dist src/*.egg-info *.spec
|
||||
|
||||
.PHONY: clean
|
||||
clean-all: clean
|
||||
rm -R venv
|
||||
|
||||
.PHONY: clean clean-all
|
||||
|
||||
BIN
data/dmg_background.png
Normal file
BIN
data/dmg_background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
@@ -1,3 +1,9 @@
|
||||
#!/bin/sh
|
||||
# Used to call the entry point, if packed with pybuild as application.
|
||||
# PYBUILD_INSTALL_ARGS=--install-lib=/usr/share/revpicommander/ \
|
||||
# --install-scripts=/usr/share/revpicommander/
|
||||
#
|
||||
# In that case the entry point will not find the revpicommander module in
|
||||
# the python lib.
|
||||
|
||||
exec "/usr/share/revpicommander/revpicommander.py" "$@"
|
||||
exec "/usr/share/revpicommander/revpicommander-gui" "$@"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[Desktop Entry]
|
||||
Name=RevPi PLC Commander
|
||||
Name=RevPi Commander
|
||||
Comment=Controls the Python PLC program on your Revolution PI
|
||||
Comment[de]=Kontrolliert das Python PLC Programm auf dem Revolution PI
|
||||
Exec=/usr/bin/revpicommander
|
||||
@@ -7,4 +7,3 @@ Icon=revpicommander
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Application;
|
||||
#StartupNotify=true
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 5.3 KiB |
39
make.bat
Normal file
39
make.bat
Normal file
@@ -0,0 +1,39 @@
|
||||
@echo off
|
||||
set PACKAGE=revpicommander
|
||||
set APP_NAME=RevPi Commander
|
||||
|
||||
if "%1" == "venv" goto venv
|
||||
if "%1" == "installer" goto installer
|
||||
if "%1" == "clean" goto clean
|
||||
|
||||
echo Make script for "%APP_NAME%" on Windows
|
||||
echo.
|
||||
echo Need action:
|
||||
echo venv Create / update your virtual environment for build process
|
||||
echo installer Build this application with PyInstaller
|
||||
echo clean Clean up your environment after build process
|
||||
goto end
|
||||
|
||||
:venv
|
||||
python -m venv venv
|
||||
venv\\Scripts\\pip.exe install --upgrade -r requirements.txt
|
||||
goto end
|
||||
|
||||
:installer
|
||||
venv\\Scripts\\pyinstaller -n "%APP_NAME%" ^
|
||||
--add-data="src\%PACKAGE%\locale;.\%PACKAGE%\locale" ^
|
||||
--add-data="data\%PACKAGE%.ico;." ^
|
||||
--icon=data\\%PACKAGE%.ico ^
|
||||
--noconfirm ^
|
||||
--clean ^
|
||||
--onedir ^
|
||||
--windowed ^
|
||||
src\\%PACKAGE%\\__main__.py
|
||||
goto end
|
||||
|
||||
:clean
|
||||
rmdir /S /Q build dist
|
||||
rmdir /S /Q src\%PACKAGE%.egg-info
|
||||
del *.spec
|
||||
|
||||
:end
|
||||
@@ -1,18 +0,0 @@
|
||||
@echo off
|
||||
set PACKAGE=revpicommander
|
||||
|
||||
rem python -m venv venv
|
||||
rem venv\\Scripts\\activate.bat
|
||||
rem pip install -r requirements.txt
|
||||
|
||||
pyinstaller -n "RevPi Commander" ^
|
||||
--add-data="src\%PACKAGE%\locale;.\revpicommander\locale" ^
|
||||
--add-data="data\%PACKAGE%.ico;." ^
|
||||
--icon=data\\%PACKAGE%.ico ^
|
||||
--noconfirm ^
|
||||
--clean ^
|
||||
--onedir ^
|
||||
--windowed ^
|
||||
src\\%PACKAGE%\\__main__.py
|
||||
|
||||
rem deactivate
|
||||
3
setup.iss
Executable file → Normal file
3
setup.iss
Executable file → Normal file
@@ -2,12 +2,13 @@
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "RevPi Commander"
|
||||
#define MyAppVersion "0.9.10rc2"
|
||||
#define MyAppVersion "0.9.10rc4"
|
||||
#define MyAppPublisher "Sven Sager"
|
||||
#define MyAppURL "https://revpimodio.org/"
|
||||
#define MyAppICO "data\revpicommander.ico"
|
||||
|
||||
[Setup]
|
||||
SignTool=kSign
|
||||
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId={{21E8D429-0C18-462F-AFC0-56EA664DE629}
|
||||
|
||||
6
setup.py
6
setup.py
@@ -6,9 +6,11 @@ __license__ = "GPLv3"
|
||||
|
||||
from setuptools import find_namespace_packages, setup
|
||||
|
||||
from src.revpicommander import __version__
|
||||
|
||||
setup(
|
||||
name="revpicommander",
|
||||
version="0.9.10rc2",
|
||||
version=__version__,
|
||||
|
||||
packages=find_namespace_packages("src"),
|
||||
package_dir={'': 'src'},
|
||||
@@ -35,7 +37,7 @@ setup(
|
||||
maintainer="Sven Sager",
|
||||
maintainer_email="akira@revpimodio.org",
|
||||
description="GUI for Revolution Pi to upload programs and do IO-Checks",
|
||||
long_description="The RevPiCommander is a GUI tool to manage your revolution Pi over the\n"
|
||||
long_description="The RevPiCommander is a GUI tool to manage your Revolution Pi over the\n"
|
||||
"network. You can search for RevPis in your network, manage the settings\n"
|
||||
"of RevPiPyLoad and do IO checks on your local machine. Developing your\n"
|
||||
"control program is very easy with the developer, upload and debug it\n"
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023 Sven Sager"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "0.9.10rc4"
|
||||
|
||||
@@ -16,6 +16,14 @@ if __package__ == "":
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) == 2 and "--version" in sys.argv:
|
||||
# Catch --version, if this is the only argument (sys.argv[0] is always the script name)
|
||||
from revpicommander import __version__
|
||||
print(__version__)
|
||||
sys.exit(0)
|
||||
|
||||
else:
|
||||
from revpicommander.revpicommander import main
|
||||
|
||||
# Run the main application of this package
|
||||
|
||||
@@ -18,7 +18,8 @@ class BackgroundWorker(QtCore.QThread):
|
||||
steps_done = QtCore.pyqtSignal(int)
|
||||
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)
|
||||
|
||||
def check_cancel(self) -> bool:
|
||||
@@ -28,33 +29,67 @@ class BackgroundWorker(QtCore.QThread):
|
||||
:return: True, if interruption was requested
|
||||
"""
|
||||
if self.isInterruptionRequested():
|
||||
self.status_message.emit(self.tr("User requested cancellation..."))
|
||||
self.status_message.emit(self._interruption_text)
|
||||
self.msleep(750)
|
||||
return True
|
||||
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.setWindowTitle(window_title)
|
||||
diag.btn_box.setEnabled(can_cancel)
|
||||
rc = diag.exec()
|
||||
diag.deleteLater()
|
||||
return rc
|
||||
|
||||
def wait_interruptable(self, seconds=-1) -> None:
|
||||
"""Save function to wait and get the cancel buttons."""
|
||||
def wait_interruptable(self, seconds=-1) -> bool:
|
||||
"""
|
||||
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
|
||||
while counter != 0:
|
||||
counter -= 1
|
||||
self.msleep(250)
|
||||
if self.check_cancel():
|
||||
break
|
||||
return True
|
||||
return False
|
||||
|
||||
def run(self) -> None:
|
||||
"""Worker thread to import pictures from camera."""
|
||||
log.debug("BackgroundWorker.run")
|
||||
self.status_message.emit("Started dummy thread...")
|
||||
self.wait_interruptable(5)
|
||||
self.status_message.emit("Completed dummy thread.")
|
||||
self.wait_interruptable(2)
|
||||
"""Override this function with your logic."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class BackgroundWaiter(BackgroundWorker):
|
||||
"""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):
|
||||
|
||||
@@ -15,20 +15,25 @@ from threading import Lock
|
||||
from uuid import uuid4
|
||||
from xmlrpc.client import Binary, ServerProxy
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
from PyQt5 import QtCore
|
||||
from paramiko.ssh_exception import AuthenticationException
|
||||
|
||||
from . import proginit as pi
|
||||
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."""
|
||||
|
||||
homedir = environ.get("HOME", "") or environ.get("APPDATA", "")
|
||||
"""Home dir of user."""
|
||||
|
||||
|
||||
class ConnectionFail(IntEnum):
|
||||
NO_XML_RPC = 1
|
||||
SSH_CONNECT = 2
|
||||
SSH_AUTH = 4
|
||||
|
||||
|
||||
class WidgetData(IntEnum):
|
||||
address = 260
|
||||
acl_level = 262
|
||||
@@ -194,6 +199,8 @@ class RevPiSettings:
|
||||
class ConnectionManager(QtCore.QThread):
|
||||
"""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()
|
||||
"""New connection established successfully with <class 'ServerProxy'>."""
|
||||
connection_disconnected = QtCore.pyqtSignal()
|
||||
@@ -311,12 +318,12 @@ class ConnectionManager(QtCore.QThread):
|
||||
self.xml_funcs.clear()
|
||||
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.
|
||||
|
||||
: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
|
||||
"""
|
||||
|
||||
@@ -325,50 +332,36 @@ class ConnectionManager(QtCore.QThread):
|
||||
|
||||
ssh_tunnel_server = None
|
||||
ssh_tunnel_port = 0
|
||||
ssh_pass = ""
|
||||
|
||||
socket.setdefaulttimeout(2)
|
||||
socket.setdefaulttimeout(revpi_settings.timeout)
|
||||
|
||||
if revpi_settings.ssh_use_tunnel:
|
||||
while True:
|
||||
diag_ssh_auth = SSHAuth(
|
||||
revpi_settings.ssh_user,
|
||||
"{0}.{1}_{2}".format(
|
||||
settings.applicationName(),
|
||||
settings.organizationName(),
|
||||
revpi_settings.internal_id),
|
||||
parent,
|
||||
)
|
||||
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(
|
||||
revpi_settings.port,
|
||||
revpi_settings.address,
|
||||
revpi_settings.ssh_port
|
||||
)
|
||||
revpi_settings.ssh_saved_password = diag_ssh_auth.in_keyring
|
||||
try:
|
||||
ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(ssh_user, ssh_pass)
|
||||
break
|
||||
ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(revpi_settings.ssh_user, ssh_pass)
|
||||
except AuthenticationException:
|
||||
diag_ssh_auth.remove_saved_password()
|
||||
QtWidgets.QMessageBox.critical(
|
||||
parent, self.tr("Error"), self.tr(
|
||||
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,
|
||||
)
|
||||
)
|
||||
return False
|
||||
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(
|
||||
self.connect_error.emit(
|
||||
self.tr("Error"), self.tr(
|
||||
"Could not establish a SSH connection to server:\n\n{0}"
|
||||
).format(str(e))
|
||||
).format(str(e)),
|
||||
ConnectionFail.SSH_CONNECT,
|
||||
revpi_settings,
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -386,17 +379,30 @@ class ConnectionManager(QtCore.QThread):
|
||||
pi.logger.exception(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
|
||||
QtWidgets.QMessageBox.critical(
|
||||
parent, self.tr("Error"), self.tr(
|
||||
self.connect_error.emit(
|
||||
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!"
|
||||
)
|
||||
),
|
||||
ConnectionFail.NO_XML_RPC,
|
||||
revpi_settings,
|
||||
)
|
||||
|
||||
return False
|
||||
@@ -408,7 +414,6 @@ class ConnectionManager(QtCore.QThread):
|
||||
self.xml_mode = xml_mode
|
||||
|
||||
with self._lck_cli:
|
||||
socket.setdefaulttimeout(revpi_settings.timeout)
|
||||
self.ssh_tunnel_server = ssh_tunnel_server
|
||||
self._cli = sp
|
||||
self._cli_connect.put_nowait((
|
||||
@@ -686,7 +691,8 @@ def import_old_settings():
|
||||
revpi_setting = RevPiSettings(i, settings_storage=old_settings)
|
||||
revpi_setting._settings = settings
|
||||
revpi_setting.save_settings()
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pi.logger.warning("Could not import saved connection {0}".format(i))
|
||||
|
||||
|
||||
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>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../avahisearch.py" line="283"/>
|
||||
<location filename="../avahisearch.py" line="285"/>
|
||||
<source> over SSH</source>
|
||||
<translation> über SSH</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>ConnectionManager</name>
|
||||
<message>
|
||||
<location filename="../helper.py" line="512"/>
|
||||
<location filename="../helper.py" line="524"/>
|
||||
<source>SIMULATING</source>
|
||||
<translation>SIMULATION</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="515"/>
|
||||
<location filename="../helper.py" line="527"/>
|
||||
<source>NOT CONNECTED</source>
|
||||
<translation>NICHT VERBUNDEN</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="532"/>
|
||||
<location filename="../helper.py" line="544"/>
|
||||
<source>SERVER ERROR</source>
|
||||
<translation>SERVER FEHLER</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="557"/>
|
||||
<location filename="../helper.py" line="569"/>
|
||||
<source>RUNNING</source>
|
||||
<translation>LÄUFT</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="559"/>
|
||||
<location filename="../helper.py" line="571"/>
|
||||
<source>PLC FILE NOT FOUND</source>
|
||||
<translation>SPS PROGRAMM NICHT GEFUNDEN</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="561"/>
|
||||
<location filename="../helper.py" line="573"/>
|
||||
<source>NOT RUNNING (NO STATUS)</source>
|
||||
<translation>LÄUFT NICHT (KEIN STATUS)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="563"/>
|
||||
<location filename="../helper.py" line="575"/>
|
||||
<source>PROGRAM KILLED</source>
|
||||
<translation>PROGRAMM GETÖTET</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="565"/>
|
||||
<location filename="../helper.py" line="577"/>
|
||||
<source>PROGRAM TERMED</source>
|
||||
<translation>PROGRAMM BEENDET</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="567"/>
|
||||
<location filename="../helper.py" line="579"/>
|
||||
<source>NOT RUNNING</source>
|
||||
<translation>LÄUFT NICHT</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="569"/>
|
||||
<location filename="../helper.py" line="581"/>
|
||||
<source>FINISHED WITH CODE {0}</source>
|
||||
<translation>BEENDET MIT CODE {0}</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="384"/>
|
||||
<location filename="../helper.py" line="395"/>
|
||||
<source>Error</source>
|
||||
<translation>Fehler</translation>
|
||||
</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.
|
||||
|
||||
Try again.</source>
|
||||
@@ -165,7 +293,7 @@ Try again.</source>
|
||||
Bitte erneut versuchen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="361"/>
|
||||
<location filename="../helper.py" line="359"/>
|
||||
<source>Could not establish a SSH connection to server:
|
||||
|
||||
{0}</source>
|
||||
@@ -174,7 +302,7 @@ Bitte erneut versuchen.</translation>
|
||||
{0}</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../helper.py" line="384"/>
|
||||
<location filename="../helper.py" line="395"/>
|
||||
<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!!!
|
||||
@@ -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>
|
||||
</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>
|
||||
<name>DebugControl</name>
|
||||
@@ -283,90 +429,90 @@ Ungesicherte Änderungen gehen verloren.</translation>
|
||||
<context>
|
||||
<name>RevPiCommander</name>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="222"/>
|
||||
<location filename="../revpicommander.py" line="306"/>
|
||||
<source>Simulator started...</source>
|
||||
<translation>Simulator gestartet...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="231"/>
|
||||
<location filename="../revpicommander.py" line="315"/>
|
||||
<source>Can not start...</source>
|
||||
<translation>Kann nicht gestartet werden...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="424"/>
|
||||
<location filename="../revpicommander.py" line="508"/>
|
||||
<source>Warning</source>
|
||||
<translation>Warnung</translation>
|
||||
</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>
|
||||
<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>
|
||||
<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>
|
||||
<translation>XML-RPC Zugriffsberechtigung in der RevPiPyLoad Konfiguraiton ist zu klein für diese Einstellungen!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="451"/>
|
||||
<location filename="../revpicommander.py" line="535"/>
|
||||
<source>Error</source>
|
||||
<translation>Fehler</translation>
|
||||
</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>
|
||||
<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>
|
||||
<location filename="../revpicommander.py" line="330"/>
|
||||
<location filename="../revpicommander.py" line="414"/>
|
||||
<source>Question</source>
|
||||
<translation>Frage</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="330"/>
|
||||
<location filename="../revpicommander.py" line="414"/>
|
||||
<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>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="343"/>
|
||||
<location filename="../revpicommander.py" line="427"/>
|
||||
<source>Success</source>
|
||||
<translation>Erfolgreich</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="343"/>
|
||||
<location filename="../revpicommander.py" line="427"/>
|
||||
<source>piControl reset executed successfully</source>
|
||||
<translation>piControl wurde erfolgreich zurückgesetzt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="350"/>
|
||||
<location filename="../revpicommander.py" line="434"/>
|
||||
<source>piControl reset could not be executed successfully</source>
|
||||
<translation>piControl konnte nicht zurückgesetzt werden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="398"/>
|
||||
<location filename="../revpicommander.py" line="482"/>
|
||||
<source>Reset to piCtory defaults...</source>
|
||||
<translation>Standardwerte von piCtory laden...</translation>
|
||||
</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>
|
||||
<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>
|
||||
<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!
|
||||
You need at least level 1 to read or level 3 to write.</source>
|
||||
<translation>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="451"/>
|
||||
<location filename="../revpicommander.py" line="535"/>
|
||||
<source>Can not load piCtory configuration.
|
||||
Did you create a hardware configuration? Please check this in piCtory!</source>
|
||||
<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>
|
||||
<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>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="222"/>
|
||||
<location filename="../revpicommander.py" line="306"/>
|
||||
<source>The simulator is running!
|
||||
|
||||
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>
|
||||
</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>
|
||||
<translation>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="398"/>
|
||||
<location filename="../revpicommander.py" line="482"/>
|
||||
<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>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="398"/>
|
||||
<location filename="../revpicommander.py" line="482"/>
|
||||
<source>zero</source>
|
||||
<translation>null</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpicommander.py" line="398"/>
|
||||
<location filename="../revpicommander.py" line="482"/>
|
||||
<source>piCtory default</source>
|
||||
<translation>piCtory Standardwerte</translation>
|
||||
</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>
|
||||
<name>RevPiFiles</name>
|
||||
@@ -547,6 +708,11 @@ Wählen Sie 'Ja' zum Überschreiben, 'Nein' um nur fehlende
|
||||
<source>Choose a local directory first.</source>
|
||||
<translation>Lokales Verzeichnis wählen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpifiles.py" line="127"/>
|
||||
<source>File transfer...</source>
|
||||
<translation>Dateiübertragung...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>RevPiInfo</name>
|
||||
@@ -668,17 +834,31 @@ Ungesicherte Änderungen gehen verloren.</translation>
|
||||
<translation type="obsolete">Neue Verbindung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpiplclist.py" line="102"/>
|
||||
<location filename="../revpiplclist.py" line="282"/>
|
||||
<source>Question</source>
|
||||
<translation>Frage</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpiplclist.py" line="102"/>
|
||||
<location filename="../revpiplclist.py" line="121"/>
|
||||
<source>Do you really want to quit?
|
||||
Unsaved changes will be lost.</source>
|
||||
<translation>Soll das Fenster wirklich geschlossen werden?
|
||||
Ungesicherte Änderungen gehen verloren.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpiplclist.py" line="282"/>
|
||||
<source>If you remote this folder, all containing elements will be removed, too.
|
||||
|
||||
Do you want to delete folder and all elements?</source>
|
||||
<translation>Wird dieser Ordner gelöscht, betrifft dies auch alle Elemente im Ordner.
|
||||
|
||||
Wollen sie den Ordner und alle Elemente löschen?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../revpiplclist.py" line="324"/>
|
||||
<source>New folder</source>
|
||||
<translation>Neuer Ordner</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>RevPiProgram</name>
|
||||
@@ -955,12 +1135,12 @@ Dies ist kein Fehler, wenn das SPS Startprogramm bereits auf dem Rev Pi ist. Pr
|
||||
<context>
|
||||
<name>SSHAuth</name>
|
||||
<message>
|
||||
<location filename="../sshauth.py" line="49"/>
|
||||
<location filename="../sshauth.py" line="51"/>
|
||||
<source>Could not save password</source>
|
||||
<translation>Konnte Kennwort nicht speichern</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../sshauth.py" line="49"/>
|
||||
<location filename="../sshauth.py" line="51"/>
|
||||
<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.
|
||||
@@ -1049,7 +1229,7 @@ Dies ist kein Fehler von RevPi Commander.</translation>
|
||||
<message>
|
||||
<location filename="../../../ui_dev/backgroundworker.ui" line="14"/>
|
||||
<source>File transfer...</source>
|
||||
<translation>Dateiübertragung...</translation>
|
||||
<translation type="obsolete">Dateiübertragung...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1684,25 +1864,30 @@ applicable law.
|
||||
<translation>SSH Authentifizierung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../ui_dev/sshauth.ui" line="26"/>
|
||||
<location filename="../../../ui_dev/sshauth.ui" line="29"/>
|
||||
<source>SSH username:</source>
|
||||
<translation>SSH Benutzername:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../ui_dev/sshauth.ui" line="33"/>
|
||||
<location filename="../../../ui_dev/sshauth.ui" line="36"/>
|
||||
<source>SSH password:</source>
|
||||
<translation>SSH Passwort:</translation>
|
||||
</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>
|
||||
<translation>Benutzername und Kennwort werden im Passwortspeicher vom Betriebssystem gesichert.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../ui_dev/sshauth.ui" line="56"/>
|
||||
<location filename="../../../ui_dev/sshauth.ui" line="59"/>
|
||||
<source>Save username and password</source>
|
||||
<translation>Benutzername und Kennwort merken</translation>
|
||||
</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>
|
||||
<name>wid_debugcontrol</name>
|
||||
|
||||
@@ -89,11 +89,12 @@ parser.add_argument(
|
||||
"-f", "--logfile", dest="logfile",
|
||||
help="Save log entries to this file"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", action="count", dest="verbose", default=0,
|
||||
help="Switch on verbose logging"
|
||||
)
|
||||
# The __main__ script will process the version number argument
|
||||
parser.add_argument("--version", action="store_true", help="Print version number of program and exit")
|
||||
pargs = parser.parse_args()
|
||||
|
||||
# Check important objects and set to default if they do not exists
|
||||
|
||||
@@ -5,28 +5,48 @@
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "0.9.10rc2"
|
||||
|
||||
import webbrowser
|
||||
from os.path import dirname, join
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from revpicommander.backgroundworker import BackgroundWaiter
|
||||
from . import __version__
|
||||
from . import helper
|
||||
from . import proginit as pi
|
||||
from . import revpilogfile
|
||||
from .avahisearch import AvahiSearch
|
||||
from .debugcontrol import DebugControl
|
||||
from .helper import RevPiSettings
|
||||
from .helper import ConnectionFail, RevPiSettings
|
||||
from .revpifiles import RevPiFiles
|
||||
from .revpiinfo import RevPiInfo
|
||||
from .revpioption import RevPiOption
|
||||
from .revpiplclist import RevPiPlcList
|
||||
from .revpiprogram import RevPiProgram
|
||||
from .simulator import Simulator
|
||||
from .sshauth import SSHAuth
|
||||
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):
|
||||
"""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)
|
||||
|
||||
helper.cm.connect_error.connect(self.on_cm_connect_error)
|
||||
helper.cm.connection_disconnected.connect(self.on_cm_connection_disconnected)
|
||||
helper.cm.connection_disconnecting.connect(self.on_cm_connection_disconnecting)
|
||||
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
|
||||
|
||||
@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)
|
||||
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))
|
||||
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()
|
||||
def on_act_connections_triggered(self):
|
||||
"""Edit saved connections to Revolution Pi devices."""
|
||||
@@ -202,7 +286,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
if self.diag_search.just_save:
|
||||
self.diag_connections.exec_with_presets(self.diag_search.connect_settings)
|
||||
else:
|
||||
helper.cm.pyload_connect(self.diag_search.connect_settings, self)
|
||||
self._pyload_connect(self.diag_search.connect_settings)
|
||||
|
||||
self._load_men_connections()
|
||||
|
||||
@@ -361,7 +445,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
@QtCore.pyqtSlot(QtWidgets.QAction)
|
||||
def on_men_connections_triggered(self, action: QtWidgets.QAction):
|
||||
"""A connection is selected in the men_connections menu."""
|
||||
helper.cm.pyload_connect(action.data(), self)
|
||||
self._pyload_connect(action.data())
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_act_webpage_triggered(self):
|
||||
|
||||
@@ -124,7 +124,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
return
|
||||
|
||||
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
|
||||
|
||||
if uploader.ec == 0:
|
||||
|
||||
@@ -37,13 +37,30 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
self.lbl_port.setText(self.lbl_port.text().format(self.__default_port))
|
||||
self.sbx_port.setValue(self.__default_port)
|
||||
|
||||
# Dirty workaround to remove default button to prevent action on ENTER key, while user edit texts
|
||||
self.__btn_dummy = QtWidgets.QPushButton(self)
|
||||
self.__btn_dummy.setVisible(False)
|
||||
self.__btn_dummy.setDefault(True)
|
||||
|
||||
def _load_cbb_folder(self):
|
||||
"""Clean up all entries and reload existing ones from treeview."""
|
||||
self.cbb_folder.blockSignals(True)
|
||||
|
||||
self.cbb_folder.clear()
|
||||
self.cbb_folder.addItem("")
|
||||
for i in range(self.tre_connections.topLevelItemCount()):
|
||||
item = self.tre_connections.topLevelItem(i)
|
||||
if item.type() != NodeType.DIR:
|
||||
continue
|
||||
self.cbb_folder.addItem(item.text(0))
|
||||
|
||||
self.cbb_folder.blockSignals(False)
|
||||
|
||||
def _load_settings(self):
|
||||
"""Load values to GUI widgets."""
|
||||
pi.logger.debug("RevPiPlcList._load_settings")
|
||||
|
||||
self.tre_connections.clear()
|
||||
self.cbb_folder.clear()
|
||||
self.cbb_folder.addItem("")
|
||||
|
||||
# Get length of array and close it, the RevPiSettings-class need it
|
||||
count_settings = helper.settings.beginReadArray("connections")
|
||||
@@ -67,7 +84,6 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
|
||||
sub_folder.setText(0, folder)
|
||||
self.tre_connections.addTopLevelItem(sub_folder)
|
||||
self.cbb_folder.addItem(folder)
|
||||
|
||||
sub_folder.addChild(con_item)
|
||||
else:
|
||||
@@ -76,7 +92,6 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
self.tre_connections.expandAll()
|
||||
self.changes = False
|
||||
|
||||
if self.tre_connections.topLevelItemCount() == 0:
|
||||
self._edit_state()
|
||||
|
||||
def accept(self) -> None:
|
||||
@@ -150,6 +165,8 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
|
||||
def _edit_state(self):
|
||||
"""Set enabled status of all controls, depending on selected item."""
|
||||
pi.logger.debug("RevPiPlcList._edit_state")
|
||||
|
||||
item = self.tre_connections.currentItem()
|
||||
if item is None:
|
||||
up_ok = False
|
||||
@@ -171,12 +188,16 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
|
||||
self.btn_up.setEnabled(up_ok)
|
||||
self.btn_down.setEnabled(down_ok)
|
||||
self.btn_delete.setEnabled(con_item)
|
||||
self.btn_delete.setEnabled(con_item or dir_item)
|
||||
self.txt_name.setEnabled(con_item)
|
||||
self.txt_address.setEnabled(con_item)
|
||||
self.sbx_port.setEnabled(con_item)
|
||||
self.sbx_timeout.setEnabled(con_item)
|
||||
self.cbb_folder.setEnabled(con_item or dir_item)
|
||||
self.cbb_folder.setEditable(dir_item)
|
||||
if self.cbb_folder.isEditable():
|
||||
# Disable auto complete, this would override a new typed name with existing one
|
||||
self.cbb_folder.setCompleter(None)
|
||||
|
||||
self.cbx_ssh_use_tunnel.setEnabled(con_item)
|
||||
self.sbx_ssh_port.setEnabled(con_item)
|
||||
@@ -203,22 +224,25 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
if 0 <= new_index < dir_item.childCount():
|
||||
item = dir_item.takeChild(index)
|
||||
dir_item.insertChild(new_index, item)
|
||||
self.tre_connections.expandItem(dir_item)
|
||||
else:
|
||||
index = self.tre_connections.indexOfTopLevelItem(item)
|
||||
new_index = index + count
|
||||
if 0 <= index < self.tre_connections.topLevelItemCount():
|
||||
item = self.tre_connections.takeTopLevelItem(index)
|
||||
self.tre_connections.insertTopLevelItem(new_index, item)
|
||||
if item.type() == NodeType.DIR:
|
||||
# Expand a moved dir node, it would be collapsed after move
|
||||
self.tre_connections.expandItem(item)
|
||||
|
||||
self.tre_connections.setCurrentItem(item)
|
||||
self._edit_state()
|
||||
|
||||
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, QtWidgets.QTreeWidgetItem)
|
||||
def on_tre_connections_currentItemChanged(
|
||||
self, current: QtWidgets.QTreeWidgetItem, previous: QtWidgets.QTreeWidgetItem):
|
||||
|
||||
self._edit_state()
|
||||
self._load_cbb_folder()
|
||||
|
||||
if current and current.type() == NodeType.CON:
|
||||
self.__current_item = current
|
||||
|
||||
@@ -255,9 +279,9 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
@QtCore.pyqtSlot()
|
||||
def on_btn_delete_clicked(self):
|
||||
"""Remove selected entry."""
|
||||
item = self.tre_connections.currentItem()
|
||||
if item and item.type() == NodeType.CON:
|
||||
|
||||
def remove_item(item: QtWidgets.QTreeWidgetItem):
|
||||
"""Remove CON item and save keyring actions for save action."""
|
||||
revpi_settings = item.data(0, WidgetData.revpi_settings) # type: RevPiSettings
|
||||
if revpi_settings.ssh_saved_password:
|
||||
# Cleans up keyring in save function
|
||||
@@ -270,7 +294,27 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
index = self.tre_connections.indexOfTopLevelItem(item)
|
||||
self.tre_connections.takeTopLevelItem(index)
|
||||
|
||||
self._edit_state()
|
||||
item_to_remove = self.tre_connections.currentItem()
|
||||
|
||||
if item_to_remove and item_to_remove.type() == NodeType.DIR:
|
||||
if item_to_remove.childCount():
|
||||
rc = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Question"), self.tr(
|
||||
"If you remote this folder, all containing elements will be removed, too. \n\n"
|
||||
"Do you want to delete folder and all elements?"
|
||||
),
|
||||
)
|
||||
if rc != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
|
||||
while item_to_remove.childCount() > 0:
|
||||
remove_item(item_to_remove.child(0))
|
||||
|
||||
item_index = self.tre_connections.indexOfTopLevelItem(item_to_remove)
|
||||
self.tre_connections.takeTopLevelItem(item_index)
|
||||
|
||||
elif item_to_remove and item_to_remove.type() == NodeType.CON:
|
||||
remove_item(item_to_remove)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_btn_add_clicked(self, settings_preset: RevPiSettings = None):
|
||||
@@ -291,6 +335,20 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
self.txt_name.setFocus()
|
||||
self.txt_name.selectAll()
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_btn_add_dir_clicked(self):
|
||||
"""Add a new folder."""
|
||||
folder_text = self.tr("New folder")
|
||||
sub_folder = QtWidgets.QTreeWidgetItem(NodeType.DIR)
|
||||
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
|
||||
sub_folder.setText(0, folder_text)
|
||||
|
||||
self.tre_connections.addTopLevelItem(sub_folder)
|
||||
self.tre_connections.setCurrentItem(sub_folder)
|
||||
self.cbb_folder.setFocus()
|
||||
|
||||
self.changes = True
|
||||
|
||||
@QtCore.pyqtSlot(str)
|
||||
def on_txt_name_textEdited(self, text):
|
||||
if self.__current_item.type() != NodeType.CON:
|
||||
@@ -349,65 +407,47 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
settings.ssh_user = text
|
||||
self.changes = True
|
||||
|
||||
@QtCore.pyqtSlot(str)
|
||||
def on_cbb_folder_currentIndexChanged(self, text: str):
|
||||
pi.logger.debug("RevPiPlcList.on_cbb_folder_currentIndexChanged({0})".format(text))
|
||||
|
||||
if self.__current_item.type() == NodeType.CON:
|
||||
new_dir_node = self._get_folder_item(text)
|
||||
current_dir_node = self.__current_item.parent()
|
||||
if current_dir_node == new_dir_node:
|
||||
# No change required, both nodes are the same
|
||||
return
|
||||
|
||||
change_item = self.__current_item
|
||||
self.tre_connections.blockSignals(True)
|
||||
self.changes = True
|
||||
|
||||
if current_dir_node:
|
||||
# Move an element to other folder or root
|
||||
index = current_dir_node.indexOfChild(change_item)
|
||||
change_item = current_dir_node.takeChild(index)
|
||||
|
||||
else:
|
||||
# Move a root element to a folder
|
||||
index = self.tre_connections.indexOfTopLevelItem(change_item)
|
||||
change_item = self.tre_connections.takeTopLevelItem(index)
|
||||
|
||||
if text == "":
|
||||
self.tre_connections.addTopLevelItem(change_item)
|
||||
|
||||
else:
|
||||
new_dir_node.addChild(change_item)
|
||||
|
||||
self.tre_connections.blockSignals(False)
|
||||
self.tre_connections.setCurrentItem(change_item)
|
||||
|
||||
@QtCore.pyqtSlot(str)
|
||||
def on_cbb_folder_editTextChanged(self, text: str):
|
||||
pi.logger.debug("RevPiPlcList.on_cbb_folder_editTextChanged({0})".format(text))
|
||||
|
||||
if self.__current_item.type() == NodeType.DIR:
|
||||
if self.__current_item.type() == NodeType.DIR and self.__current_item.text(0) != text:
|
||||
# We just have to rename the dir node
|
||||
self.__current_item.setText(0, text)
|
||||
|
||||
elif self.__current_item.type() == NodeType.CON:
|
||||
sub_folder = self._get_folder_item(text)
|
||||
dir_node = self.__current_item.parent()
|
||||
if dir_node:
|
||||
if dir_node.text(0) == text:
|
||||
# It is the same folder
|
||||
return
|
||||
|
||||
if text != "" and dir_node.childCount() == 1 and not sub_folder:
|
||||
# The folder hold just one item, so we can rename that
|
||||
for i in range(self.cbb_folder.count()):
|
||||
if self.cbb_folder.itemText(i) == dir_node.text(0):
|
||||
self.cbb_folder.setItemText(i, text)
|
||||
break
|
||||
dir_node.setText(0, text)
|
||||
return
|
||||
|
||||
index = dir_node.indexOfChild(self.__current_item)
|
||||
self.__current_item = dir_node.takeChild(index)
|
||||
|
||||
elif text != "":
|
||||
# Move root to folder
|
||||
index = self.tre_connections.indexOfTopLevelItem(self.__current_item)
|
||||
self.__current_item = self.tre_connections.takeTopLevelItem(index)
|
||||
|
||||
else:
|
||||
# Root stays root
|
||||
return
|
||||
|
||||
if text == "":
|
||||
self.tre_connections.addTopLevelItem(self.__current_item)
|
||||
|
||||
else:
|
||||
if sub_folder is None:
|
||||
sub_folder = QtWidgets.QTreeWidgetItem(NodeType.DIR)
|
||||
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
|
||||
sub_folder.setText(0, text)
|
||||
self.tre_connections.addTopLevelItem(sub_folder)
|
||||
self.cbb_folder.addItem(text)
|
||||
sub_folder.addChild(self.__current_item)
|
||||
|
||||
if dir_node and dir_node.childCount() == 0:
|
||||
# Remove empty folders
|
||||
for i in range(self.cbb_folder.count()):
|
||||
if self.cbb_folder.itemText(i) == dir_node.text(0):
|
||||
self.cbb_folder.removeItem(i)
|
||||
break
|
||||
index = self.tre_connections.indexOfTopLevelItem(dir_node)
|
||||
self.tre_connections.takeTopLevelItem(index)
|
||||
|
||||
self.tre_connections.setCurrentItem(self.__current_item)
|
||||
self.cbb_folder.setFocus()
|
||||
self.changes = True
|
||||
|
||||
# endregion # # # # #
|
||||
|
||||
@@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_diag_backgroundworker(object):
|
||||
def setupUi(self, diag_backgroundworker):
|
||||
diag_backgroundworker.setObjectName("diag_backgroundworker")
|
||||
diag_backgroundworker.resize(418, 97)
|
||||
diag_backgroundworker.resize(424, 104)
|
||||
diag_backgroundworker.setModal(True)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(diag_backgroundworker)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
@@ -37,8 +37,7 @@ class Ui_diag_backgroundworker(object):
|
||||
QtCore.QMetaObject.connectSlotsByName(diag_backgroundworker)
|
||||
|
||||
def retranslateUi(self, diag_backgroundworker):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_backgroundworker.setWindowTitle(_translate("diag_backgroundworker", "File transfer..."))
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,7 @@ class Ui_diag_connections(object):
|
||||
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.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.cbb_folder.setObjectName("cbb_folder")
|
||||
self.cbb_folder.addItem("")
|
||||
self.cbb_folder.setItemText(0, "")
|
||||
@@ -115,39 +116,41 @@ class Ui_diag_connections(object):
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.vl_edit.addItem(spacerItem)
|
||||
self.btn_up = QtWidgets.QPushButton(diag_connections)
|
||||
self.btn_up.setText("")
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(":/action/ico/arrow-up.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.btn_up.setIcon(icon)
|
||||
self.btn_up.setObjectName("btn_up")
|
||||
self.vl_edit.addWidget(self.btn_up)
|
||||
self.btn_down = QtWidgets.QPushButton(diag_connections)
|
||||
self.btn_down.setText("")
|
||||
icon1 = QtGui.QIcon()
|
||||
icon1.addPixmap(QtGui.QPixmap(":/action/ico/arrow-down.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.btn_down.setIcon(icon1)
|
||||
self.btn_down.setObjectName("btn_down")
|
||||
self.vl_edit.addWidget(self.btn_down)
|
||||
self.btn_delete = QtWidgets.QPushButton(diag_connections)
|
||||
self.btn_delete.setText("")
|
||||
icon2 = QtGui.QIcon()
|
||||
icon2.addPixmap(QtGui.QPixmap(":/action/ico/edit-delete.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
icon2.addPixmap(QtGui.QPixmap(":/action/ico/edit-delete-6.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.btn_delete.setIcon(icon2)
|
||||
self.btn_delete.setObjectName("btn_delete")
|
||||
self.vl_edit.addWidget(self.btn_delete)
|
||||
self.btn_add = QtWidgets.QPushButton(diag_connections)
|
||||
self.btn_add.setText("")
|
||||
self.btn_add_dir = QtWidgets.QPushButton(diag_connections)
|
||||
icon3 = QtGui.QIcon()
|
||||
icon3.addPixmap(QtGui.QPixmap(":/action/ico/edit-add.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.btn_add.setIcon(icon3)
|
||||
icon3.addPixmap(QtGui.QPixmap(":/action/ico/folder-open.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.btn_add_dir.setIcon(icon3)
|
||||
self.btn_add_dir.setObjectName("btn_add_dir")
|
||||
self.vl_edit.addWidget(self.btn_add_dir)
|
||||
self.btn_add = QtWidgets.QPushButton(diag_connections)
|
||||
icon4 = QtGui.QIcon()
|
||||
icon4.addPixmap(QtGui.QPixmap(":/action/ico/edit-add.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.btn_add.setIcon(icon4)
|
||||
self.btn_add.setObjectName("btn_add")
|
||||
self.vl_edit.addWidget(self.btn_add)
|
||||
self.gridLayout.addLayout(self.vl_edit, 0, 1, 1, 1)
|
||||
|
||||
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
|
||||
self.btn_box.accepted.connect(diag_connections.accept) # type: ignore
|
||||
QtCore.QMetaObject.connectSlotsByName(diag_connections)
|
||||
|
||||
def retranslateUi(self, diag_connections):
|
||||
|
||||
@@ -15,8 +15,9 @@ class Ui_diag_sshauth(object):
|
||||
def setupUi(self, diag_sshauth):
|
||||
diag_sshauth.setObjectName("diag_sshauth")
|
||||
diag_sshauth.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||
diag_sshauth.resize(363, 163)
|
||||
diag_sshauth.resize(331, 225)
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(diag_sshauth)
|
||||
self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.wid_password = QtWidgets.QWidget(diag_sshauth)
|
||||
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.setObjectName("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.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.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.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__":
|
||||
|
||||
@@ -6,13 +6,10 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>418</width>
|
||||
<height>97</height>
|
||||
<width>424</width>
|
||||
<height>104</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>File transfer...</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
||||
BIN
ui_dev/ico/edit-delete-6.ico
Normal file
BIN
ui_dev/ico/edit-delete-6.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
ui_dev/ico/process-stop.ico
Executable file → Normal file
BIN
ui_dev/ico/process-stop.ico
Executable file → Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -9,6 +9,7 @@
|
||||
<file>ico/file-python.ico</file>
|
||||
</qresource>
|
||||
<qresource prefix="action">
|
||||
<file>ico/edit-delete-6.ico</file>
|
||||
<file>ico/applications-utilities.ico</file>
|
||||
<file>ico/edit-find.ico</file>
|
||||
<file>ico/process-stop.ico</file>
|
||||
|
||||
@@ -108,6 +108,9 @@
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string/>
|
||||
@@ -218,9 +221,6 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_up">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ressources.qrc">
|
||||
<normaloff>:/action/ico/arrow-up.ico</normaloff>:/action/ico/arrow-up.ico</iconset>
|
||||
@@ -229,9 +229,6 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_down">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ressources.qrc">
|
||||
<normaloff>:/action/ico/arrow-down.ico</normaloff>:/action/ico/arrow-down.ico</iconset>
|
||||
@@ -240,20 +237,22 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_delete">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ressources.qrc">
|
||||
<normaloff>:/action/ico/edit-delete.ico</normaloff>:/action/ico/edit-delete.ico</iconset>
|
||||
<normaloff>:/action/ico/edit-delete-6.ico</normaloff>:/action/ico/edit-delete-6.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_add_dir">
|
||||
<property name="icon">
|
||||
<iconset resource="ressources.qrc">
|
||||
<normaloff>:/action/ico/folder-open.ico</normaloff>:/action/ico/folder-open.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_add">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="ressources.qrc">
|
||||
<normaloff>:/action/ico/edit-add.ico</normaloff>:/action/ico/edit-add.ico</iconset>
|
||||
@@ -268,22 +267,6 @@
|
||||
<include location="ressources.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>btn_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>diag_connections</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>btn_box</sender>
|
||||
<signal>rejected()</signal>
|
||||
@@ -291,8 +274,8 @@
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
<x>322</x>
|
||||
<y>564</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
@@ -300,5 +283,21 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>btn_box</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>diag_connections</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>254</x>
|
||||
<y>564</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>363</width>
|
||||
<height>163</height>
|
||||
<width>331</width>
|
||||
<height>225</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>SSH authentication</string>
|
||||
</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>
|
||||
<widget class="QWidget" name="wid_password" native="true">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
@@ -67,6 +70,22 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
Reference in New Issue
Block a user