diff --git a/.hgignore b/.hgignore
index 9e0632f..a10df60 100644
--- a/.hgignore
+++ b/.hgignore
@@ -3,6 +3,7 @@ syntax: glob
deb_dist/*
dist/*
revpipycontrol.egg-info/*
-doc/*
deb/*
.eric6project/*
+*.directory
+*.mo
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..621e3bb
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,41 @@
+
+
+Table of contents
+
+
+
+
+Table of contents
+
+
+
+
+Modules
+
+
\ No newline at end of file
diff --git a/doc/mytools.html b/doc/mytools.html
new file mode 100644
index 0000000..c8a2a96
--- /dev/null
+++ b/doc/mytools.html
@@ -0,0 +1,59 @@
+
+
+mytools
+
+
+
+
+mytools
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+addroot
+addroot(filename)
+
+Hängt root-dir der Anwendung vor Dateinamen.
+
+ Je nach Ausführungsart der Anwendung muss das root-dir über
+ andere Arten abgerufen werden.
+
+- filename
+-
+Datei oder Ordnername
+
+
+- Returns:
+-
+root dir
+
+
+
+
+
+gettrans
+gettrans(proglang=None)
+
+
+
+
\ No newline at end of file
diff --git a/doc/revpicheckclient.html b/doc/revpicheckclient.html
new file mode 100644
index 0000000..7b5ca6f
--- /dev/null
+++ b/doc/revpicheckclient.html
@@ -0,0 +1,294 @@
+
+
+revpicheckclient
+
+
+
+
+revpicheckclient
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+RevPiCheckClient
+
+
+Derived from
+tkinter.Frame
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+| RevPiCheckClient |
+Instantiiert MyApp-Klasse. |
+
+| __chval |
+Schreibt neuen Output Wert auf den RevPi. |
+
+| __hidewin |
+Verbergt übergebenes Fenster. |
+
+| __saveoldvalue |
+Speichert bei Keypress aktuellen Wert für wiederherstellung. |
+
+| __showwin |
+Zeigt oder verbergt übergebenes Fenster. |
+
+| __spinboxkey |
+Prüft die Eingabe auf plausibilität. |
+
+| _createiogroup |
+Erstellt IO-Gruppen. |
+
+| _createwidgets |
+Erstellt den Fensterinhalt. |
+
+| _onfrmconf |
+Erstellt Fenster in einem Canvas. |
+
+| _warnwrite |
+Warnung für Benutzer über Schreibfunktion einmal fragen. |
+
+| _workvalues |
+Alle Werte der Inputs und Outputs abrufen. |
+
+| hideallwindows |
+Versteckt alle Fenster. |
+
+| readvalues |
+Ruft nur Input Werte von RevPi ab und aktualisiert Fenster. |
+
+| refreshvalues |
+Ruft alle IO Werte von RevPi ab und aktualisiert Fenster. |
+
+| tmr_workvalues |
+Timer für zyklische Abfrage. |
+
+| toggleauto |
+Schaltet zwischen Autorefresh um und aktualisiert Widgets. |
+
+| togglewrite |
+Schaltet zwischen DoWrite um und aktiviert Schreibfunktion. |
+
+| validatereturn |
+Überprüft die Rückgaben der setvalue Funktion. |
+
+| writevalues |
+Schreibt geänderte Outputs auf den RevPi. |
+
+
+
+Static Methods
+
+
+
+RevPiCheckClient (Constructor)
+RevPiCheckClient(master, xmlcli, xmlmode=0)
+
+Instantiiert MyApp-Klasse.
+
+
+RevPiCheckClient.__chval
+__chval(device, io, event=None)
+
+Schreibt neuen Output Wert auf den RevPi.
+
+
+RevPiCheckClient.__hidewin
+__hidewin(win, event=None)
+
+Verbergt übergebenes Fenster.
+
+- win
+-
+Fenster zum verbergen
+
- event
+-
+Tkinter Event
+
+
+
+RevPiCheckClient.__saveoldvalue
+__saveoldvalue(event, tkvar)
+
+Speichert bei Keypress aktuellen Wert für wiederherstellung.
+
+
+RevPiCheckClient.__showwin
+__showwin(win)
+
+Zeigt oder verbergt übergebenes Fenster.
+
+- win
+-
+Fenster zum anzeigen/verbergen
+
+
+
+RevPiCheckClient.__spinboxkey
+__spinboxkey(device, io, event=None)
+
+Prüft die Eingabe auf plausibilität.
+
+- event
+-
+tkinter Event
+
- io
+-
+IO Liste mit tkinter Variable
+
+
+
+RevPiCheckClient._createiogroup
+_createiogroup(device, frame, iotype)
+
+Erstellt IO-Gruppen.
+
+- device
+-
+Deviceposition
+
- frame
+-
+tkinter Frame
+
- iotype
+-
+'inp' oder 'out' als str()
+
+
+
+RevPiCheckClient._createwidgets
+_createwidgets()
+
+Erstellt den Fensterinhalt.
+
+
+RevPiCheckClient._onfrmconf
+_onfrmconf(canvas)
+
+Erstellt Fenster in einem Canvas.
+
+- canvas
+-
+Canvas in dem Objekte erstellt werden sollen
+
+
+
+RevPiCheckClient._warnwrite
+_warnwrite()
+
+Warnung für Benutzer über Schreibfunktion einmal fragen.
+
+- Returns:
+-
+True, wenn Warnung einmal mit OK bestätigt wurde
+
+
+
+RevPiCheckClient._workvalues
+_workvalues(io_dicts=None, writeout=False)
+
+Alle Werte der Inputs und Outputs abrufen.
+
+- io_dicts
+-
+Arbeit nur für dieses Dict()
+
- writeout
+-
+Änderungen auf RevPi schreiben
+
+
+- Returns:
+-
+None
+
+
+
+RevPiCheckClient.hideallwindows
+hideallwindows()
+
+Versteckt alle Fenster.
+
+
+RevPiCheckClient.readvalues
+readvalues()
+
+Ruft nur Input Werte von RevPi ab und aktualisiert Fenster.
+
+
+RevPiCheckClient.refreshvalues
+refreshvalues()
+
+Ruft alle IO Werte von RevPi ab und aktualisiert Fenster.
+
+
+RevPiCheckClient.tmr_workvalues
+tmr_workvalues()
+
+Timer für zyklische Abfrage.
+
+- Returns:
+-
+None
+
+
+
+RevPiCheckClient.toggleauto
+toggleauto()
+
+Schaltet zwischen Autorefresh um und aktualisiert Widgets.
+
+
+RevPiCheckClient.togglewrite
+togglewrite()
+
+Schaltet zwischen DoWrite um und aktiviert Schreibfunktion.
+
+
+RevPiCheckClient.validatereturn
+validatereturn(returnlist)
+
+Überprüft die Rückgaben der setvalue Funktion.
+
+- returnlist
+-
+list() der xml Rückgabe
+
+
+
+RevPiCheckClient.writevalues
+writevalues()
+
+Schreibt geänderte Outputs auf den RevPi.
+
+
+
+
\ No newline at end of file
diff --git a/doc/revpiinfo.html b/doc/revpiinfo.html
new file mode 100644
index 0000000..ebc2484
--- /dev/null
+++ b/doc/revpiinfo.html
@@ -0,0 +1,99 @@
+
+
+revpiinfo
+
+
+
+
+revpiinfo
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+RevPiInfo
+
+
+Derived from
+tkinter.Frame
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+Static Methods
+
+
+
+RevPiInfo (Constructor)
+RevPiInfo(master, xmlcli, version)
+
+Init RevPiLogfile-Class.
+
+
+RevPiInfo._checkclose
+_checkclose(event=None)
+
+Prüft ob Fenster beendet werden soll.
+
+- event
+-
+tkinter-Event
+
+
+
+RevPiInfo._createwidgets
+_createwidgets(extended=False)
+
+Erstellt alle Widgets.
+
+
+RevPiInfo.visitwebsite
+visitwebsite(event=None)
+
+Öffnet auf dem System einen Webbrowser zur Projektseite.
+
+
+
+
\ No newline at end of file
diff --git a/doc/revpilogfile.html b/doc/revpilogfile.html
new file mode 100644
index 0000000..3e0242b
--- /dev/null
+++ b/doc/revpilogfile.html
@@ -0,0 +1,164 @@
+
+
+revpilogfile
+
+
+
+
+revpilogfile
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+RevPiLogfile
+
+
+Derived from
+tkinter.Frame
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+Static Methods
+
+
+
+RevPiLogfile (Constructor)
+RevPiLogfile(master, xmlcli)
+
+Init RevPiLogfile-Class.
+
+
+RevPiLogfile._checkclose
+_checkclose(event=None)
+
+Prüft ob Fenster beendet werden soll.
+
+- event
+-
+tkinter-Event
+
+
+
+RevPiLogfile._createwidgets
+_createwidgets()
+
+Erstellt alle Widgets.
+
+
+RevPiLogfile._load_log
+_load_log(textwidget, xmlcall, startposition, full)
+
+Läd die angegebenen Logfiles herunter.
+
+- textwidget
+-
+Widget in das Logs eingefügt werden sollen
+
- xmlcall
+-
+xmlrpc Funktion zum Abrufen der Logdaten
+
- startposition
+-
+Startposition ab der Logdaten kommen sollen
+
- full
+-
+Komplettes Logbuch laden
+
+
+- Returns:
+-
+Ende der Datei (neue Startposition)
+
+
+
+RevPiLogfile.btn_clearapp
+btn_clearapp()
+
+Leert die Logliste der App.
+
+
+RevPiLogfile.btn_clearplc
+btn_clearplc()
+
+Leert die Logliste des PLC.
+
+
+RevPiLogfile.get_applog
+get_applog(full=False)
+
+Ruft App Logbuch ab.
+
+- full
+-
+Ganzes Logbuch laden
+
+
+
+RevPiLogfile.get_plclog
+get_plclog(full=False)
+
+Ruft PLC Logbuch ab.
+
+- full
+-
+Ganzes Logbuch laden
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/revpioption.html b/doc/revpioption.html
new file mode 100644
index 0000000..1c42b8d
--- /dev/null
+++ b/doc/revpioption.html
@@ -0,0 +1,164 @@
+
+
+revpioption
+
+
+
+
+revpioption
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+RevPiOption
+
+
+Derived from
+tkinter.Frame
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+Static Methods
+
+
+
+RevPiOption (Constructor)
+RevPiOption(master, xmlcli)
+
+Init RevPiOption-Class.
+
+- Returns:
+-
+None
+
+
+
+RevPiOption._changesdone
+_changesdone()
+
+Prüft ob sich die Einstellungen geändert haben.
+
+- Returns:
+-
+True, wenn min. eine Einstellung geändert wurde
+
+
+
+RevPiOption._checkclose
+_checkclose(event=None)
+
+Prüft ob Fenster beendet werden soll.
+
+- event
+-
+tkinter-Event
+
+
+
+RevPiOption._createwidgets
+_createwidgets()
+
+Erstellt Widgets.
+
+
+RevPiOption._loadappdata
+_loadappdata(refresh=False)
+
+Läd aktuelle Einstellungen vom RevPi.
+
+- refresh
+-
+Wenn True, werden Einstellungen heruntergeladen.
+
+
+
+RevPiOption._setappdata
+_setappdata()
+
+Speichert geänderte Einstellungen auf RevPi.
+
+- Returns:
+-
+None
+
+
+
+RevPiOption.askxmlon
+askxmlon()
+
+Fragt Nuter, ob wirklicht abgeschaltet werden soll.
+
+
+RevPiOption.xmlmod2_tail
+xmlmod2_tail()
+
+Passt XML-Optionszugriff an.
+
+
+RevPiOption.xmlmod_tail
+xmlmod_tail()
+
+Passt XML-Optionszugriff an.
+
+
+
+
\ No newline at end of file
diff --git a/doc/revpiplclist.html b/doc/revpiplclist.html
new file mode 100644
index 0000000..d2f8095
--- /dev/null
+++ b/doc/revpiplclist.html
@@ -0,0 +1,188 @@
+
+
+revpiplclist
+
+
+
+
+revpiplclist
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+RevPiPlcList
+
+
+Derived from
+tkinter.Frame
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+Static Methods
+
+
+
+RevPiPlcList (Constructor)
+RevPiPlcList(master)
+
+Init RevPiPlcList-class.
+
+- master
+-
+tkinter master
+
+
+
+RevPiPlcList._checkclose
+_checkclose(event=None)
+
+Prüft ob Fenster beendet werden soll.
+
+- event
+-
+tkinter-Event
+
+
+
+RevPiPlcList._createwidgets
+_createwidgets()
+
+Erstellt alle Widgets.
+
+
+RevPiPlcList._saveappdata
+_saveappdata()
+
+Speichert Verbindungen im home Dir.
+
+- Returns:
+-
+True, bei erfolgreicher Verarbeitung
+
+
+
+RevPiPlcList.build_listconn
+build_listconn()
+
+Füllt Verbindungsliste.
+
+
+RevPiPlcList.evt_btnadd
+evt_btnadd()
+
+Verbindungseinstellungen übernehmen.
+
+
+RevPiPlcList.evt_btnnew
+evt_btnnew()
+
+Neue Verbindung erstellen.
+
+
+RevPiPlcList.evt_btnremove
+evt_btnremove()
+
+Verbindung löschen.
+
+
+RevPiPlcList.evt_btnsave
+evt_btnsave()
+
+Alle Verbindungen speichern.
+
+
+RevPiPlcList.evt_keypress
+evt_keypress(evt=None)
+
+Passt bei Tastendruck den Status der Buttons an.
+
+
+RevPiPlcList.evt_listconn
+evt_listconn(evt=None)
+
+Übernimmt Einstellungen in Eingabefelder.
+
+
+
+
+get_connections
+get_connections()
+
+Verbindungen aus Datei laden.
+
+- Returns:
+-
+dict() mit Verbindungen
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/revpiprogram.html b/doc/revpiprogram.html
new file mode 100644
index 0000000..4709afd
--- /dev/null
+++ b/doc/revpiprogram.html
@@ -0,0 +1,246 @@
+
+
+revpiprogram
+
+
+
+
+revpiprogram
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+RevPiProgram
+
+
+Derived from
+tkinter.Frame
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+Static Methods
+
+
+
+RevPiProgram (Constructor)
+RevPiProgram(master, xmlcli, xmlmode, revpi)
+
+Init RevPiProgram-Class.
+
+- Returns:
+-
+None
+
+
+
+RevPiProgram._checkclose
+_checkclose(event=None)
+
+Prüft ob Fenster beendet werden soll.
+
+- event
+-
+tkinter-Event
+
+
+
+RevPiProgram._createwidgets
+_createwidgets()
+
+Erstellt alle Widgets.
+
+
+RevPiProgram._evt_optdown
+_evt_optdown(text="")
+
+Passt je nach gewählter Option den Status der Widgets an.
+
+
+RevPiProgram._evt_optup
+_evt_optup(text="")
+
+Passt je nach gewählter Option den Status der Widgets an.
+
+
+RevPiProgram._loaddefault
+_loaddefault(full=False)
+
+Übernimmt für den Pi die letzen Pfade.
+
+- full
+-
+Einstellungen für alle Verbindungen laden
+
+
+- Returns:
+-
+dict() mit Einstellungen
+
+
+
+RevPiProgram._savedefaults
+_savedefaults()
+
+Schreibt fuer den Pi die letzen Pfade.
+
+- Returns:
+-
+True, bei erfolgreicher Verarbeitung
+
+
+
+RevPiProgram.check_replacedir
+check_replacedir(rootdir)
+
+Gibt das rootdir von einem entpackten Verzeichnis zurueck.
+
+ Dabei wird geprueft, ob es sich um einen einzelnen Ordner handelt
+ und ob es eine piCtory Konfiguration im rootdir gibt.
+
+- rootdir
+-
+Verzeichnis fuer Pruefung
+
+
+- Returns:
+-
+Abgeaendertes rootdir
+
+
+
+RevPiProgram.create_filelist
+create_filelist(rootdir)
+
+Erstellt eine Dateiliste von einem Verzeichnis.
+
+- rootdir
+-
+Verzeichnis fuer das eine Liste erstellt werden soll
+
+
+- Returns:
+-
+Dateiliste
+
+
+
+RevPiProgram.getpictoryrsc
+getpictoryrsc()
+
+Läd die piCtory Konfiguration herunter.
+
+
+RevPiProgram.getprocimg
+getprocimg()
+
+Läd das aktuelle Prozessabbild herunter.
+
+
+RevPiProgram.picontrolreset
+picontrolreset()
+
+Fürt ein Reset der piBridge durch.
+
+
+RevPiProgram.plcdownload
+plcdownload()
+
+Läd das aktuelle Projekt herunter.
+
+
+RevPiProgram.plcupload
+plcupload()
+
+Lädt das angegebene Projekt auf den RevPi.
+
+- Returns:
+-
+True, bei erfolgreicher Verarbeitung
+
+
+
+RevPiProgram.setpictoryrsc
+setpictoryrsc(filename=None)
+
+Überträgt die angegebene piCtory-Konfiguration.
+
+
+
+
\ No newline at end of file
diff --git a/doc/revpipycontrol.html b/doc/revpipycontrol.html
new file mode 100644
index 0000000..25d825a
--- /dev/null
+++ b/doc/revpipycontrol.html
@@ -0,0 +1,270 @@
+
+
+revpipycontrol
+
+
+
+
+revpipycontrol
+
+
+Global Attributes
+
+
+Classes
+
+
+Functions
+
+
+
+RevPiPyControl
+
+
+Derived from
+tkinter.Frame
+
+Class Attributes
+
+
+Class Methods
+
+
+Methods
+
+
+Static Methods
+
+
+
+RevPiPyControl (Constructor)
+RevPiPyControl(master=None)
+
+Init RevPiPyControl-Class.
+
+- master
+-
+tkinter master
+
+
+
+RevPiPyControl._btnstate
+_btnstate()
+
+Setzt den state der Buttons.
+
+
+RevPiPyControl._closeall
+_closeall()
+
+Schließt alle Fenster.
+
+
+RevPiPyControl._closeapp
+_closeapp(event=None)
+
+Räumt auf und beendet Programm.
+
+- event
+-
+tkinter Event
+
+
+
+RevPiPyControl._createwidgets
+_createwidgets()
+
+Erstellt den Fensterinhalt.
+
+
+RevPiPyControl._fillconnbar
+_fillconnbar()
+
+Generiert Menüeinträge für Verbindungen.
+
+
+RevPiPyControl._fillmbar
+_fillmbar()
+
+Generiert Menüeinträge.
+
+
+RevPiPyControl._opt_conn
+_opt_conn(text, reconnect=False)
+
+Stellt eine neue Verbindung zu RevPiPyLoad her.
+
+- text
+-
+Verbindungsname
+
- reconnect
+-
+Socket Timeout nicht heruntersetzen
+
+
+
+RevPiPyControl.infowindow
+infowindow()
+
+Öffnet das Fenster für die Info.
+
+
+RevPiPyControl.plcdebug
+plcdebug()
+
+Baut den Debugframe und packt ihn.
+
+
+RevPiPyControl.plclist
+plclist()
+
+Öffnet das Fenster für die Verbindungen.
+
+
+RevPiPyControl.plclogs
+plclogs()
+
+Öffnet das Fenster für Logdateien.
+
+- Returns:
+-
+None
+
+
+
+RevPiPyControl.plcoptions
+plcoptions()
+
+Startet das Optionsfenster.
+
+
+RevPiPyControl.plcprogram
+plcprogram()
+
+Startet das Programmfenster.
+
+
+RevPiPyControl.plcrestart
+plcrestart()
+
+Startet das PLC Programm neu.
+
+
+RevPiPyControl.plcstart
+plcstart()
+
+Startet das PLC Programm.
+
+
+RevPiPyControl.plcstop
+plcstop()
+
+Beendet das PLC Programm.
+
+
+RevPiPyControl.serverdisconnect
+serverdisconnect()
+
+Trennt eine bestehende Verbindung.
+
+
+RevPiPyControl.servererror
+servererror()
+
+Setzt alles zurück für neue Verbindungen.
+
+
+RevPiPyControl.tmr_plcrunning
+tmr_plcrunning()
+
+Timer der den Status des PLC Programms prüft.
+
+
+RevPiPyControl.visitwebsite
+visitwebsite()
+
+Öffnet auf dem System einen Webbrowser zur Projektseite.
+
+
+
+
\ No newline at end of file
diff --git a/revpipycontrol.api b/revpipycontrol.api
index 41dff9d..0fcb54c 100644
--- a/revpipycontrol.api
+++ b/revpipycontrol.api
@@ -1,35 +1,54 @@
-revpicheckclient.RevPiCheckClient._autorw?5()
+mytools.addroot?4(filename)
+mytools.gettrans?4(proglang=None)
+revpicheckclient.RevPiCheckClient.__chval?6(device, io, event=None)
+revpicheckclient.RevPiCheckClient.__hidewin?6(win, event=None)
+revpicheckclient.RevPiCheckClient.__saveoldvalue?6(event, tkvar)
+revpicheckclient.RevPiCheckClient.__showwin?6(win)
+revpicheckclient.RevPiCheckClient.__spinboxkey?6(device, io, event=None)
revpicheckclient.RevPiCheckClient._createiogroup?5(device, frame, iotype)
revpicheckclient.RevPiCheckClient._createwidgets?5()
-revpicheckclient.RevPiCheckClient._readvaluesdev?5(device, iotype)
-revpicheckclient.RevPiCheckClient._writevaluesdev?5(device)
-revpicheckclient.RevPiCheckClient.myapp?7
-revpicheckclient.RevPiCheckClient.onfrmconf?4(canvas)
+revpicheckclient.RevPiCheckClient._onfrmconf?5(canvas)
+revpicheckclient.RevPiCheckClient._warnwrite?5()
+revpicheckclient.RevPiCheckClient._workvalues?5(io_dicts=None, writeout=False)
+revpicheckclient.RevPiCheckClient.hideallwindows?4()
revpicheckclient.RevPiCheckClient.readvalues?4()
-revpicheckclient.RevPiCheckClient.root?7
+revpicheckclient.RevPiCheckClient.refreshvalues?4()
+revpicheckclient.RevPiCheckClient.tmr_workvalues?4()
revpicheckclient.RevPiCheckClient.toggleauto?4()
+revpicheckclient.RevPiCheckClient.togglewrite?4()
+revpicheckclient.RevPiCheckClient.validatereturn?4(returnlist)
revpicheckclient.RevPiCheckClient.writevalues?4()
-revpicheckclient.RevPiCheckClient?1(master, xmlcli)
+revpicheckclient.RevPiCheckClient?1(master, xmlcli, xmlmode=0)
+revpicheckclient._?8
+revpiinfo.RevPiInfo._checkclose?5(event=None)
+revpiinfo.RevPiInfo._createwidgets?5(extended=False)
+revpiinfo.RevPiInfo.visitwebsite?4(event=None)
+revpiinfo.RevPiInfo?1(master, xmlcli, version)
+revpiinfo._?8
+revpilogfile.RevPiLogfile._checkclose?5(event=None)
revpilogfile.RevPiLogfile._createwidgets?5()
+revpilogfile.RevPiLogfile._load_log?5(textwidget, xmlcall, startposition, full)
revpilogfile.RevPiLogfile.btn_clearapp?4()
revpilogfile.RevPiLogfile.btn_clearplc?4()
-revpilogfile.RevPiLogfile.get_applines?4()
-revpilogfile.RevPiLogfile.get_applog?4()
-revpilogfile.RevPiLogfile.get_plclines?4()
-revpilogfile.RevPiLogfile.get_plclog?4()
+revpilogfile.RevPiLogfile.get_applog?4(full=False)
+revpilogfile.RevPiLogfile.get_plclog?4(full=False)
revpilogfile.RevPiLogfile?1(master, xmlcli)
+revpilogfile._?8
+revpioption.RevPiOption._changesdone?5()
+revpioption.RevPiOption._checkclose?5(event=None)
revpioption.RevPiOption._createwidgets?5()
-revpioption.RevPiOption._loadappdata?5()
+revpioption.RevPiOption._loadappdata?5(refresh=False)
revpioption.RevPiOption._setappdata?5()
revpioption.RevPiOption.askxmlon?4()
-revpioption.RevPiOption.xmlmods?4()
-revpioption.RevPiOption?1(master, xmlcli, xmlmode)
+revpioption.RevPiOption.xmlmod2_tail?4()
+revpioption.RevPiOption.xmlmod_tail?4()
+revpioption.RevPiOption?1(master, xmlcli)
+revpioption._?8
+revpiplclist.RevPiPlcList._checkclose?5(event=None)
revpiplclist.RevPiPlcList._createwidgets?5()
-revpiplclist.RevPiPlcList._loadappdata?5()
revpiplclist.RevPiPlcList._saveappdata?5()
revpiplclist.RevPiPlcList.build_listconn?4()
revpiplclist.RevPiPlcList.evt_btnadd?4()
-revpiplclist.RevPiPlcList.evt_btnclose?4()
revpiplclist.RevPiPlcList.evt_btnnew?4()
revpiplclist.RevPiPlcList.evt_btnremove?4()
revpiplclist.RevPiPlcList.evt_btnsave?4()
@@ -38,8 +57,10 @@ revpiplclist.RevPiPlcList.evt_listconn?4(evt=None)
revpiplclist.RevPiPlcList.myapp?7
revpiplclist.RevPiPlcList.root?7
revpiplclist.RevPiPlcList?1(master)
+revpiplclist._?8
revpiplclist.get_connections?4()
revpiplclist.savefile?7
+revpiprogram.RevPiProgram._checkclose?5(event=None)
revpiprogram.RevPiProgram._createwidgets?5()
revpiprogram.RevPiProgram._evt_optdown?5(text="")
revpiprogram.RevPiProgram._evt_optup?5(text="")
@@ -49,31 +70,35 @@ revpiprogram.RevPiProgram.check_replacedir?4(rootdir)
revpiprogram.RevPiProgram.create_filelist?4(rootdir)
revpiprogram.RevPiProgram.getpictoryrsc?4()
revpiprogram.RevPiProgram.getprocimg?4()
-revpiprogram.RevPiProgram.myapp?7
revpiprogram.RevPiProgram.picontrolreset?4()
revpiprogram.RevPiProgram.plcdownload?4()
revpiprogram.RevPiProgram.plcupload?4()
-revpiprogram.RevPiProgram.root?7
revpiprogram.RevPiProgram.setpictoryrsc?4(filename=None)
revpiprogram.RevPiProgram?1(master, xmlcli, xmlmode, revpi)
+revpiprogram._?8
revpiprogram.savefile?7
revpipycontrol.RevPiPyControl._btnstate?5()
revpipycontrol.RevPiPyControl._closeall?5()
+revpipycontrol.RevPiPyControl._closeapp?5(event=None)
revpipycontrol.RevPiPyControl._createwidgets?5()
revpipycontrol.RevPiPyControl._fillconnbar?5()
revpipycontrol.RevPiPyControl._fillmbar?5()
-revpipycontrol.RevPiPyControl._opt_conn?5(text)
+revpipycontrol.RevPiPyControl._opt_conn?5(text, reconnect=False)
+revpipycontrol.RevPiPyControl.infowindow?4()
revpipycontrol.RevPiPyControl.myapp?7
+revpipycontrol.RevPiPyControl.plcdebug?4()
revpipycontrol.RevPiPyControl.plclist?4()
revpipycontrol.RevPiPyControl.plclogs?4()
-revpipycontrol.RevPiPyControl.plcmonitor?4()
revpipycontrol.RevPiPyControl.plcoptions?4()
revpipycontrol.RevPiPyControl.plcprogram?4()
revpipycontrol.RevPiPyControl.plcrestart?4()
revpipycontrol.RevPiPyControl.plcstart?4()
revpipycontrol.RevPiPyControl.plcstop?4()
revpipycontrol.RevPiPyControl.root?7
+revpipycontrol.RevPiPyControl.serverdisconnect?4()
revpipycontrol.RevPiPyControl.servererror?4()
revpipycontrol.RevPiPyControl.tmr_plcrunning?4()
+revpipycontrol.RevPiPyControl.visitwebsite?4()
revpipycontrol.RevPiPyControl?1(master=None)
-revpipycontrol.addroot?4(filename)
+revpipycontrol._?8
+revpipycontrol.pycontrolversion?7
diff --git a/revpipycontrol.bas b/revpipycontrol.bas
index c02ecc3..32a817a 100644
--- a/revpipycontrol.bas
+++ b/revpipycontrol.bas
@@ -1,4 +1,5 @@
RevPiCheckClient tkinter.Frame
+RevPiInfo tkinter.Frame
RevPiLogfile tkinter.Frame
RevPiOption tkinter.Frame
RevPiPlcList tkinter.Frame
diff --git a/revpipycontrol.e4p b/revpipycontrol.e4p
index 68fd5fa..38de6f1 100644
--- a/revpipycontrol.e4p
+++ b/revpipycontrol.e4p
@@ -1,7 +1,7 @@
-
+
en_US
@@ -9,7 +9,7 @@
Python3
Console
- 0.2.12
+ 0.4.1
Sven Sager
akira@narux.de
@@ -21,6 +21,8 @@
revpipycontrol/revpilogfile.py
revpipycontrol/revpioption.py
revpipycontrol/revpiprogram.py
+ revpipycontrol/mytools.py
+ revpipycontrol/revpiinfo.py
@@ -30,6 +32,7 @@
data
doc
revpipycontrol.api
+ stdeb.cfg
revpipycontrol/revpipycontrol.py
@@ -224,12 +227,6 @@
setup.py
-
- noindex
-
-
- True
-
outputDirectory
diff --git a/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po b/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po
new file mode 100644
index 0000000..5b858a2
--- /dev/null
+++ b/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po
@@ -0,0 +1,722 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: RevPiPyControl 0.4.0\n"
+"POT-Creation-Date: 2017-07-03 10:36+0200\n"
+"PO-Revision-Date: 2017-07-03 10:36+0200\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.7.1\n"
+"X-Poedit-Basepath: ../../..\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-SearchPath-0: .\n"
+
+#: revpicheckclient.py:107 revpicheckclient.py:432 revpioption.py:309
+#: revpiplclist.py:205 revpiprogram.py:315 revpiprogram.py:346
+#: revpiprogram.py:410 revpiprogram.py:416 revpiprogram.py:422
+#: revpiprogram.py:430 revpiprogram.py:436 revpiprogram.py:469
+#: revpiprogram.py:553 revpiprogram.py:626 revpiprogram.py:657
+#: revpiprogram.py:670 revpiprogram.py:725 revpiprogram.py:745
+#: revpiprogram.py:753 revpipycontrol.py:363
+msgid "Error"
+msgstr "Fehler"
+
+#: revpicheckclient.py:108
+msgid ""
+"Given value for Output '{}' is not valid! \n"
+"Reset to '{}'"
+msgstr ""
+"Angegebener Wert für Output '{}' ist nicht gültig! \n"
+"Setze auf '{}' zurück"
+
+#: revpicheckclient.py:207
+msgid "Devices of RevPi"
+msgstr "Devices vom RevPi"
+
+#: revpicheckclient.py:239
+msgid "Control"
+msgstr "Kontrolle"
+
+#: revpicheckclient.py:243
+msgid "Read all IOs"
+msgstr "Lese alle IOs"
+
+#: revpicheckclient.py:248
+msgid "Read just Inputs"
+msgstr "Nur Inputs lesen"
+
+#: revpicheckclient.py:255
+msgid "Write Outputs"
+msgstr "Outputs schreiben"
+
+#: revpicheckclient.py:261
+msgid "Autorefresh values"
+msgstr "Aktualisiere Werte automatisch"
+
+#: revpicheckclient.py:269
+msgid "Write values to RevPi"
+msgstr "Schreibe Werte auf RevPi"
+
+#: revpicheckclient.py:283 revpiprogram.py:443 revpipycontrol.py:235
+#: revpipycontrol.py:280 revpipycontrol.py:298 revpipycontrol.py:324
+msgid "Warning"
+msgstr "Warnung"
+
+#: revpicheckclient.py:284
+msgid ""
+"You want to set outputs on the RevPi! Note that these are set "
+"IMMEDIATELY!!! \n"
+"If another control program is running on the RevPi, it could interfere and "
+"reset the outputs."
+msgstr ""
+"Sie wollen Outputs auf dem RevPi setzen! Beachten Sie, dass diese SOFORT "
+"gesetzt werden!!! \n"
+"Wenn auf dem RevPi ein anderes Programm zur Steuerung läuft, könnte dies "
+"gestört werden und die Ausgänge wieder zurücksetzen."
+
+#: revpicheckclient.py:429
+msgid "Error set value of device '{}' Output '{}': {} \n"
+msgstr "Fehler beim Setzen der Werte auf Device '{}' bei Output '{}': {}\n"
+
+#: revpiinfo.py:38
+msgid "RevPi Python PLC info"
+msgstr "RevPi Python PLC Information"
+
+#: revpiinfo.py:51
+msgid "RevPi Python PLC - Control"
+msgstr "RevPi Python PLC - Kontrollcenter"
+
+#: revpiinfo.py:55
+msgid "Version: {}"
+msgstr "Version: {}"
+
+#: revpiinfo.py:78
+msgid "RevPiPyLoad version on RevPi:"
+msgstr "RevPiPyLoad Version auf RevPi:"
+
+#: revpiinfo.py:83
+msgid "not conn."
+msgstr "nicht verb."
+
+#: revpiinfo.py:91
+msgid ""
+"\n"
+"RevPiModIO, RevPiPyLoad and RevPiPyControl\n"
+"are community driven projects. They are all\n"
+"free and open source software.\n"
+"All of them comes with ABSOLUTELY NO\n"
+"WARRANTY, to the extent permitted by \n"
+"applicable law.\n"
+"\n"
+"\n"
+"(c) Sven Sager, License: LGPLv3"
+msgstr ""
+"\n"
+"RevPiModIO, RevPiPyLoad und RevPiPyControl\n"
+"sind gemeinschaftsorientierte Projekte. Sie sind\n"
+"alle frei und Open Source Software.\n"
+"Alle kommen mit ABSOLUT KEINER\n"
+"GARANTIE, soweit gesetzlich zulässig.\n"
+"\n"
+"\n"
+"(c) Sven Sager, Lizenz: LGPLv3"
+
+#: revpiinfo.py:138 revpioption.py:232 revpiplclist.py:134
+msgid "Close"
+msgstr "Schließen"
+
+#: revpilogfile.py:41
+msgid "RevPi Python PLC Logs"
+msgstr "RevPi Python PLC Logdaten"
+
+#: revpilogfile.py:54
+msgid "RevPiPyLoad - Logfile"
+msgstr "RevPiPyLoad - Logdatei"
+
+#: revpilogfile.py:58 revpilogfile.py:73
+msgid "Clear screen"
+msgstr "Leere Ansicht"
+
+#: revpilogfile.py:69
+msgid "Python PLC program - Logfile"
+msgstr "Python PLC Programm - Logdatei"
+
+#: revpilogfile.py:157
+msgid "Can not access log file on the RevPi"
+msgstr "Auf die Logdatei des RevPi kann nicht zugegriffen werden"
+
+#: revpioption.py:66 revpioption.py:272 revpioption.py:319 revpiplclist.py:63
+#: revpiplclist.py:181 revpiprogram.py:379 revpiprogram.py:454
+msgid "Question"
+msgstr "Frage"
+
+#: revpioption.py:67 revpiplclist.py:64
+msgid ""
+"Do you really want to quit? \n"
+"Unsaved changes will be lost"
+msgstr ""
+"Wollen Sie wirklich beenden? \n"
+"Nicht gespeicherte Änderungen gehen verloren"
+
+#: revpioption.py:77
+msgid "RevPi Python PLC Options"
+msgstr "RevPi Python PLC Einstellungen"
+
+#: revpioption.py:87
+msgid "Start / Stop behavior"
+msgstr "Start / Stop Verhalten"
+
+#: revpioption.py:96
+msgid "Start program automatically"
+msgstr "Starte Programm automatisch"
+
+#: revpioption.py:102
+msgid "Restart program after exit"
+msgstr "Starte Programm nach Beenden neu"
+
+#: revpioption.py:110
+msgid ""
+"Set process image to NULL if program\n"
+"terminates successfully"
+msgstr ""
+"Prozessabbild auf NULL setzen, wenn Programm\n"
+"erfolgreich beendet wird"
+
+#: revpioption.py:118
+msgid ""
+"Set process image to NULL if program\n"
+"terminates with errors"
+msgstr ""
+"Prozessabbild auf NULL setzen, wenn Programm\n"
+"fehlerhaft beendet wird"
+
+#: revpioption.py:125
+msgid "PLC program"
+msgstr "PLC Programm"
+
+#: revpioption.py:136
+msgid "Python version"
+msgstr "Python Version"
+
+#: revpioption.py:153
+msgid "Python PLC program name"
+msgstr "Python PLC Programmname"
+
+#: revpioption.py:165
+msgid "Program arguments"
+msgstr "Programmargumente"
+
+#: revpioption.py:174
+msgid "Use RevPi as PLC-Slave"
+msgstr "RevPi als PLC-Slave verwenden"
+
+#: revpioption.py:181
+msgid "XML-RPC server"
+msgstr "XML-RPC Server"
+
+#: revpioption.py:193
+msgid "Activate XML-RPC server on RevPi"
+msgstr "Aktiviere XML-RPC Server auf RevPi"
+
+#: revpioption.py:201
+msgid ""
+"Allow download of piCtory configuration and\n"
+"PLC programm"
+msgstr ""
+"Download von piCtroy Konfiguration und\n"
+"PLC Programm zulassen"
+
+#: revpioption.py:208
+msgid ""
+"Allow upload of piCtory configuration and\n"
+"PLC programm"
+msgstr ""
+"Hochladen von piCtroy Konfiguration und\n"
+"PLC Programm zulassen"
+
+#: revpioption.py:213
+msgid "XML-RPC server port"
+msgstr "XML-RPC Serverport"
+
+#: revpioption.py:227 revpiplclist.py:131
+msgid "Save"
+msgstr "Speichern"
+
+#: revpioption.py:265 revpioption.py:301 revpiplclist.py:197 revpiprogram.py:68
+msgid "Information"
+msgstr "Information"
+
+#: revpioption.py:266
+msgid "You have not made any changes to save."
+msgstr "Sie haben keine Änderungen zum Speichern vorgenommen."
+
+#: revpioption.py:273
+msgid ""
+"The settings are now saved on the Revolution Pi. \n"
+"\n"
+"Should the new settings take effect immediately? \n"
+"This means a restart of the service and the PLC program!"
+msgstr ""
+"Die Einstellungen werden jetzt auf dem Revolution Pi gespeichert.\n"
+"\n"
+"Sollen die neuen Einstellungen sofort in Kraft treten?\n"
+"Dies bedeutet einen Neustart des Dienstes und des laufenden PLC-Programms!"
+
+#: revpioption.py:302
+msgid "Settings saved"
+msgstr "Einstellungen gespeichert"
+
+#: revpioption.py:310
+msgid "The settings could not be saved. This can happen if values are wrong!"
+msgstr ""
+"Die Einstellungen konnten nicht gesichert werden. Dies kann passieren, wenn "
+"Werte falsch sind!"
+
+#: revpioption.py:320
+msgid ""
+"Are you sure you want to deactivate the XML-RPC server? You will NOT be able "
+"to access the Revolution Pi with this program."
+msgstr ""
+"Soll der XML-RPC Server wirklich beendet werden? Sie können dann NICHT mehr "
+"mit diesem Programm auf den Revolution Pi zugreifen."
+
+#: revpiplclist.py:74
+msgid "RevPi Python PLC connections"
+msgstr "RevPi Python PLC Verbindungen"
+
+#: revpiplclist.py:94
+msgid "Name"
+msgstr "Name"
+
+#: revpiplclist.py:101
+msgid "IP address"
+msgstr "IP Adresse"
+
+#: revpiplclist.py:109
+msgid "Port"
+msgstr "Port"
+
+#: revpiplclist.py:118
+msgid "New"
+msgstr "Neu"
+
+#: revpiplclist.py:121
+msgid "Apply"
+msgstr "Übernehmen"
+
+#: revpiplclist.py:125
+msgid "Remove"
+msgstr "Entfernen"
+
+#: revpiplclist.py:182
+msgid "Do you really want to delete the selected connection '{}'?"
+msgstr "Wollen Sie die ausgewählte Verbindung '{}' wirklich löschen?"
+
+#: revpiplclist.py:198
+msgid ""
+"Successfully saved. \n"
+"Do you want to close this window?"
+msgstr ""
+"Verbindungen erfolgreich gespeichert. \n"
+"Möchten Sie dieses Fenster jetzt schließen?"
+
+#: revpiplclist.py:206
+msgid "Failed to save connections"
+msgstr "Verbindungen konnten nicht gespeichert werden"
+
+#: revpiprogram.py:69
+msgid ""
+"A PLC program has been uploaded. Please check the PLC options to see if the "
+"correct program is specified as the start program."
+msgstr ""
+"Ein PLC Programm wurde hochgeladen. Bitte prüfen Sie die PLC Optionen, ob "
+"dort das richtige Programm als Startprogramm angegeben ist."
+
+#: revpiprogram.py:78
+msgid "RevPi Python PLC program"
+msgstr "RevPi Python PLC Programm"
+
+#: revpiprogram.py:92
+msgid "PLC python program"
+msgstr "PLC Python Programm"
+
+#: revpiprogram.py:102 revpiprogram.py:104 revpiprogram.py:112
+#: revpiprogram.py:116
+msgid "Files"
+msgstr "Dateien"
+
+#: revpiprogram.py:102 revpiprogram.py:104 revpiprogram.py:501
+#: revpiprogram.py:610
+msgid "Zip archive"
+msgstr "Zip Archiv"
+
+#: revpiprogram.py:102 revpiprogram.py:104 revpiprogram.py:514
+#: revpiprogram.py:640
+msgid "TGZ archive"
+msgstr "TGZ Archiv"
+
+#: revpiprogram.py:104
+msgid "Folder"
+msgstr "Verzeichnis"
+
+#: revpiprogram.py:121
+msgid "Download PLC program as:"
+msgstr "PLC Programm herunterladen als:"
+
+#: revpiprogram.py:131
+msgid "include piCtory configuration"
+msgstr "inkl. piCtory Konfiguration"
+
+#: revpiprogram.py:136 revpiprogram.py:180 revpiprogram.py:201
+msgid "Download"
+msgstr "Herunterladen"
+
+#: revpiprogram.py:141
+msgid "Upload PLC program as:"
+msgstr "PLC Programm hochladen als:"
+
+#: revpiprogram.py:153
+msgid "clean upload folder before upload"
+msgstr "Uploadverzeichnis vor dem Upload leeren"
+
+#: revpiprogram.py:160
+msgid "includes piCtory configuration"
+msgstr "enthält piCtory Konfiguration"
+
+#: revpiprogram.py:166 revpiprogram.py:188
+msgid "Upload"
+msgstr "Hochladen"
+
+#: revpiprogram.py:172
+msgid "piCtory configuration"
+msgstr "piCtory Konfiguration"
+
+#: revpiprogram.py:176
+msgid "Download piCtory configuration"
+msgstr "piCtory Konfiguration herunterladen"
+
+#: revpiprogram.py:183
+msgid "Upload piCtory configuration"
+msgstr "piCtory Konfiguration hochladen"
+
+#: revpiprogram.py:194
+msgid "piControl0 process image"
+msgstr "piControl0 Prozessabbild"
+
+#: revpiprogram.py:197
+msgid "Download process image dump"
+msgstr "Prozessabbild Dump herunterladen"
+
+#: revpiprogram.py:207
+msgid "Reset piControl"
+msgstr "piControl zurücksetzen"
+
+#: revpiprogram.py:210
+msgid "Execute piControlReset"
+msgstr "piControlReset ausführen"
+
+#: revpiprogram.py:214
+msgid "execute"
+msgstr "ausführen"
+
+#: revpiprogram.py:220 revpipycontrol.py:107
+msgid "Exit"
+msgstr "Beenden"
+
+#: revpiprogram.py:305 revpiprogram.py:336 revpiprogram.py:497
+#: revpiprogram.py:510
+msgid "Save as..."
+msgstr "Speichern unter..."
+
+#: revpiprogram.py:308 revpiprogram.py:371
+msgid "piCtory config"
+msgstr "piCtory Konfiguration"
+
+#: revpiprogram.py:308 revpiprogram.py:339 revpiprogram.py:371
+#: revpiprogram.py:501 revpiprogram.py:514 revpiprogram.py:585
+#: revpiprogram.py:610 revpiprogram.py:640
+msgid "All files"
+msgstr "Alle Dateien"
+
+#: revpiprogram.py:316 revpiprogram.py:347 revpiprogram.py:554
+msgid "Could not load and save file!"
+msgstr "Datei konnte nicht geladen und gespeichert werden!"
+
+#: revpiprogram.py:321 revpiprogram.py:352 revpiprogram.py:391
+#: revpiprogram.py:399 revpiprogram.py:463 revpiprogram.py:559
+#: revpiprogram.py:715
+msgid "Success"
+msgstr "Erfolgreich"
+
+#: revpiprogram.py:322 revpiprogram.py:353 revpiprogram.py:560
+msgid "File successfully loaded and saved."
+msgstr "Dateien erfolgreich übertragen und gespeichert."
+
+#: revpiprogram.py:339
+msgid "Imagefiles"
+msgstr "Image Dateien"
+
+#: revpiprogram.py:367
+msgid "Open piCtory file..."
+msgstr "piCtory Datei öffnen..."
+
+#: revpiprogram.py:380
+msgid ""
+"Should the piControl driver be reset after uploading the piCtory "
+"configuration?"
+msgstr ""
+"Soll nach dem Hochladen der piCtory Konfiguration ein Reset am piControl "
+"Treiber durchgeführt werden?"
+
+#: revpiprogram.py:392
+msgid ""
+"The transfer of the piCtory configuration and the reset of piControl have "
+"been successfully executed."
+msgstr ""
+"Die Übertragung der piCtory Konfiguration und der Reset von piControl wurden "
+"erfolgreich ausgeführt."
+
+#: revpiprogram.py:400
+msgid "The piCtory configuration was successfully transferred."
+msgstr ""
+"Die Übertragung der piCtory Konfiguration wurde erfolgreich ausgeführt."
+
+#: revpiprogram.py:411
+msgid "Can not process the transferred file."
+msgstr "Kann die übertragene Datei nicht verarbeiten."
+
+#: revpiprogram.py:417
+msgid "Can not find main elements in piCtory file."
+msgstr "Kann Hauptelemente in piCtory datei nicht finden."
+
+#: revpiprogram.py:423
+msgid ""
+"Contained devices could not be found on Revolution Pi. The configuration may "
+"be from a newer piCtory version!"
+msgstr ""
+"Enthaltene Geräte konnten auf dem Revolution Pi nicht gefunden werden. "
+"Möglicherweise stammt die Konfiguration von einer neueren piCtory Version!"
+
+#: revpiprogram.py:431
+msgid "Could not load RAP catalog on Revolution Pi."
+msgstr "Konnte RAP Katalog auf dem Revolution Pi nicht laden."
+
+#: revpiprogram.py:437
+msgid "The piCtory configuration could not be written on the Revolution Pi."
+msgstr ""
+"Die piCtory Konfiguration konnte auf dem Revolution Pi nicht geschrieben "
+"werden."
+
+#: revpiprogram.py:444
+msgid ""
+"The piCtroy configuration has been saved successfully. \n"
+"An error occurred on piControl reset!"
+msgstr ""
+"Die piCtroy Konfiguration wurde erfolgreich gespeichert. \n"
+"Beim piControl Reset trat allerdings ein Fehler auf!"
+
+#: revpiprogram.py:455
+msgid ""
+"Are you sure to reset piControl? \n"
+"The process image and the piBridge are interrupted !!!"
+msgstr ""
+"Soll piControlReset wirklich durchgeführt werden? \n"
+"Das Prozessabbild und die piBridge werden dann unterbrochen!!!"
+
+#: revpiprogram.py:464
+msgid "piControl reset executed successfully"
+msgstr "piControl reset wurde erfolgreich ausgeführt"
+
+#: revpiprogram.py:470
+msgid "piControl reset could not be executed successfully"
+msgstr "piControl Reset konnte nicht erfolgreich durchgeführt werden"
+
+#: revpiprogram.py:484
+msgid "Directory to save"
+msgstr "Verzeichnis zum speichern"
+
+#: revpiprogram.py:595
+msgid "Folder to upload"
+msgstr "Verzeichnis zum Hochladen"
+
+#: revpiprogram.py:606
+msgid "Upload Zip archive..."
+msgstr "Zip Archiv hochladen..."
+
+#: revpiprogram.py:627
+msgid "The specified file is not a ZIP archive."
+msgstr "Die angegebene Datei ist kein ZIP Archiv."
+
+#: revpiprogram.py:636
+msgid "Upload TarGz archiv..."
+msgstr "TarGz Archiv hochladen..."
+
+#: revpiprogram.py:658
+msgid "The specified file is not a TAR archive."
+msgstr "Die angegebene Datei ist kein TAR Archiv."
+
+#: revpiprogram.py:671
+msgid "There was an error deleting the files on the Revolution Pi."
+msgstr ""
+"Beim Löschen der Dateien auf dem Revolution Pi ist ein Fehler aufgetreten."
+
+#: revpiprogram.py:716
+msgid "The PLC program was transferred successfully."
+msgstr "Das PLC Programm wurde erfolgreich übertragen."
+
+#: revpiprogram.py:726
+msgid "There is no piCtory configuration in this archive."
+msgstr "Es wurde keine piCtory Konfiguration im Archiv gefunden."
+
+#: revpiprogram.py:746
+msgid "The Revolution Pi could not process some parts of the transmission."
+msgstr "Der Revoluton Pi konnte Teile der Übertragung nicht verarbeiten."
+
+#: revpiprogram.py:754
+msgid "Errors occurred during transmission"
+msgstr "Bei der Übertragung traten Fehler auf"
+
+#: revpipycontrol.py:105
+msgid "Connections..."
+msgstr "Verbindungen..."
+
+#: revpipycontrol.py:108
+msgid "Main"
+msgstr "Datei"
+
+#: revpipycontrol.py:116
+msgid "Visit website..."
+msgstr "Webseite besuchen..."
+
+#: revpipycontrol.py:118
+msgid "Info..."
+msgstr "Info..."
+
+#: revpipycontrol.py:119
+msgid "Help"
+msgstr "Hilfe"
+
+#: revpipycontrol.py:127
+msgid "PLC start"
+msgstr "PLC Start"
+
+#: revpipycontrol.py:132
+msgid "PLC stop"
+msgstr "PLC Stopp"
+
+#: revpipycontrol.py:137
+msgid "PLC restart"
+msgstr "PLC Neustart"
+
+#: revpipycontrol.py:142
+msgid "PLC logs"
+msgstr "PLC Logs"
+
+#: revpipycontrol.py:153
+msgid "PLC watch mode"
+msgstr "PLC watch Modus"
+
+#: revpipycontrol.py:170
+msgid "PLC log..."
+msgstr "PLC Log..."
+
+#: revpipycontrol.py:172
+msgid "PLC options..."
+msgstr "PLC Optionen..."
+
+#: revpipycontrol.py:174
+msgid "PLC program..."
+msgstr "PLC Programm..."
+
+#: revpipycontrol.py:178
+msgid "Disconnect"
+msgstr "Trennen"
+
+#: revpipycontrol.py:183
+msgid "Connect"
+msgstr "Verbinden"
+
+#: revpipycontrol.py:236
+msgid ""
+"The watch mode ist not supported in version {} of RevPiPyLoad on your RevPi! "
+"You need at least version 0.4.0. Or the python3-revpimodio module is not "
+"installton your RevPi at least version 0.15.0."
+msgstr ""
+"Der Watch-Modus wird von der RevPiPyLoad Version {} auf dem RevPi nicht "
+"unterstützt! Sie benötigen mindestens Version 0.4.0. Oder das python3-"
+"revpimodio Modul ist auf dem RevPi nicht in min. Version 0.15.0 installiert."
+
+#: revpipycontrol.py:281
+msgid ""
+"This version of Logviewer ist not supported in version {} of RevPiPyLoad on "
+"your RevPi! You need at least version 0.4.1."
+msgstr ""
+"Diese Version vom Logbetrachter wird in der RevPiPyLoad Version {} auf Ihrem "
+"RevPi nicht unterstützt! Sie benötigen mindestens Version 0.4.1."
+
+#: revpipycontrol.py:299 revpipycontrol.py:325
+msgid ""
+"XML-RPC access mode in the RevPiPyLoad configuration is too small to access "
+"this dialog!"
+msgstr ""
+"Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch genug eingestellt, um "
+"diesen Dialog zu verwenden!"
+
+#: revpipycontrol.py:364
+msgid "Can not reach server!"
+msgstr "Server ist nicht erreichbar!"
+
+#~ msgid ""
+#~ "The watch mode ist not supported in version {} of RevPiPyLoad on your "
+#~ "RevPi! You need at least version 0.4.0."
+#~ msgstr ""
+#~ "Der Watch-Modus wird von der RevPiPyLoad Version {} auf dem RevPi nicht "
+#~ "unterstützt! Sie benötigen mindestens Version 0.4.0."
+
+#~ msgid ""
+#~ "XML-RPC access mode in the RevPiPyLoad configuration is too small to "
+#~ "access this dialog"
+#~ msgstr ""
+#~ "Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch genug eingestellt, um "
+#~ "diesen Dialog zu verwenden!"
+
+#~ msgid "Do you really want to delete the selected connection '{}'"
+#~ msgstr "Wollen Sie die ausgewählte Verbindung '{}' wirklich löschen?"
+
+#~ msgid "piControl0 prozess image"
+#~ msgstr "piControl0 Prozessabbild"
+
+#~ msgid "piControlReset executed successfully"
+#~ msgstr "piControl Reset war erfolgreich"
+
+#~ msgid ""
+#~ "XML-RPC access mode in the RevPiPyLoad configuration is to small to "
+#~ "access this dialog"
+#~ msgstr ""
+#~ "Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch genug eingestellt, um "
+#~ "diesen Dialog zu verwenden!"
+
+#~ msgid "Quit"
+#~ msgstr "Beenden"
+
+#~ msgid "The transfer was successful."
+#~ msgstr "Die Übertragung war erfolgreich."
+
+#~ msgid ""
+#~ "The transfer of the piCtory configuration and the reset of piControl have "
+#~ "been successfully executed"
+#~ msgstr ""
+#~ "Die Übertragung der piCtory Konfiguration und der Reset von piControl "
+#~ "wurden erfolgreich ausgeführt"
+
+#~ msgid "The piCtory configuration was successfully transferred"
+#~ msgstr ""
+#~ "Die Übertragung der piCtory Konfiguration wurde erfolgreich ausgeführt"
+
+#~ msgid "Given value for Output '{}' is not valid! Reset to '{}'"
+#~ msgstr ""
+#~ "Angegebener Wert bei Output '{}' ist nicht gültig! Setze auf '{}' zurück"
diff --git a/revpipycontrol/mytools.py b/revpipycontrol/mytools.py
new file mode 100644
index 0000000..fb90979
--- /dev/null
+++ b/revpipycontrol/mytools.py
@@ -0,0 +1,46 @@
+#
+# RevPiPyControl
+#
+# Webpage: https://revpimodio.org/revpipyplc/
+# (c) Sven Sager, License: LGPLv3
+#
+# -*- coding: utf-8 -*-
+import gettext
+import locale
+import sys
+from os.path import dirname
+from os.path import join as pathjoin
+
+
+def addroot(filename):
+ u"""Hängt root-dir der Anwendung vor Dateinamen.
+
+ Je nach Ausführungsart der Anwendung muss das root-dir über
+ andere Arten abgerufen werden.
+
+ @param filename Datei oder Ordnername
+ @return root dir
+
+ """
+ if getattr(sys, "frozen", False):
+ return pathjoin(dirname(sys.executable), filename)
+ else:
+ return pathjoin(dirname(__file__), filename)
+
+
+def gettrans(proglang=None):
+
+ # Sprache auswählen
+ if proglang is None:
+ # Autodetect Language or switch to static
+ # proglang = "en"
+ proglang = locale.getdefaultlocale()[0].split('_')[0]
+
+ # Übersetzungen laden
+ trans = gettext.translation(
+ "revpipycontrol",
+ addroot("locale"),
+ languages=[proglang],
+ fallback=True
+ )
+ return trans.gettext
diff --git a/revpipycontrol/revpicheckclient.py b/revpipycontrol/revpicheckclient.py
index 1076251..9226f2e 100644
--- a/revpipycontrol/revpicheckclient.py
+++ b/revpipycontrol/revpicheckclient.py
@@ -9,216 +9,429 @@
import pickle
import tkinter
-from argparse import ArgumentParser
-from concurrent.futures import ThreadPoolExecutor
-from time import sleep
-from xmlrpc.client import ServerProxy, Binary, MultiCall
+import tkinter.messagebox as tkmsg
+from mytools import gettrans
+from threading import Lock
+from xmlrpc.client import MultiCall
+
+# Übersetzung laden
+_ = gettrans()
class RevPiCheckClient(tkinter.Frame):
- def __init__(self, master, xmlcli):
+ def __init__(self, master, xmlcli, xmlmode=0):
"""Instantiiert MyApp-Klasse."""
super().__init__(master)
- self.pack(fill="both", expand=True)
+ # XML-Daten abrufen
+ self.xmlmode = xmlmode
self.cli = xmlcli
+ self.cli.psstart()
+ self.lst_devices = self.cli.ps_devices()
+ self.dict_devices = {v[0]: v[1] for v in self.lst_devices}
+ self.lst_devices = [d[0] for d in self.lst_devices]
+ self.dict_inps = pickle.loads(self.cli.ps_inps().data)
+ self.dict_outs = pickle.loads(self.cli.ps_outs().data)
+ self.err_workvalues = 0
+ self.max_errors = 25
- self.lst_devices = self.cli.get_devicenames()
- self.lst_group = []
- self.dict_inpvar = {}
- self.dict_outvar = {}
+ self.lk = Lock()
+ self.dict_wins = {}
+ self.__checkwrite = True
+ self.__lockedvar = None
+ self.__oldvalue = None
self.autorw = tkinter.BooleanVar()
- self.fut_autorw = None
+ self.dowrite = tkinter.BooleanVar()
# Fenster aufbauen
self._createwidgets()
# Aktuelle Werte einlesen
- self.readvalues()
+ self.refreshvalues()
- def _autorw(self):
- dict_inp = {}
- dict_out = {}
+ def __chval(self, device, io, event=None):
+ u"""Schreibt neuen Output Wert auf den RevPi."""
+ if self.dowrite.get() and self._warnwrite():
+ with self.lk:
+ self.validatereturn(
+ self.cli.ps_setvalue(device, io[0], io[5].get())
+ )
- while self.autorw.get():
- for dev in self.lst_devices:
- try:
- dict_out[dev] = [
- value[8].get() for value in self.dict_outvar[dev]
- ]
- except:
- print("lasse {} aus".format(dev))
+ # Alles neu einlesen wenn nicht AutoRW aktiv ist
+ if not self.autorw.get():
+ self.refreshvalues()
- dict_inp = self.cli.refreshvalues(
- Binary(pickle.dumps(dict_out, 3))
+ self.__lockedvar = None
+
+ def __hidewin(self, win, event=None):
+ u"""Verbergt übergebenes Fenster.
+ @param win Fenster zum verbergen
+ @param event Tkinter Event"""
+ win.withdraw()
+
+ def __saveoldvalue(self, event, tkvar):
+ u"""Speichert bei Keypress aktuellen Wert für wiederherstellung."""
+ if self.__lockedvar is None:
+ self.__lockedvar = tkvar
+ try:
+ self.__oldvalue = tkvar.get()
+ except Exception:
+ pass
+
+ def __showwin(self, win):
+ u"""Zeigt oder verbergt übergebenes Fenster.
+ @param win Fenster zum anzeigen/verbergen"""
+ if win.winfo_viewable():
+ win.withdraw()
+ else:
+ win.deiconify()
+
+ def __spinboxkey(self, device, io, event=None):
+ u"""Prüft die Eingabe auf plausibilität.
+ @param event tkinter Event
+ @param io IO Liste mit tkinter Variable"""
+ # io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
+ try:
+ newvalue = io[5].get()
+ # Wertebereich prüfen
+ if newvalue < 0 or newvalue > 256 ** io[1] - 1:
+ raise ValueError("too big")
+
+ self.__chval(device, io)
+
+ except Exception:
+ io[5].set(self.__oldvalue)
+ tkmsg.showerror(
+ _("Error"),
+ _("Given value for Output '{}' is not valid! \nReset to ""'{}'"
+ "").format(self.dict_devices[device], self.__oldvalue),
+ parent=self.dict_wins[device]
)
- dict_inp = pickle.loads(dict_inp.data)
- for dev in dict_inp:
- for io in self.dict_inpvar[dev]:
- try:
- io[8].set(dict_inp[dev].pop(0))
- except:
- print("lasse {} aus".format(io[0]))
-
- sleep(0.1)
-
- def onfrmconf(self, canvas):
- canvas.configure(scrollregion=canvas.bbox("all"))
+ # Focus zurücksetzen
+ event.widget.focus_set()
def _createiogroup(self, device, frame, iotype):
- """Erstellt IO-Gruppen."""
- # IOs generieren
- canvas = tkinter.Canvas(frame, borderwidth=0, width=180, heigh=800)
+ u"""Erstellt IO-Gruppen.
+
+ @param device Deviceposition
+ @param frame tkinter Frame
+ @param iotype 'inp' oder 'out' als str()
+
+ """
+ # IO-Typen festlegen
+ if iotype == "inp":
+ lst_io = self.dict_inps[device]
+ else:
+ lst_io = self.dict_outs[device]
+
+ # Fensterinhalt aufbauen
+ calc_heigh = len(lst_io) * 21
+ canvas = tkinter.Canvas(
+ frame,
+ borderwidth=0,
+ width=180,
+ heigh=calc_heigh if calc_heigh <= 600 else 600
+ )
s_frame = tkinter.Frame(canvas)
vsb = tkinter.Scrollbar(frame, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
+ # Scrollrad Linux
+ canvas.bind(
+ "",
+ lambda x: canvas.yview_scroll(-1, "units")
+ )
+ canvas.bind(
+ "",
+ lambda x: canvas.yview_scroll(1, "units")
+ )
+
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4, 4), window=s_frame, anchor="nw")
s_frame.bind(
- "", lambda event, canvas=canvas: self.onfrmconf(canvas)
+ "", lambda event, canvas=canvas: self._onfrmconf(canvas)
)
+ # IOs generieren
rowcount = 0
- for io in self.cli.get_iolist(device, iotype):
- # io = [name,default,anzbits,adressbyte,export,adressid,bmk,bitaddress,tkinter_var]
+ for io in lst_io:
+ # io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
tkinter.Label(s_frame, text=io[0]).grid(
column=0, row=rowcount, sticky="w"
)
- if io[7] >= 0:
+ if io[4] >= 0:
var = tkinter.BooleanVar()
check = tkinter.Checkbutton(s_frame)
+ check["command"] = \
+ lambda device=device, io=io: self.__chval(device, io)
check["state"] = "disabled" if iotype == "inp" else "normal"
check["text"] = ""
check["variable"] = var
check.grid(column=1, row=rowcount)
else:
var = tkinter.IntVar()
- txt = tkinter.Spinbox(s_frame, to=256)
+ txt = tkinter.Spinbox(s_frame, to=256 ** io[1] - 1)
+ txt.bind(
+ "",
+ lambda event, tkvar=var: self.__saveoldvalue(event, tkvar)
+ )
+ txt.bind(
+ "",
+ lambda event, device=device, io=io:
+ self.__spinboxkey(device, io, event)
+ )
+ txt["command"] = \
+ lambda device=device, io=io: self.__chval(device, io)
txt["state"] = "disabled" if iotype == "inp" else "normal"
- txt["width"] = 4
+ txt["width"] = 5
txt["textvariable"] = var
txt.grid(column=1, row=rowcount)
- # Steuerelementvariable in IO übernehmen
+ # Steuerelementvariable in IO übernehmen (mutabel)
io.append(var)
- if iotype == "inp":
- self.dict_inpvar[device].append(io)
- elif iotype == "out":
- self.dict_outvar[device].append(io)
rowcount += 1
def _createwidgets(self):
"""Erstellt den Fensterinhalt."""
- # Hauptfenster
- self.master.wm_title("RevPi Onlineview")
+ cFxPxy53 = {"fill": "x", "padx": 5, "pady": 3}
+
+ devgrp = tkinter.LabelFrame(self)
+ devgrp["text"] = _("Devices of RevPi")
+ devgrp.pack(expand=True, fill="both", side="left")
for dev in self.lst_devices:
- # Variablen vorbereiten
- self.dict_inpvar[dev] = []
- self.dict_outvar[dev] = []
+ win = tkinter.Toplevel(self)
+ win.wm_title(self.dict_devices[dev])
+ win.protocol(
+ "WM_DELETE_WINDOW",
+ lambda win=win: self.__hidewin(win)
+ )
+ win.resizable(False, True)
+ win.withdraw()
+ self.dict_wins[dev] = win
# Devicegruppe erstellen
- group = tkinter.LabelFrame(self)
- group["text"] = dev
+ group = tkinter.LabelFrame(win)
+ group["text"] = self.dict_devices[dev]
group.pack(side="left", fill="both", expand=True)
- self.lst_group.append(group)
for iotype in ["inp", "out"]:
frame = tkinter.Frame(group)
frame.pack(side="left", fill="both", expand=True)
self._createiogroup(dev, frame, iotype)
-# self.btn_update = tkinter.Button(self)
-# self.btn_update["text"] = "UPDATE"
-# self.btn_update["command"] = self._autorw
-# self.btn_update.pack(anchor="s", side="bottom", fill="x")
+ # Button erstellen
+ btn = tkinter.Button(devgrp)
+ btn["command"] = lambda win=win: self.__showwin(win)
+ btn["text"] = self.dict_devices[dev]
+ btn.pack(**cFxPxy53)
- self.btn_write = tkinter.Button(self)
- self.btn_write["text"] = "SCHREIBEN"
- self.btn_write["command"] = self.writevalues
- self.btn_write.pack(side="bottom", fill="x")
+ # Steuerungsfunktionen
+ cntgrp = tkinter.LabelFrame(self)
+ cntgrp["text"] = _("Control")
+ cntgrp.pack(expand=True, fill="both", side="right")
- self.btn_read = tkinter.Button(self)
- self.btn_read["text"] = "LESEN"
+ self.btn_refresh = tkinter.Button(cntgrp)
+ self.btn_refresh["text"] = _("Read all IOs")
+ self.btn_refresh["command"] = self.refreshvalues
+ self.btn_refresh.pack(**cFxPxy53)
+
+ self.btn_read = tkinter.Button(cntgrp)
+ self.btn_read["text"] = _("Read just Inputs")
self.btn_read["command"] = self.readvalues
- self.btn_read.pack(side="bottom", fill="x")
+ self.btn_read.pack(**cFxPxy53)
- check = tkinter.Checkbutton(self)
- check["command"] = self.toggleauto
- check["text"] = "autoupdate"
- check["variable"] = self.autorw
- check.pack(side="bottom")
+ self.btn_write = tkinter.Button(cntgrp)
+ self.btn_write["state"] = "normal" if self.xmlmode >= 3 \
+ else "disabled"
+ self.btn_write["text"] = _("Write Outputs")
+ self.btn_write["command"] = self.writevalues
+ self.btn_write.pack(**cFxPxy53)
- def _readvaluesdev(self, device, iotype):
- """Ruft alle aktuellen Werte fuer das Device ab."""
- # Multicall vorbereiten
- mc_values = MultiCall(self.cli)
+ self.chk_auto = tkinter.Checkbutton(cntgrp)
+ self.chk_auto["command"] = self.toggleauto
+ self.chk_auto["text"] = _("Autorefresh values")
+ self.chk_auto["variable"] = self.autorw
+ self.chk_auto.pack(anchor="w")
- if iotype == "inp":
- lst_ios = self.dict_inpvar[device]
- elif iotype == "out":
- lst_ios = self.dict_outvar[device]
+ self.chk_dowrite = tkinter.Checkbutton(cntgrp)
+ self.chk_dowrite["command"] = self.togglewrite
+ self.chk_dowrite["state"] = "normal" if self.xmlmode >= 3 \
+ and self.autorw.get() else "disabled"
+ self.chk_dowrite["text"] = _("Write values to RevPi")
+ self.chk_dowrite["variable"] = self.dowrite
+ self.chk_dowrite.pack(anchor="w")
- for io in lst_ios:
- mc_values.get_iovalue(device, io[0])
+ def _onfrmconf(self, canvas):
+ u"""Erstellt Fenster in einem Canvas.
+ @param canvas Canvas in dem Objekte erstellt werden sollen"""
+ canvas.configure(scrollregion=canvas.bbox("all"))
- i = 0
- for value in mc_values():
- value = pickle.loads(value.data)
- if type(value) == bytes:
- value = int.from_bytes(value, byteorder="little")
+ def _warnwrite(self):
+ u"""Warnung für Benutzer über Schreibfunktion einmal fragen.
+ @return True, wenn Warnung einmal mit OK bestätigt wurde"""
+ if self.__checkwrite:
+ self.__checkwrite = not tkmsg.askokcancel(
+ _("Warning"),
+ _("You want to set outputs on the RevPi! Note that these are "
+ "set IMMEDIATELY!!! \nIf another control program is "
+ "running on the RevPi, it could interfere and reset the "
+ "outputs."),
+ icon=tkmsg.WARNING,
+ parent=self.master
+ )
+ return not self.__checkwrite
- lst_ios[i][8].set(value)
- i += 1
+ def _workvalues(self, io_dicts=None, writeout=False):
+ u"""Alle Werte der Inputs und Outputs abrufen.
- def _writevaluesdev(self, device):
- """Sendet Werte der Outputs fuer ein Device."""
- # Multicall vorbereiten
- mc_values = MultiCall(self.cli)
- lst_ios = lst_ios = self.dict_outvar[device]
+ @param io_dicts Arbeit nur für dieses Dict()
+ @param writeout Änderungen auf RevPi schreiben
+ @return None
- for io in lst_ios:
- mc_values.set_iovalue(device, io[0], pickle.dumps(io[8].get(), 3))
+ """
+ # Abfragelisten vorbereiten
+ if io_dicts is None:
+ io_dicts = [self.dict_inps, self.dict_outs]
- # Multicall ausführen
- mc_values()
+ # Werte abrufen
+ with self.lk:
+ try:
+ ba_values = bytearray(self.cli.ps_values().data)
+ self.err_workvalues = 0
+ except:
+ if self.autorw.get():
+ self.err_workvalues += 1
+ else:
+ self.err_workvalues = self.max_errors
+
+ if self.err_workvalues >= self.max_errors:
+ self.hideallwindows()
+ self.pack_forget()
+
+ return None
+
+ # Multicall zum Schreiben vorbereiten
+ if writeout:
+ xmlmc = MultiCall(self.cli)
+
+ for dev in self.dict_devices:
+ # io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
+
+ # IO Typ verarbeiten
+ for iotype in io_dicts:
+ # ios verarbeiten
+ for io in iotype[dev]:
+
+ # Gesperrte Variable überspringen
+ if io[5] == self.__lockedvar:
+ continue
+
+ # Bytes umwandeln
+ int_byte = int.from_bytes(
+ ba_values[io[2]:io[2] + io[1]], byteorder="little"
+ )
+ if io[4] >= 0:
+ # Bit-IO
+ new_val = bool(int_byte & 1 << io[4])
+ if writeout and new_val != io[5].get():
+ xmlmc.ps_setvalue(dev, io[0], io[5].get())
+ else:
+ io[5].set(new_val)
+ else:
+ # Byte-IO
+ if writeout and int_byte != io[5].get():
+ xmlmc.ps_setvalue(dev, io[0], io[5].get())
+ else:
+ io[5].set(int_byte)
+
+ # Werte per Multicall schreiben
+ if writeout:
+ with self.lk:
+ self.validatereturn(xmlmc())
+
+ def hideallwindows(self):
+ u"""Versteckt alle Fenster."""
+ for win in self.dict_wins:
+ self.dict_wins[win].withdraw()
def readvalues(self):
- """Alle Werte der Inputs und Outputs abrufen."""
- # Werte aus Prozessabbild einlesen
- self.cli.readprocimg()
+ u"""Ruft nur Input Werte von RevPi ab und aktualisiert Fenster."""
+ if not self.autorw.get():
+ self._workvalues([self.dict_inps])
- for dev in self.lst_devices:
- self._readvaluesdev(dev, "inp")
- self._readvaluesdev(dev, "out")
+ def refreshvalues(self):
+ u"""Ruft alle IO Werte von RevPi ab und aktualisiert Fenster."""
+ if not self.autorw.get():
+ self._workvalues()
+
+ def tmr_workvalues(self):
+ u"""Timer für zyklische Abfrage.
+ @return None"""
+ # Verbleibener Timer könnte schon ungültig sein
+ if not self.autorw.get():
+ try:
+ self.chk_auto["state"] = "normal"
+ except:
+ pass
+ return None
+
+ self._workvalues()
+
+ self.master.after(200, self.tmr_workvalues)
def toggleauto(self):
- self.btn_read["state"] = "disabled" if self.autorw.get() else "normal"
- self.btn_write["state"] = "disabled" if self.autorw.get() else "normal"
- if self.autorw.get() \
- and (self.fut_autorw is None or self.fut_autorw.done()):
- e = ThreadPoolExecutor(max_workers=1)
- self.fut_autorw = e.submit(self._autorw)
+ u"""Schaltet zwischen Autorefresh um und aktualisiert Widgets."""
+ stateval = "disabled" if self.autorw.get() else "normal"
+ self.btn_refresh["state"] = stateval
+ self.btn_read["state"] = stateval
+ self.btn_write["state"] = stateval if self.xmlmode >= 3 \
+ else "disabled"
+
+ self.chk_dowrite["state"] = "normal" if self.xmlmode >= 3 \
+ and self.autorw.get() else "disabled"
+
+ if self.autorw.get():
+ self.tmr_workvalues()
+ else:
+ self.chk_auto["state"] = "disabled"
+ self.dowrite.set(False)
+
+ def togglewrite(self):
+ u"""Schaltet zwischen DoWrite um und aktiviert Schreibfunktion."""
+ if self._warnwrite():
+ self.refreshvalues()
+ else:
+ self.dowrite.set(False)
+
+ def validatereturn(self, returnlist):
+ u"""Überprüft die Rückgaben der setvalue Funktion.
+ @param returnlist list() der xml Rückgabe"""
+ if type(returnlist[0]) != list:
+ returnlist = [returnlist]
+
+ str_errmsg = ""
+ for lst_result in returnlist:
+ # [device, io, status, msg]
+
+ if not lst_result[2]:
+ # Fehlermeldungen erstellen
+ devicename = self.dict_devices[lst_result[0]]
+ str_errmsg += _(
+ "Error set value of device '{}' Output '{}': {} \n"
+ ).format(devicename, lst_result[1], lst_result[3])
+ if str_errmsg != "":
+ tkmsg.showerror(_("Error"), str_errmsg)
def writevalues(self):
- """Alle Outputs senden."""
- pass
- #for dev in self.lst_devices:
- #self._writevaluesdev(dev)
-
- # Werte in Prozessabbild schreiben
- #self.cli.writeprocimg()
-
-if __name__ == "__main__":
- root = tkinter.Tk()
- myapp = RevPiCheckClient(root)
- myapp.mainloop()
+ u"""Schreibt geänderte Outputs auf den RevPi."""
+ if self._warnwrite() and not self.autorw.get():
+ self._workvalues([self.dict_outs], True)
diff --git a/revpipycontrol/revpiinfo.py b/revpipycontrol/revpiinfo.py
new file mode 100644
index 0000000..775ba9b
--- /dev/null
+++ b/revpipycontrol/revpiinfo.py
@@ -0,0 +1,143 @@
+#
+# RevPiPyControl
+#
+# Webpage: https://revpimodio.org/revpipyplc/
+# (c) Sven Sager, License: LGPLv3
+#
+# -*- coding: utf-8 -*-
+import tkinter
+import tkinter.font as tkf
+import webbrowser
+from mytools import gettrans
+
+# Übersetzung laden
+_ = gettrans()
+
+
+class RevPiInfo(tkinter.Frame):
+
+ def __init__(self, master, xmlcli, version):
+ u"""Init RevPiLogfile-Class."""
+ self.master = master
+ self.xmlcli = xmlcli
+
+ # Systemvariablen
+ self.version = version
+
+ # Fenster bauen
+ self._createwidgets()
+
+ def _checkclose(self, event=None):
+ u"""Prüft ob Fenster beendet werden soll.
+ @param event tkinter-Event"""
+ self.master.destroy()
+
+ def _createwidgets(self, extended=False):
+ u"""Erstellt alle Widgets."""
+ super().__init__(self.master)
+ self.master.wm_title(_("RevPi Python PLC info"))
+ self.master.bind("", self._checkclose)
+ self.master.resizable(False, False)
+ self.pack(fill="both", expand=True)
+
+ # Fonts laden
+ fntlarge = tkf.Font(size=20, weight="bold")
+ fntmid = tkf.Font(size=15)
+ fntbold = tkf.Font(size=10, weight="bold")
+
+ # Kopfdaten
+ lbl = tkinter.Label(self)
+ lbl["font"] = fntlarge
+ lbl["text"] = _("RevPi Python PLC - Control")
+ lbl.pack(pady=5)
+ lbl = tkinter.Label(self)
+ lbl["font"] = fntmid
+ lbl["text"] = _("Version: {}").format(self.version)
+ lbl.bind(
+ "",
+ lambda event: self._createwidgets(extended=not extended)
+ )
+ lbl.pack(pady=5)
+
+ # Mittelframe geteilt (links/rechts) ---------------------------------
+ frame_main = tkinter.Frame(self)
+ frame_main.pack(anchor="nw", fill="x", pady=5)
+
+ # Rows konfigurieren
+ frame_main.rowconfigure(0, weight=0)
+ frame_main.rowconfigure(1, weight=1)
+ frame_main.rowconfigure(2, weight=1)
+
+ int_row = 0
+ cpadnw = {"padx": 4, "pady": 2, "sticky": "nw"}
+ cpadsw = {"padx": 4, "pady": 2, "sticky": "sw"}
+
+ # Linke Seite Mittelframe ----------------
+ lbl = tkinter.Label(frame_main)
+ lbl["font"] = fntbold
+ lbl["text"] = _("RevPiPyLoad version on RevPi:")
+ lbl.grid(column=0, row=int_row, **cpadnw)
+
+ lbl = tkinter.Label(frame_main)
+ lbl["font"] = fntbold
+ lbl["text"] = _("not conn.") if self.xmlcli is None \
+ else self.xmlcli.version()
+ lbl.grid(column=1, row=int_row, **cpadnw)
+
+ int_row += 1 # 1
+ lbl = tkinter.Label(frame_main)
+ lbl["justify"] = "left"
+ lbl["text"] = _(
+ "\nRevPiModIO, RevPiPyLoad and RevPiPyControl\n"
+ "are community driven projects. They are all\n"
+ "free and open source software.\n"
+ "All of them comes with ABSOLUTELY NO\n"
+ "WARRANTY, to the extent permitted by \n"
+ "applicable law.\n"
+ "\n"
+ "\n"
+ "(c) Sven Sager, License: LGPLv3"
+ )
+ lbl.grid(column=0, row=int_row, columnspan=2, **cpadnw)
+
+ int_row += 1 # 2
+ lbl = tkinter.Label(frame_main)
+ lbl.bind("", self.visitwebsite)
+ lbl["fg"] = "blue"
+ lbl["text"] = "https://revpimodio.org/"
+ lbl.grid(column=0, row=int_row, columnspan=2, **cpadsw)
+
+ # int_row += 1 # 3
+
+ # Rechte Seite Mittelframe ---------------
+
+ # Funktionen der Gegenstelle
+ if self.xmlcli is not None:
+ frame_func = tkinter.Frame(frame_main)
+ txt_xmlfunc = tkinter.Text(frame_func, width=30, height=15)
+ scr_xmlfunc = tkinter.Scrollbar(frame_func)
+ if extended:
+ txt_xmlfunc.insert(tkinter.END, "\n".join(
+ self.xmlcli.system.listMethods()
+ ))
+ elif "get_filelist" in self.xmlcli.system.listMethods():
+ txt_xmlfunc.insert(tkinter.END, "\n".join(
+ self.xmlcli.get_filelist()
+ ))
+ txt_xmlfunc["yscrollcommand"] = scr_xmlfunc.set
+ txt_xmlfunc["state"] = "disabled"
+ scr_xmlfunc["command"] = txt_xmlfunc.yview
+ txt_xmlfunc.pack(side="left")
+ scr_xmlfunc.pack(fill="y", side="right")
+ if txt_xmlfunc.get(1.0) != "\n":
+ frame_func.grid(column=3, row=0, rowspan=int_row + 1, **cpadnw)
+
+ # Unten Beenden-Button -----------------------------------------------
+ self.btnapplog = tkinter.Button(self)
+ self.btnapplog["command"] = self._checkclose
+ self.btnapplog["text"] = _("Close")
+ self.btnapplog.pack(fill="x", padx=100)
+
+ def visitwebsite(self, event=None):
+ u"""Öffnet auf dem System einen Webbrowser zur Projektseite."""
+ webbrowser.open("https://revpimodio.org")
diff --git a/revpipycontrol/revpilogfile.py b/revpipycontrol/revpilogfile.py
index 0de9088..5595303 100644
--- a/revpipycontrol/revpilogfile.py
+++ b/revpipycontrol/revpilogfile.py
@@ -5,22 +5,40 @@
# (c) Sven Sager, License: LGPLv3
#
# -*- coding: utf-8 -*-
-import pickle
import tkinter
+from mytools import gettrans
+
+# Übersetzung laden
+_ = gettrans()
class RevPiLogfile(tkinter.Frame):
def __init__(self, master, xmlcli):
+ u"""Init RevPiLogfile-Class."""
super().__init__(master)
+ self.master.bind("", self._checkclose)
self.pack(fill="both", expand=True)
self.xmlcli = xmlcli
+ # Systemvariablen
+ self.loadblock = 16384
+ self.errapp = 0
+ self.errplc = 0
+ self.mrkapp = 0
+ self.mrkplc = 0
+
# Fenster bauen
self._createwidgets()
+ def _checkclose(self, event=None):
+ u"""Prüft ob Fenster beendet werden soll.
+ @param event tkinter-Event"""
+ self.master.destroy()
+
def _createwidgets(self):
- self.master.wm_title("RevPi Python PLC Logs")
+ u"""Erstellt alle Widgets."""
+ self.master.wm_title(_("RevPi Python PLC Logs"))
self.rowconfigure(0, weight=0)
self.rowconfigure(1, weight=1)
@@ -33,11 +51,11 @@ class RevPiLogfile(tkinter.Frame):
# PLC Log
self.lblapplog = tkinter.Label(self)
- self.lblapplog["text"] = "RevPyPyLoad - Logfile"
+ self.lblapplog["text"] = _("RevPiPyLoad - Logfile")
self.lblapplog.grid(column=0, row=0, sticky="w")
self.btnapplog = tkinter.Button(self)
self.btnapplog["command"] = self.btn_clearplc
- self.btnapplog["text"] = "Clear screen"
+ self.btnapplog["text"] = _("Clear screen")
self.btnapplog.grid(column=1, row=0, sticky="e")
self.plclog = tkinter.Text(self)
self.plcscr = tkinter.Scrollbar(self)
@@ -48,11 +66,11 @@ class RevPiLogfile(tkinter.Frame):
# APP Log
self.lblapplog = tkinter.Label(self)
- self.lblapplog["text"] = "Python PLC program - Logfile"
+ self.lblapplog["text"] = _("Python PLC program - Logfile")
self.lblapplog.grid(column=3, row=0, sticky="w")
self.btnapplog = tkinter.Button(self)
self.btnapplog["command"] = self.btn_clearapp
- self.btnapplog["text"] = "Clear screen"
+ self.btnapplog["text"] = _("Clear screen")
self.btnapplog.grid(column=4, row=0, sticky="e")
self.applog = tkinter.Text(self)
self.appscr = tkinter.Scrollbar(self)
@@ -61,47 +79,92 @@ class RevPiLogfile(tkinter.Frame):
self.applog["yscrollcommand"] = self.appscr.set
self.appscr["command"] = self.applog.yview
- self.get_applog()
- self.get_plclog()
-
- # Timer zum nachladen aktivieren
- self.master.after(1000, self.get_applines)
- self.master.after(1000, self.get_plclines)
+ # Logtimer zum Laden starten
+ self.get_applog(full=True)
+ self.get_plclog(full=True)
def btn_clearapp(self):
+ u"""Leert die Logliste der App."""
self.applog.delete(1.0, tkinter.END)
def btn_clearplc(self):
+ u"""Leert die Logliste des PLC."""
self.plclog.delete(1.0, tkinter.END)
- def get_applines(self):
- roll = self.applog.yview()[1] == 1.0
+ def get_applog(self, full=False):
+ u"""Ruft App Logbuch ab.
+ @param full Ganzes Logbuch laden"""
+
+ # Logs abrufen und letzte Position merken
try:
- for line in pickle.loads(self.xmlcli.get_applines().data):
- self.applog.insert(tkinter.END, line)
+ self.mrkapp = self._load_log(
+ self.applog, self.xmlcli.load_applog, self.mrkapp, full
+ )
+ self.errapp = 0
except:
- pass
- if roll:
- self.applog.see(tkinter.END)
- self.master.after(1000, self.get_applines)
+ self.errapp += 1
- def get_applog(self):
- self.applog.delete(1.0, tkinter.END)
- self.applog.insert(1.0, pickle.loads(self.xmlcli.get_applog().data))
- self.applog.see(tkinter.END)
+ # Timer neu starten
+ self.master.after(1000, self.get_applog)
- def get_plclines(self):
- roll = self.plclog.yview()[1] == 1.0
+ def get_plclog(self, full=False):
+ u"""Ruft PLC Logbuch ab.
+ @param full Ganzes Logbuch laden"""
+
+ # Logs abrufen und letzte Position merken
try:
- for line in pickle.loads(self.xmlcli.get_plclines().data):
- self.plclog.insert(tkinter.END, line)
+ self.mrkplc = self._load_log(
+ self.plclog, self.xmlcli.load_plclog, self.mrkplc, full
+ )
+ self.errplc = 0
except:
- pass
- if roll:
- self.plclog.see(tkinter.END)
- self.master.after(1000, self.get_plclines)
+ self.errplc += 1
- def get_plclog(self):
- self.plclog.delete(1.0, tkinter.END)
- self.plclog.insert(1.0, pickle.loads(self.xmlcli.get_plclog().data))
- self.plclog.see(tkinter.END)
+ # Timer neu starten
+ self.master.after(1000, self.get_plclog)
+
+ def _load_log(self, textwidget, xmlcall, startposition, full):
+ u"""Läd die angegebenen Logfiles herunter.
+
+ @param textwidget Widget in das Logs eingefügt werden sollen
+ @param xmlcall xmlrpc Funktion zum Abrufen der Logdaten
+ @param startposition Startposition ab der Logdaten kommen sollen
+ @param full Komplettes Logbuch laden
+ @return Ende der Datei (neue Startposition)
+
+ """
+ roll = textwidget.yview()[1] == 1.0
+ startposition = 0 if full else startposition
+ logbytes = b''
+ while True:
+ # Datenblock vom XML-RPC Server holen
+ bytebuff = xmlcall(startposition, self.loadblock).data
+
+ logbytes += bytebuff
+ startposition += len(bytebuff)
+
+ # Prüfen ob alle Daten übertragen wurden
+ if len(bytebuff) < self.loadblock:
+ break
+
+ if full:
+ textwidget.delete(1.0, tkinter.END)
+
+ if bytebuff == b'\x16': #
+ # Kein Zugriff auf Logdatei
+ textwidget.delete(1.0, tkinter.END)
+ textwidget.insert(
+ tkinter.END, _("Can not access log file on the RevPi")
+ )
+ elif bytebuff == b'\x19': #
+ # Logdatei neu begonnen
+ startposition = 0
+ else:
+ # Text in Widget übernehmen
+ textwidget.insert(tkinter.END, logbytes.decode("utf-8"))
+
+ # Automatisch ans Ende rollen
+ if roll or full:
+ textwidget.see(tkinter.END)
+
+ return startposition
diff --git a/revpipycontrol/revpioption.py b/revpipycontrol/revpioption.py
index 68dce00..c7bfd1d 100644
--- a/revpipycontrol/revpioption.py
+++ b/revpipycontrol/revpioption.py
@@ -7,35 +7,84 @@
# -*- coding: utf-8 -*-
import tkinter
import tkinter.messagebox as tkmsg
+from mytools import gettrans
+
+# Übersetzung laden
+_ = gettrans()
class RevPiOption(tkinter.Frame):
- def __init__(self, master, xmlcli, xmlmode):
- if xmlmode < 2:
+ def __init__(self, master, xmlcli):
+ u"""Init RevPiOption-Class.
+ @return None"""
+ try:
+ self.dc = xmlcli.get_config()
+ except:
+ self.dc = None
return None
super().__init__(master)
+ self.master.bind("", self._checkclose)
+ self.master.protocol("WM_DELETE_WINDOW", self._checkclose)
self.pack(expand=True, fill="both")
self.xmlcli = xmlcli
- self.xmlmode = xmlmode
- self.xmlstate = "normal" if xmlmode == 3 else "disabled"
+ self.mrk_var_xmlmod2 = False
+ self.mrk_var_xmlmod3 = False
+ self.mrk_xmlmodask = False
+ self.dorestart = False
# Fenster bauen
self._createwidgets()
self._loadappdata()
+ def _changesdone(self):
+ u"""Prüft ob sich die Einstellungen geändert haben.
+ @return True, wenn min. eine Einstellung geändert wurde"""
+ return (
+ self.var_start.get() != self.dc.get("autostart", "1")
+ or self.var_reload.get() != self.dc.get("autoreload", "1")
+ or self.var_zexit.get() != self.dc.get("zeroonexit", "0")
+ or self.var_zerr.get() != self.dc.get("zeroonerror", "0")
+ or self.var_startpy.get() != self.dc.get("plcprogram", "none.py")
+ or self.var_startargs.get() != self.dc.get("plcarguments", "")
+ or self.var_pythonver.get() != self.dc.get("pythonversion", "3")
+ or self.var_slave.get() != self.dc.get("plcslave", "0")
+ or self.var_xmlon.get() != (self.dc.get("xmlrpc", 0) >= 1)
+ or self.var_xmlmod2.get() != (self.dc.get("xmlrpc", 0) >= 2)
+ or self.var_xmlmod3.get() != (self.dc.get("xmlrpc", 0) >= 3)
+ or self.var_xmlport.get() != self.dc.get("xmlrpcport", "55123")
+ )
+
+ def _checkclose(self, event=None):
+ u"""Prüft ob Fenster beendet werden soll.
+ @param event tkinter-Event"""
+ ask = True
+ if self._changesdone():
+ ask = tkmsg.askyesno(
+ _("Question"),
+ _("Do you really want to quit? \nUnsaved changes will "
+ "be lost"),
+ parent=self.master, default="no"
+ )
+
+ if ask:
+ self.master.destroy()
+
def _createwidgets(self):
- self.master.wm_title("RevPi Python PLC Options")
+ u"""Erstellt Widgets."""
+ self.master.wm_title(_("RevPi Python PLC Options"))
self.master.wm_resizable(width=False, height=False)
+ xmlstate = "normal" if self.dc["xmlrpc"] >= 3 else "disabled"
+
cpadw = {"padx": 4, "pady": 2, "sticky": "w"}
cpadwe = {"padx": 4, "pady": 2, "sticky": "we"}
# Gruppe Start/Stop
stst = tkinter.LabelFrame(self)
- stst["text"] = "Start / Stopp Verhalten"
+ stst["text"] = _("Start / Stop behavior")
stst.grid(columnspan=2, pady=2, sticky="we")
self.var_start = tkinter.BooleanVar(stst)
@@ -44,34 +93,36 @@ class RevPiOption(tkinter.Frame):
self.var_zerr = tkinter.BooleanVar(stst)
ckb_start = tkinter.Checkbutton(stst)
- ckb_start["text"] = "Programm automatisch starten"
- ckb_start["state"] = self.xmlstate
+ ckb_start["text"] = _("Start program automatically")
+ ckb_start["state"] = xmlstate
ckb_start["variable"] = self.var_start
ckb_start.grid(**cpadw)
ckb_reload = tkinter.Checkbutton(stst)
- ckb_reload["text"] = "Programm nach Beenden neu starten"
- ckb_reload["state"] = self.xmlstate
+ ckb_reload["text"] = _("Restart program after exit")
+ ckb_reload["state"] = xmlstate
ckb_reload["variable"] = self.var_reload
ckb_reload.grid(**cpadw)
ckb_zexit = tkinter.Checkbutton(stst, justify="left")
- ckb_zexit["state"] = self.xmlstate
- ckb_zexit["text"] = "Prozessabbild auf NULL setzen, wenn " \
- "Programm\nerfolgreich beendet wird"
+ ckb_zexit["state"] = xmlstate
+ ckb_zexit["text"] = _(
+ "Set process image to NULL if program\n"
+ "terminates successfully")
ckb_zexit["variable"] = self.var_zexit
ckb_zexit.grid(**cpadw)
ckb_zerr = tkinter.Checkbutton(stst, justify="left")
- ckb_zerr["state"] = self.xmlstate
- ckb_zerr["text"] = "Prozessabbild auf NULL setzen, wenn " \
- "Programm\ndurch Absturz beendet wird"
+ ckb_zerr["state"] = xmlstate
+ ckb_zerr["text"] = _(
+ "Set process image to NULL if program\n"
+ "terminates with errors")
ckb_zerr["variable"] = self.var_zerr
ckb_zerr.grid(**cpadw)
# Gruppe Programm
prog = tkinter.LabelFrame(self)
- prog["text"] = "PLC Programm"
+ prog["text"] = _("PLC program")
prog.grid(columnspan=2, pady=2, sticky="we")
self.var_pythonver = tkinter.IntVar(prog)
@@ -82,24 +133,24 @@ class RevPiOption(tkinter.Frame):
self.var_pythonver.set(3)
lbl = tkinter.Label(prog)
- lbl["text"] = "Python Version"
+ lbl["text"] = _("Python version")
lbl.grid(columnspan=2, row=0, **cpadw)
rbn = tkinter.Radiobutton(prog)
- rbn["state"] = self.xmlstate
+ rbn["state"] = xmlstate
rbn["text"] = "Python2"
rbn["value"] = 2
rbn["variable"] = self.var_pythonver
rbn.grid(column=0, row=1, **cpadw)
rbn = tkinter.Radiobutton(prog)
- rbn["state"] = self.xmlstate
+ rbn["state"] = xmlstate
rbn["text"] = "Python3"
rbn["value"] = 3
rbn["variable"] = self.var_pythonver
rbn.grid(column=1, row=1, **cpadw)
lbl = tkinter.Label(prog)
- lbl["text"] = "Python PLC Programname"
+ lbl["text"] = _("Python PLC program name")
lbl.grid(columnspan=2, **cpadw)
lst = self.xmlcli.get_filelist()
@@ -107,11 +158,11 @@ class RevPiOption(tkinter.Frame):
lst.append("none")
opt_startpy = tkinter.OptionMenu(
prog, self.var_startpy, *lst)
- opt_startpy["state"] = self.xmlstate
+ opt_startpy["state"] = xmlstate
opt_startpy.grid(columnspan=2, **cpadwe)
lbl = tkinter.Label(prog)
- lbl["text"] = "Programm Argumente"
+ lbl["text"] = _("Program arguments")
lbl.grid(columnspan=2, **cpadw)
txt = tkinter.Entry(prog)
@@ -119,15 +170,15 @@ class RevPiOption(tkinter.Frame):
txt.grid(columnspan=2, **cpadw)
ckb_slave = tkinter.Checkbutton(prog, justify="left")
- ckb_slave["state"] = self.xmlstate
- ckb_slave["text"] = "RevPi als PLC-Slave verwenden"
+ ckb_slave["state"] = xmlstate
+ ckb_slave["text"] = _("Use RevPi as PLC-Slave")
ckb_slave["state"] = "disabled"
ckb_slave["variable"] = self.var_slave
ckb_slave.grid(columnspan=2, **cpadw)
# Gruppe XMLRPC
xmlrpc = tkinter.LabelFrame(self)
- xmlrpc["text"] = "XML-RPC Server"
+ xmlrpc["text"] = _("XML-RPC server")
xmlrpc.grid(columnspan=2, pady=2, sticky="we")
self.var_xmlon = tkinter.BooleanVar(xmlrpc)
@@ -138,124 +189,161 @@ class RevPiOption(tkinter.Frame):
ckb_xmlon = tkinter.Checkbutton(xmlrpc)
ckb_xmlon["command"] = self.askxmlon
- ckb_xmlon["state"] = self.xmlstate
- ckb_xmlon["text"] = "XML-RPC Server aktiv auf RevPi"
+ ckb_xmlon["state"] = xmlstate
+ ckb_xmlon["text"] = _("Activate XML-RPC server on RevPi")
ckb_xmlon["variable"] = self.var_xmlon
ckb_xmlon.grid(**cpadw)
self.ckb_xmlmod2 = tkinter.Checkbutton(xmlrpc, justify="left")
- self.ckb_xmlmod2["command"] = self.xmlmods
- self.ckb_xmlmod2["state"] = self.xmlstate
+ self.ckb_xmlmod2["command"] = self.xmlmod2_tail
+ self.ckb_xmlmod2["state"] = xmlstate
self.ckb_xmlmod2["text"] = \
- "Download von piCtory Konfiguration und\nPLC Programm zulassen"
+ _("Allow download of piCtory configuration and\nPLC programm")
self.ckb_xmlmod2["variable"] = self.var_xmlmod2
self.ckb_xmlmod2.grid(**cpadw)
self.ckb_xmlmod3 = tkinter.Checkbutton(xmlrpc, justify="left")
- self.ckb_xmlmod3["state"] = self.xmlstate
+ self.ckb_xmlmod3["state"] = xmlstate
self.ckb_xmlmod3["text"] = \
- "Upload von piCtory Konfiguration und\nPLC Programm zualssen"
+ _("Allow upload of piCtory configuration and\nPLC programm")
self.ckb_xmlmod3["variable"] = self.var_xmlmod3
self.ckb_xmlmod3.grid(**cpadw)
lbl = tkinter.Label(xmlrpc)
- lbl["text"] = "XML-RPC Serverport"
+ lbl["text"] = _("XML-RPC server port")
lbl.grid(**cpadw)
spb_xmlport = tkinter.Spinbox(xmlrpc)
spb_xmlport["to"] = 65535
spb_xmlport["from"] = 1024
- spb_xmlport["state"] = self.xmlstate
+ spb_xmlport["state"] = xmlstate
spb_xmlport["textvariable"] = self.var_xmlport
spb_xmlport.grid(**cpadwe)
# Buttons
btn_save = tkinter.Button(self)
btn_save["command"] = self._setappdata
- btn_save["state"] = self.xmlstate
- btn_save["text"] = "Speichern"
+ btn_save["state"] = xmlstate
+ btn_save["text"] = _("Save")
btn_save.grid(column=0, row=3)
btn_close = tkinter.Button(self)
- btn_close["command"] = self.master.destroy
- btn_close["text"] = "Schließen"
+ btn_close["command"] = self._checkclose
+ btn_close["text"] = _("Close")
btn_close.grid(column=1, row=3)
- def _loadappdata(self):
- dc = self.xmlcli.get_config()
+ def _loadappdata(self, refresh=False):
+ u"""Läd aktuelle Einstellungen vom RevPi.
+ @param refresh Wenn True, werden Einstellungen heruntergeladen."""
+ if refresh:
+ self.dc = self.xmlcli.get_config()
- self.var_start.set(dc.get("autostart", "1"))
- self.var_reload.set(dc.get("autoreload", "1"))
- self.var_zexit.set(dc.get("zeroonexit", "0"))
- self.var_zerr.set(dc.get("zeroonerror", "0"))
+ self.var_start.set(self.dc.get("autostart", "1"))
+ self.var_reload.set(self.dc.get("autoreload", "1"))
+ self.var_zexit.set(self.dc.get("zeroonexit", "0"))
+ self.var_zerr.set(self.dc.get("zeroonerror", "0"))
- self.var_startpy.set(dc.get("plcprogram", "none.py"))
- self.var_startargs.set(dc.get("plcarguments", ""))
- self.var_pythonver.set(dc.get("pythonversion", "3"))
- self.var_slave.set(dc.get("plcslave", "0"))
+ self.var_startpy.set(self.dc.get("plcprogram", "none.py"))
+ self.var_startargs.set(self.dc.get("plcarguments", ""))
+ self.var_pythonver.set(self.dc.get("pythonversion", "3"))
+ self.var_slave.set(self.dc.get("plcslave", "0"))
- self.var_xmlon.set(dc.get("xmlrpc", 0) >= 1)
- self.var_xmlmod2.set(dc.get("xmlrpc", 0) >= 2)
- self.var_xmlmod3.set(dc.get("xmlrpc", 0) >= 3)
+ self.var_xmlon.set(self.dc.get("xmlrpc", 0) >= 1)
+ self.var_xmlmod2.set(self.dc.get("xmlrpc", 0) >= 2)
+ self.mrk_var_xmlmod2 = self.var_xmlmod2.get()
+ self.var_xmlmod3.set(self.dc.get("xmlrpc", 0) >= 3)
+ self.mrk_var_xmlmod3 = self.var_xmlmod3.get()
- self.var_xmlport.set(dc.get("xmlrpcport", "55123"))
+ self.var_xmlport.set(self.dc.get("xmlrpcport", "55123"))
def _setappdata(self):
- dc = {}
- dc["autostart"] = int(self.var_start.get())
- dc["autoreload"] = int(self.var_reload.get())
- dc["zeroonexit"] = int(self.var_zexit.get())
- dc["zeroonerror"] = int(self.var_zerr.get())
+ u"""Speichert geänderte Einstellungen auf RevPi.
+ @return None"""
- dc["plcprogram"] = self.var_startpy.get()
- dc["plcarguments"] = self.var_startargs.get()
- dc["pythonversion"] = self.var_pythonver.get()
- dc["plcslave"] = int(self.var_slave.get())
-
- dc["xmlrpc"] = 0
- if self.var_xmlon.get():
- dc["xmlrpc"] += 1
- if self.var_xmlmod2.get():
- dc["xmlrpc"] += 1
- if self.var_xmlmod3.get():
- dc["xmlrpc"] += 1
-
- dc["xmlrpcport"] = self.var_xmlport.get()
- self.xmlmode = dc["xmlrpc"]
+ if not self._changesdone():
+ tkmsg.showinfo(
+ _("Information"),
+ _("You have not made any changes to save."),
+ )
+ self._checkclose()
+ return None
ask = tkmsg.askyesnocancel(
- "Frage", "Die Einstellungen werden jetzt auf dem Revolution Pi "
- "gespeichert. \n\nSollen die neuen Einstellungen sofort in Kraft "
- "treten? \nDies bedeutet einen Neustart des Dienstes und des ggf. "
- "laufenden PLC-Programms!", parent=self.master
+ _("Question"),
+ _("The settings are now saved on the Revolution Pi. \n\n"
+ "Should the new settings take effect immediately? \nThis "
+ "means a restart of the service and the PLC program!"),
+ parent=self.master
)
if ask is not None:
- if self.xmlcli.set_config(dc, ask):
+ self.dc["autostart"] = int(self.var_start.get())
+ self.dc["autoreload"] = int(self.var_reload.get())
+ self.dc["zeroonexit"] = int(self.var_zexit.get())
+ self.dc["zeroonerror"] = int(self.var_zerr.get())
+
+ self.dc["plcprogram"] = self.var_startpy.get()
+ self.dc["plcarguments"] = self.var_startargs.get()
+ self.dc["pythonversion"] = self.var_pythonver.get()
+ self.dc["plcslave"] = int(self.var_slave.get())
+
+ self.dc["xmlrpc"] = 0
+ if self.var_xmlon.get():
+ self.dc["xmlrpc"] += 1
+ if self.var_xmlmod2.get():
+ self.dc["xmlrpc"] += 1
+ if self.var_xmlmod3.get():
+ self.dc["xmlrpc"] += 1
+
+ self.dc["xmlrpcport"] = self.var_xmlport.get()
+
+ if self.xmlcli.set_config(self.dc, ask):
tkmsg.showinfo(
- "Information", "Einstellungen gespeichert.",
+ _("Information"),
+ _("Settings saved"),
parent=self.master
)
+ self.dorestart = ask
+ self._checkclose()
else:
tkmsg.showerror(
- "Fehler", "Die Einstellungen konnten nicht gesichert"
- "werden. Dies kann passieren, wenn Werte falsch sind!",
+ _("Error"),
+ _("The settings could not be saved. This can happen if "
+ "values are wrong!"),
parent=self.master
)
def askxmlon(self):
- if not self.var_xmlon.get():
- ask = tkmsg.askyesno(
- "Frage", "Soll der XML-RPC Server wirklich beendet werden? "
- "Sie können dann NICHT mehr mit diesem Programm auf den "
- "Revolution Pi zugreifen.", parent=self.master
+ u"""Fragt Nuter, ob wirklicht abgeschaltet werden soll."""
+ if not (self.var_xmlon.get() or self.mrk_xmlmodask):
+ self.mrk_xmlmodask = tkmsg.askyesno(
+ _("Question"),
+ _("Are you sure you want to deactivate the XML-RPC server? "
+ "You will NOT be able to access the Revolution Pi with "
+ "this program."),
+ parent=self.master
)
- if not ask:
+ if not self.mrk_xmlmodask:
self.var_xmlon.set(True)
- self.xmlmods()
+ self.xmlmod_tail()
- def xmlmods(self):
- self.ckb_xmlmod2["state"] = \
- "normal" if self.var_xmlon.get() else "disabled"
- self.ckb_xmlmod3["state"] = \
- "normal" if self.var_xmlmod2.get() else "disabled"
+ def xmlmod_tail(self):
+ u"""Passt XML-Optionszugriff an."""
+ if self.var_xmlon.get():
+ self.var_xmlmod2.set(self.mrk_var_xmlmod2)
+ self.ckb_xmlmod2["state"] = "normal"
+ else:
+ self.mrk_var_xmlmod2 = self.var_xmlmod2.get()
+ self.var_xmlmod2.set(False)
+ self.ckb_xmlmod2["state"] = "disabled"
+ self.xmlmod2_tail()
+
+ def xmlmod2_tail(self):
+ u"""Passt XML-Optionszugriff an."""
+ if self.var_xmlmod2.get():
+ self.var_xmlmod3.set(self.mrk_var_xmlmod3)
+ self.ckb_xmlmod3["state"] = "normal"
+ else:
+ self.mrk_var_xmlmod3 = self.var_xmlmod3.get()
+ self.var_xmlmod3.set(False)
+ self.ckb_xmlmod3["state"] = "disabled"
diff --git a/revpipycontrol/revpiplclist.py b/revpipycontrol/revpiplclist.py
index db7a039..8c59b6a 100644
--- a/revpipycontrol/revpiplclist.py
+++ b/revpipycontrol/revpiplclist.py
@@ -9,10 +9,14 @@ import os.path
import pickle
import tkinter
import tkinter.messagebox as tkmsg
+from mytools import gettrans
from os import environ
from os import makedirs
from sys import platform
+# Übersetzungen laden
+_ = gettrans()
+
# Systemwerte
if platform == "linux":
homedir = environ["HOME"]
@@ -21,8 +25,9 @@ else:
savefile = os.path.join(homedir, ".revpipyplc", "connections.dat")
-# Für andere Module zum Laden der Connections
def get_connections():
+ u"""Verbindungen aus Datei laden.
+ @return dict() mit Verbindungen"""
if os.path.exists(savefile):
fh = open(savefile, "rb")
connections = pickle.load(fh)
@@ -34,21 +39,41 @@ def get_connections():
class RevPiPlcList(tkinter.Frame):
def __init__(self, master):
+ u"""Init RevPiPlcList-class.
+ @param master tkinter master"""
super().__init__(master)
+ self.master.bind("", self._checkclose)
self.pack()
self.changes = False
# Daten laden
- self._connections = {}
+ self._connections = get_connections()
# Fenster bauen
self._createwidgets()
- self._loadappdata()
+ self.build_listconn()
+
+ def _checkclose(self, event=None):
+ u"""Prüft ob Fenster beendet werden soll.
+ @param event tkinter-Event"""
+ ask = True
+ if self.changes:
+ ask = tkmsg.askyesno(
+ _("Question"),
+ _("Do you really want to quit? \nUnsaved changes will "
+ "be lost"),
+ parent=self.master
+ )
+
+ if ask:
+ self.master.destroy()
def _createwidgets(self):
- self.master.wm_title("RevPi Python PLC Connections")
+ u"""Erstellt alle Widgets."""
+ self.master.wm_title(_("RevPi Python PLC connections"))
self.master.wm_resizable(width=False, height=False)
+ self.master.protocol("WM_DELETE_WINDOW", self._checkclose)
# Listbox mit vorhandenen Verbindungen
self.scr_conn = tkinter.Scrollbar(self)
@@ -66,14 +91,14 @@ class RevPiPlcList(tkinter.Frame):
self.var_port.set("55123")
# Eingabefelder für Adresse und Namen
- tkinter.Label(self, text="Name").grid(
+ tkinter.Label(self, text=_("Name")).grid(
column=2, row=0, sticky="wn", padx=5, pady=5)
self.txt_name = tkinter.Entry(self, textvariable=self.var_name)
self.txt_name.bind("", self.evt_keypress)
self.txt_name.grid(
column=3, row=0, columnspan=3, sticky="n", padx=5, pady=5)
- tkinter.Label(self, text="IP-Adresse").grid(
+ tkinter.Label(self, text=_("IP address")).grid(
column=2, row=1, sticky="wn", padx=5, pady=5
)
self.txt_address = tkinter.Entry(self, textvariable=self.var_address)
@@ -81,7 +106,7 @@ class RevPiPlcList(tkinter.Frame):
self.txt_address.grid(
column=3, row=1, columnspan=3, sticky="n", padx=5, pady=5)
- tkinter.Label(self, text="Port").grid(
+ tkinter.Label(self, text=_("Port")).grid(
column=2, row=2, sticky="wn", padx=5, pady=5)
self.txt_port = tkinter.Entry(self, textvariable=self.var_port)
self.txt_port.bind("", self.evt_keypress)
@@ -90,32 +115,28 @@ class RevPiPlcList(tkinter.Frame):
# Listenbutton
self.btn_new = tkinter.Button(
- self, text="Neu", command=self.evt_btnnew)
+ self, text=_("New"), command=self.evt_btnnew)
self.btn_new.grid(column=2, row=3, sticky="s")
self.btn_add = tkinter.Button(
- self, text="Übernehmen", command=self.evt_btnadd,
+ self, text=_("Apply"), command=self.evt_btnadd,
state="disabled")
self.btn_add.grid(column=3, row=3, sticky="s")
self.btn_remove = tkinter.Button(
- self, text="Entfernen", command=self.evt_btnremove,
+ self, text=_("Remove"), command=self.evt_btnremove,
state="disabled")
self.btn_remove.grid(column=4, row=3, sticky="s")
# Fensterbuttons
self.btn_save = tkinter.Button(
- self, text="Speichern", command=self.evt_btnsave)
+ self, text=_("Save"), command=self.evt_btnsave)
self.btn_save.grid(column=3, row=9, sticky="se")
self.btn_close = tkinter.Button(
- self, text="Schließen", command=self.evt_btnclose)
+ self, text=_("Close"), command=self._checkclose)
self.btn_close.grid(column=4, row=9, sticky="se")
- def _loadappdata(self):
- if os.path.exists(savefile):
- fh = open(savefile, "rb")
- self._connections = pickle.load(fh)
- self.build_listconn()
-
def _saveappdata(self):
+ u"""Speichert Verbindungen im home Dir.
+ @return True, bei erfolgreicher Verarbeitung"""
try:
makedirs(os.path.dirname(savefile), exist_ok=True)
fh = open(savefile, "wb")
@@ -126,11 +147,13 @@ class RevPiPlcList(tkinter.Frame):
return True
def build_listconn(self):
+ u"""Füllt Verbindungsliste."""
self.list_conn.delete(0, "end")
lst_conns = sorted(self._connections.keys(), key=lambda x: x.lower())
self.list_conn.insert("end", *lst_conns)
def evt_btnadd(self):
+ u"""Verbindungseinstellungen übernehmen."""
# TODO: Daten prüfen
self._connections[self.var_name.get()] = \
(self.var_address.get(), self.var_port.get())
@@ -139,20 +162,8 @@ class RevPiPlcList(tkinter.Frame):
self.evt_btnnew()
self.changes = True
- def evt_btnclose(self):
- if self.changes:
- ask = tkmsg.askyesno(
- parent=self.master, title="Frage...",
- message="Wollen Sie wirklich beenden?\n"
- "Nicht gespeicherte Änderungen gehen verloren",
- )
- else:
- ask = True
-
- if ask:
- self.master.destroy()
-
def evt_btnnew(self):
+ u"""Neue Verbindung erstellen."""
self.list_conn.select_clear(0, "end")
self.evt_listconn()
@@ -162,13 +173,14 @@ class RevPiPlcList(tkinter.Frame):
self.var_port.set("55123")
def evt_btnremove(self):
+ u"""Verbindung löschen."""
item_index = self.list_conn.curselection()
if len(item_index) == 1:
item = self.list_conn.get(item_index[0])
ask = tkmsg.askyesno(
- "Frage",
- "Wollen Sie die Ausgewählte Verbindung '{}' wirklich "
- "löschen?".format(item),
+ _("Question"),
+ _("Do you really want to delete the selected connection '{}'?"
+ "").format(item),
parent=self.master
)
if ask:
@@ -179,21 +191,24 @@ class RevPiPlcList(tkinter.Frame):
self.changes = True
def evt_btnsave(self):
+ u"""Alle Verbindungen speichern."""
if self._saveappdata():
ask = tkmsg.askyesno(
- "Information", "Verbindungen erfolgreich gespeichert.\n"
- "Möchten Sie dieses Fenster jetzt schließen?",
+ _("Information"),
+ _("Successfully saved. \nDo you want to close this window?"),
parent=self.master
)
if ask:
self.master.destroy()
else:
tkmsg.showerror(
- "Fehler", "Verbindungen konnten nicht gespeichert werden",
+ _("Error"),
+ _("Failed to save connections"),
parent=self.master
)
def evt_listconn(self, evt=None):
+ u"""Übernimmt Einstellungen in Eingabefelder."""
item_index = self.list_conn.curselection()
if len(item_index) == 1:
@@ -211,6 +226,7 @@ class RevPiPlcList(tkinter.Frame):
self.btn_remove["state"] = "disabled"
def evt_keypress(self, evt=None):
+ u"""Passt bei Tastendruck den Status der Buttons an."""
okvalue = "normal" if (
self.var_address.get() != ""
and self.var_name.get() != ""
diff --git a/revpipycontrol/revpiprogram.py b/revpipycontrol/revpiprogram.py
index 01abff2..4eea90e 100644
--- a/revpipycontrol/revpiprogram.py
+++ b/revpipycontrol/revpiprogram.py
@@ -13,13 +13,16 @@ import tkinter
import tkinter.filedialog as tkfd
import tkinter.messagebox as tkmsg
import zipfile
+from mytools import gettrans
from os import environ
from os import makedirs
from shutil import rmtree
from sys import platform
-from tempfile import mktemp, mkdtemp
+from tempfile import mkstemp, mkdtemp
from xmlrpc.client import Binary
+# Übersetzung laden
+_ = gettrans()
# Systemwerte
if platform == "linux":
@@ -32,11 +35,14 @@ savefile = os.path.join(homedir, ".revpipyplc", "programpath.dat")
class RevPiProgram(tkinter.Frame):
def __init__(self, master, xmlcli, xmlmode, revpi):
+ u"""Init RevPiProgram-Class.
+ @return None"""
if xmlmode < 2:
return None
super().__init__(master)
-# master.protocol("WM_DELETE_WINDOW", self._checkclose)
+ self.master.protocol("WM_DELETE_WINDOW", self._checkclose)
+ self.master.bind("", self._checkclose)
self.pack(expand=True, fill="both")
self.uploaded = False
@@ -54,15 +60,22 @@ class RevPiProgram(tkinter.Frame):
self._evt_optdown()
self._evt_optup()
-# def _checkclose(self):
-# if self.uploaded:
-# tkmsg.showinfo("Ein PLC Programm wurde hochgeladen. "
-# "Bitte die PLC options prüfen ob dort das neue Programm"
-# "eingestellt werden muss.")
-# self.master.destroy()
+ def _checkclose(self, event=None):
+ u"""Prüft ob Fenster beendet werden soll.
+ @param event tkinter-Event"""
+ if self.uploaded:
+ tkmsg.showinfo(
+ _("Information"),
+ _("A PLC program has been uploaded. Please check the "
+ "PLC options to see if the correct program is specified "
+ "as the start program."),
+ parent=self.master
+ )
+ self.master.destroy()
def _createwidgets(self):
- self.master.wm_title("RevPi Python PLC Programm")
+ u"""Erstellt alle Widgets."""
+ self.master.wm_title(_("RevPi Python PLC program"))
self.master.wm_resizable(width=False, height=False)
self.rowconfigure(0, weight=1)
@@ -76,7 +89,7 @@ class RevPiProgram(tkinter.Frame):
# Gruppe Programm
prog = tkinter.LabelFrame(self)
prog.columnconfigure(0, weight=1)
- prog["text"] = "PLC Python programm"
+ prog["text"] = _("PLC python program")
prog.grid(columnspan=2, pady=2, sticky="we")
# Variablen vorbereiten
@@ -86,16 +99,26 @@ class RevPiProgram(tkinter.Frame):
self.var_typedown = tkinter.StringVar(prog)
self.var_typeup = tkinter.StringVar(prog)
- self.lst_typedown = ["Dateien", "Zip Archiv", "TGZ Archiv"]
- self.lst_typeup = ["Dateien", "Ordner", "Zip Archiv", "TGZ Archiv"]
+ self.lst_typedown = [_("Files"), _("Zip archive"), _("TGZ archive")]
+ self.lst_typeup = [
+ _("Files"), _("Folder"), _("Zip archive"), _("TGZ archive")
+ ]
self.var_picdown.set(self.opt.get("picdown", False))
self.var_picup.set(self.opt.get("picup", False))
- self.var_typedown.set(self.opt.get("typedown", self.lst_typedown[0]))
- self.var_typeup.set(self.opt.get("typeup", self.lst_typeup[0]))
+
+ # Gespeicherte Werte übernehmen
+ saved_val = self.opt.get("typedown", self.lst_typedown[0])
+ self.var_typedown.set(
+ saved_val if saved_val in self.lst_typedown else _("Files")
+ )
+ saved_val = self.opt.get("typeup", self.lst_typeup[0])
+ self.var_typeup.set(
+ saved_val if saved_val in self.lst_typeup else _("Files")
+ )
r = 0
lbl = tkinter.Label(prog)
- lbl["text"] = "PLC Programm herunterladen als:"
+ lbl["text"] = _("Download PLC program as:")
lbl.grid(column=0, row=r, **cpadw)
opt = tkinter.OptionMenu(
prog, self.var_typedown, *self.lst_typedown,
@@ -105,17 +128,17 @@ class RevPiProgram(tkinter.Frame):
r = 1
self.ckb_picdown = tkinter.Checkbutton(prog)
- self.ckb_picdown["text"] = "inkl. piCtory Konfiguration"
+ self.ckb_picdown["text"] = _("include piCtory configuration")
self.ckb_picdown["variable"] = self.var_picdown
self.ckb_picdown.grid(column=0, row=r, **cpadw)
btn = tkinter.Button(prog)
btn["command"] = self.plcdownload
- btn["text"] = "Download"
+ btn["text"] = _("Download")
btn.grid(column=1, row=r, **cpad)
r = 2
lbl = tkinter.Label(prog)
- lbl["text"] = "PLC Programm hochladen als:"
+ lbl["text"] = _("Upload PLC program as:")
lbl.grid(column=0, row=r, **cpadw)
opt = tkinter.OptionMenu(
prog, self.var_typeup, *self.lst_typeup,
@@ -127,77 +150,78 @@ class RevPiProgram(tkinter.Frame):
r = 3
ckb = tkinter.Checkbutton(prog)
ckb["state"] = self.xmlstate
- ckb["text"] = "vorher alles im Uploadverzeichnis löschen"
+ ckb["text"] = _("clean upload folder before upload")
ckb["variable"] = self.var_cleanup
ckb.grid(column=0, row=r, columnspan=2, **cpadw)
r = 4
self.ckb_picup = tkinter.Checkbutton(prog)
self.ckb_picup["state"] = self.xmlstate
- self.ckb_picup["text"] = "enthält piCtory Konfiguration"
+ self.ckb_picup["text"] = _("includes piCtory configuration")
self.ckb_picup["variable"] = self.var_picup
self.ckb_picup.grid(column=0, row=r, **cpadw)
btn = tkinter.Button(prog)
btn["command"] = self.plcupload
btn["state"] = self.xmlstate
- btn["text"] = "Upload"
+ btn["text"] = _("Upload")
btn.grid(column=1, row=r, **cpad)
# Gruppe piCtory
picto = tkinter.LabelFrame(self)
picto.columnconfigure(0, weight=1)
- picto["text"] = "piCtory Konfiguration"
+ picto["text"] = _("piCtory configuration")
picto.grid(columnspan=2, pady=2, sticky="we")
lbl = tkinter.Label(picto)
- lbl["text"] = "piCtory Konfiguration herunterladen"
+ lbl["text"] = _("Download piCtory configuration")
lbl.grid(column=0, row=0, **cpadw)
btn = tkinter.Button(picto)
btn["command"] = self.getpictoryrsc
- btn["text"] = "Download"
+ btn["text"] = _("Download")
btn.grid(column=1, row=0, **cpad)
lbl = tkinter.Label(picto)
- lbl["text"] = "piCtory Konfiguration hochladen"
+ lbl["text"] = _("Upload piCtory configuration")
lbl.grid(column=0, row=1, **cpadw)
btn = tkinter.Button(picto)
btn["command"] = self.setpictoryrsc
btn["state"] = self.xmlstate
- btn["text"] = "Upload"
+ btn["text"] = _("Upload")
btn.grid(column=1, row=1, **cpad)
# Gruppe ProcImg
proc = tkinter.LabelFrame(self)
proc.columnconfigure(0, weight=1)
- proc["text"] = "piControl0 Prozessabbild"
+ proc["text"] = _("piControl0 process image")
proc.grid(columnspan=2, pady=2, sticky="we")
lbl = tkinter.Label(proc)
- lbl["text"] = "Prozessabbild-Dump herunterladen"
+ lbl["text"] = _("Download process image dump")
lbl.grid(column=0, row=0, **cpadw)
btn = tkinter.Button(proc)
btn["command"] = self.getprocimg
- btn["text"] = "Download"
+ btn["text"] = _("Download")
btn.grid(column=1, row=0, **cpad)
# Gruppe piControlReset
picon = tkinter.LabelFrame(self)
picon.columnconfigure(0, weight=1)
- picon["text"] = "piControl Reset"
+ picon["text"] = _("Reset piControl")
picon.grid(columnspan=2, pady=2, sticky="we")
lbl = tkinter.Label(picon)
- lbl["text"] = "piControlReset ausführen"
+ lbl["text"] = _("Execute piControlReset")
lbl.grid(column=0, row=0, **cpadw)
btn = tkinter.Button(picon)
btn["command"] = self.picontrolreset
- btn["text"] = "ausführen"
+ btn["text"] = _("execute")
btn.grid(column=1, row=0, **cpad)
# Beendenbutton
btn = tkinter.Button(self)
- btn["command"] = self.master.destroy
- btn["text"] = "Beenden"
+ btn["command"] = self._checkclose
+ btn["text"] = _("Exit")
btn.grid()
def _evt_optdown(self, text=""):
+ u"""Passt je nach gewählter Option den Status der Widgets an."""
if self.lst_typedown.index(self.var_typedown.get()) == 0:
self.var_picdown.set(False)
self.ckb_picdown["state"] = "disable"
@@ -205,6 +229,7 @@ class RevPiProgram(tkinter.Frame):
self.ckb_picdown["state"] = "normal"
def _evt_optup(self, text=""):
+ u"""Passt je nach gewählter Option den Status der Widgets an."""
if self.lst_typeup.index(self.var_typeup.get()) <= 1:
self.var_picup.set(False)
self.ckb_picup["state"] = "disable"
@@ -212,7 +237,9 @@ class RevPiProgram(tkinter.Frame):
self.ckb_picup["state"] = "normal"
def _loaddefault(self, full=False):
- """Uebernimmt fuer den Pi die letzen Pfade."""
+ u"""Übernimmt für den Pi die letzen Pfade.
+ @param full Einstellungen für alle Verbindungen laden
+ @return dict() mit Einstellungen"""
if os.path.exists(savefile):
fh = open(savefile, "rb")
dict_all = pickle.load(fh)
@@ -223,7 +250,8 @@ class RevPiProgram(tkinter.Frame):
return {}
def _savedefaults(self):
- """Schreibt fuer den Pi die letzen Pfade."""
+ u"""Schreibt fuer den Pi die letzen Pfade.
+ @return True, bei erfolgreicher Verarbeitung"""
try:
makedirs(os.path.dirname(savefile), exist_ok=True)
dict_all = self._loaddefault(full=True)
@@ -236,9 +264,9 @@ class RevPiProgram(tkinter.Frame):
return True
def create_filelist(self, rootdir):
- """Erstellt eine Dateiliste von einem Verzeichnis.
- @param rootdir: Verzeichnis fuer das eine Liste erstellt werden soll
- @returns: Dateiliste"""
+ u"""Erstellt eine Dateiliste von einem Verzeichnis.
+ @param rootdir Verzeichnis fuer das eine Liste erstellt werden soll
+ @return Dateiliste"""
filelist = []
for tup_dir in os.walk(rootdir):
for fname in tup_dir[2]:
@@ -249,9 +277,9 @@ class RevPiProgram(tkinter.Frame):
"""Gibt das rootdir von einem entpackten Verzeichnis zurueck.
Dabei wird geprueft, ob es sich um einen einzelnen Ordner handelt
- und ob es eine piCtory Konfiguraiton im rootdir gibt.
- @param rootdir: Verzeichnis fuer Pruefung
- @returns: Abgeaendertes rootdir
+ und ob es eine piCtory Konfiguration im rootdir gibt.
+ @param rootdir Verzeichnis fuer Pruefung
+ @return Abgeaendertes rootdir
"""
lst_dir = os.listdir(rootdir)
@@ -270,28 +298,29 @@ class RevPiProgram(tkinter.Frame):
return (rootdir, None)
def getpictoryrsc(self):
+ u"""Läd die piCtory Konfiguration herunter."""
fh = tkfd.asksaveasfile(
mode="wb", parent=self.master,
confirmoverwrite=True,
- title="Speichern als...",
+ title=_("Save as..."),
initialdir=self.opt.get("getpictoryrsc_dir", ""),
initialfile=self.revpi + ".rsc",
- filetypes=(("piCtory Config", "*.rsc"), ("All Files", "*.*"))
+ filetypes=((_("piCtory config"), "*.rsc"), (_("All files"), "*.*"))
)
if fh is not None:
try:
fh.write(self.xmlcli.get_pictoryrsc().data)
except:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Datei konnte nicht geladen und gespeichert "
- "werden!"
+ _("Error"),
+ _("Could not load and save file!"),
+ parent=self.master,
)
else:
tkmsg.showinfo(
- parent=self.master, title="Erfolgreich",
- message="Datei erfolgreich vom Revolution Pi geladen "
- "und gespeichert.",
+ _("Success"),
+ _("File successfully loaded and saved."),
+ parent=self.master
)
# Einstellungen speichern
self.opt["getpictoryrsc_dir"] = os.path.dirname(fh.name)
@@ -300,28 +329,29 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def getprocimg(self):
+ u"""Läd das aktuelle Prozessabbild herunter."""
fh = tkfd.asksaveasfile(
mode="wb", parent=self.master,
confirmoverwrite=True,
- title="Speichern als...",
+ title=_("Save as..."),
initialdir=self.opt.get("getprocimg_dir", ""),
initialfile=self.revpi + ".img",
- filetypes=(("Imagefiles", "*.img"), ("All Files", "*.*"))
+ filetypes=((_("Imagefiles"), "*.img"), (_("All files"), "*.*"))
)
if fh is not None:
try:
fh.write(self.xmlcli.get_procimg().data)
except:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Datei konnte nicht geladen und gespeichert"
- "werden!"
+ _("Error"),
+ _("Could not load and save file!"),
+ parent=self.master
)
else:
tkmsg.showinfo(
- parent=self.master, title="Erfolgreich",
- message="Datei erfolgreich vom Revolution Pi geladen "
- "und gespeichert.",
+ _("Success"),
+ _("File successfully loaded and saved."),
+ parent=self.master
)
# Einstellungen speichern
self.opt["getprocimg_dir"] = os.path.dirname(fh.name)
@@ -330,77 +360,118 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def setpictoryrsc(self, filename=None):
+ u"""Überträgt die angegebene piCtory-Konfiguration."""
if filename is None:
fh = tkfd.askopenfile(
mode="rb", parent=self.master,
- title="piCtory Datei öffnen...",
+ title=_("Open piCtory file..."),
initialdir=self.opt.get("setpictoryrsc_dir", ""),
initialfile=self.revpi + ".rsc",
- filetypes=(("piCtory Config", "*.rsc"), ("All Files", "*.*"))
+ filetypes=(
+ (_("piCtory config"), "*.rsc"), (_("All files"), "*.*")
+ )
)
else:
fh = open(filename, "rb")
if fh is not None:
ask = tkmsg.askyesno(
- parent=self.master, title="Frage",
- message="Soll nach dem Hochladen der piCtory Konfiguration "
- "ein Reset am piControl Treiber durchgeführt werden?"
+ _("Question"),
+ _("Should the piControl driver be reset after "
+ "uploading the piCtory configuration?"),
+ parent=self.master
)
ec = self.xmlcli.set_pictoryrsc(Binary(fh.read()), ask)
- print(ec)
+
if ec == 0:
if ask:
tkmsg.showinfo(
- parent=self.master, title="Erfolgreich",
- message="Die Übertragung der piCtory Konfiguration "
- "und der Reset von piControl wurden erfolgreich "
- "ausgeführt")
+ _("Success"),
+ _("The transfer of the piCtory configuration "
+ "and the reset of piControl have been "
+ "successfully executed."),
+ parent=self.master
+ )
else:
tkmsg.showinfo(
- parent=self.master, title="Erfolgreich",
- message="Die Übertragung der piCtory Konfiguration "
- "wurde erfolgreich ausgeführt")
+ _("Success"),
+ _("The piCtory configuration was "
+ "successfully transferred."),
+ parent=self.master
+ )
- #Einstellungen speichern
+ # Einstellungen speichern
self.opt["setpictoryrsc_dir"] = os.path.dirname(fh.name)
self._savedefaults()
+ elif ec == -1:
+ tkmsg.showerror(
+ _("Error"),
+ _("Can not process the transferred file."),
+ parent=self.master
+ )
+ elif ec == -2:
+ tkmsg.showerror(
+ _("Error"),
+ _("Can not find main elements in piCtory file."),
+ parent=self.master
+ )
+ elif ec == -4:
+ tkmsg.showerror(
+ _("Error"),
+ _("Contained devices could not be found on Revolution "
+ "Pi. The configuration may be from a newer piCtory "
+ "version!"),
+ parent=self.master
+ )
+ elif ec == -5:
+ tkmsg.showerror(
+ _("Error"),
+ _("Could not load RAP catalog on Revolution Pi."),
+ parent=self.master
+ )
elif ec < 0:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Die piCtory Konfiguration konnte auf dem "
- "Revolution Pi nicht geschrieben werden.")
+ _("Error"),
+ _("The piCtory configuration could not be "
+ "written on the Revolution Pi."),
+ parent=self.master
+ )
elif ec > 0:
tkmsg.showwarning(
- parent=self.master, title="Warnung",
- message="Die piCtroy Konfiguration wurde erfolgreich "
- "gespeichert. \nBeim piControl Reset trat allerdings ein "
- "Fehler auf!")
+ _("Warning"),
+ _("The piCtroy configuration has been saved successfully."
+ " \nAn error occurred on piControl reset!"),
+ parent=self.master
+ )
fh.close()
def picontrolreset(self):
+ u"""Fürt ein Reset der piBridge durch."""
ask = tkmsg.askyesno(
- parent=self.master, title="Frage...",
- message="Soll piControlReset wirklich durchgeführt werden? \n"
- "Das Prozessabbild und die Steuerung werden dann unterbrochen!!!"
+ _("Question"),
+ _("Are you sure to reset piControl? \nThe process image "
+ "and the piBridge are interrupted !!!"),
+ parent=self.master
)
if ask:
ec = self.xmlcli.resetpicontrol()
if ec == 0:
tkmsg.showinfo(
- parent=self.master, title="Erfolgreich",
- message="piControlReset erfolgreich durchgeführt"
+ _("Success"),
+ _("piControl reset executed successfully"),
+ parent=self.master
)
else:
tkmsg.showerror(
- parten=self.master, title="Fehler",
- message="piControlReset konnte nicht erfolgreich "
- "durchgeführt werden"
+ _("Error"),
+ _("piControl reset could not be executed successfully"),
+ parten=self.master
)
def plcdownload(self):
+ u"""Läd das aktuelle Projekt herunter."""
tdown = self.lst_typedown.index(self.var_typedown.get())
fh = None
dirselect = ""
@@ -409,23 +480,25 @@ class RevPiProgram(tkinter.Frame):
# Ordner
dirselect = tkfd.askdirectory(
parent=self.master,
- title="Verzeichnis zum Ablegen",
+ title=_("Directory to save"),
mustexist=False,
initialdir=self.opt.get("plcdownload_dir", self.revpi)
)
if type(dirselect) == str and dirselect != "":
- fh = open(mktemp(), "wb")
+ fh = open(mkstemp()[1], "wb")
elif tdown == 1:
# Zip
fh = tkfd.asksaveasfile(
mode="wb", parent=self.master,
confirmoverwrite=True,
- title="Speichern als...",
+ title=_("Save as..."),
initialdir=self.opt.get("plcdownload_file", ""),
initialfile=self.revpi + ".zip",
- filetypes=(("Zip Archiv", "*.zip"), ("All Files", "*.*"))
+ filetypes=(
+ (_("Zip archive"), "*.zip"), (_("All files"), "*.*")
+ )
)
elif tdown == 2:
@@ -433,19 +506,23 @@ class RevPiProgram(tkinter.Frame):
fh = tkfd.asksaveasfile(
mode="wb", parent=self.master,
confirmoverwrite=True,
- title="Speichern als...",
+ title=_("Save as..."),
initialdir=self.opt.get("plcdownload_file", ""),
initialfile=self.revpi + ".tar.gz",
- filetypes=(("Tar Archiv", "*.tar.gz"), ("All Files", "*.*"))
+ filetypes=(
+ (_("TGZ archive"), "*.tar.gz"), (_("All files"), "*.*")
+ )
)
if fh is not None:
if tdown == 1:
plcfile = self.xmlcli.plcdownload(
- "zip", self.var_picdown.get())
+ "zip", self.var_picdown.get()
+ )
else:
plcfile = self.xmlcli.plcdownload(
- "tar", self.var_picdown.get())
+ "tar", self.var_picdown.get()
+ )
try:
fh.write(plcfile.data)
@@ -456,9 +533,7 @@ class RevPiProgram(tkinter.Frame):
fh_pack = tarfile.open(fh.name)
# Unterverzeichnis streichen
- rootname = ""
for taritem in fh_pack.getmembers():
- print(rootname)
if not taritem.name == "revpipyload":
taritem.name = \
taritem.name.replace("revpipyload/", "")
@@ -474,15 +549,15 @@ class RevPiProgram(tkinter.Frame):
except:
raise
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Datei konnte nicht geladen und gespeichert "
- "werden!"
+ _("Error"),
+ _("Could not load and save file!"),
+ parent=self.master
)
else:
tkmsg.showinfo(
- parent=self.master, title="Erfolgreich",
- message="Datei erfolgreich vom Revolution Pi geladen "
- "und gespeichert.",
+ _("Success"),
+ _("File successfully loaded and saved."),
+ parent=self.master
)
# Einstellungen speichern
@@ -491,6 +566,8 @@ class RevPiProgram(tkinter.Frame):
fh.close()
def plcupload(self):
+ u"""Lädt das angegebene Projekt auf den RevPi.
+ @return True, bei erfolgreicher Verarbeitung"""
tup = self.lst_typeup.index(self.var_typeup.get())
dirselect = ""
dirtmp = None
@@ -502,9 +579,9 @@ class RevPiProgram(tkinter.Frame):
# Datei
fileselect = tkfd.askopenfilenames(
parent=self.master,
- title="Python Programm übertragen...",
+ title="Upload Python program...",
initialdir=self.opt.get("plcupload_dir", ""),
- filetypes=(("Python", "*.py"), ("All Files", "*.*"))
+ filetypes=(("Python", "*.py"), (_("All files"), "*.*"))
)
if type(fileselect) == tuple and len(fileselect) > 0:
for file in fileselect:
@@ -514,7 +591,7 @@ class RevPiProgram(tkinter.Frame):
# Ordner
dirselect = tkfd.askdirectory(
parent=self.master,
- title="Verzeichnis zum Hochladen",
+ title=_("Folder to upload"),
mustexist=True,
initialdir=self.opt.get("plcupload_dir", self.revpi)
)
@@ -525,10 +602,12 @@ class RevPiProgram(tkinter.Frame):
# Zip
fileselect = tkfd.askopenfilename(
parent=self.master,
- title="Zip-Archive übertragen...",
+ title=_("Upload Zip archive..."),
initialdir=self.opt.get("plcupload_file", ""),
initialfile=self.revpi + ".zip",
- filetypes=(("Zip Archiv", "*.zip"), ("All Files", "*.*"))
+ filetypes=(
+ (_("Zip archive"), "*.zip"), (_("All files"), "*.*")
+ )
)
if type(fileselect) == str and fileselect != "":
# Zipdatei prüfen
@@ -543,18 +622,22 @@ class RevPiProgram(tkinter.Frame):
else:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Die angegebene Datei ist kein ZIP-Archiv.")
+ _("Error"),
+ _("The specified file is not a ZIP archive."),
+ parent=self.master
+ )
return False
elif tup == 3:
# TarGz
fileselect = tkfd.askopenfilename(
parent=self.master,
- title="TarGz-Archiv übertragen...",
+ title=_("Upload TarGz archiv..."),
initialdir=self.opt.get("plcupload_file", ""),
initialfile=self.revpi + ".tar.gz",
- filetypes=(("Tar Archiv", "*.tar.gz"), ("All Files", "*.*"))
+ filetypes=(
+ (_("TGZ archive"), "*.tar.gz"), (_("All files"), "*.*")
+ )
)
if type(fileselect) == str and fileselect != "":
@@ -570,8 +653,10 @@ class RevPiProgram(tkinter.Frame):
else:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Die angegebene Datei ist kein TAR-Archiv.")
+ _("Error"),
+ _("The specified file is not a TAR archive."),
+ parent=self.master
+ )
return False
# Wenn keine Dateien gewählt
@@ -581,12 +666,16 @@ class RevPiProgram(tkinter.Frame):
# Vor Übertragung aufräumen wenn ausgewählt
if self.var_cleanup.get() and not self.xmlcli.plcuploadclean():
tkmsg.showerror(
- parent=self.masger, title="Fehler",
- message="Beim Löschen der Dateien auf dem Revolution Pi ist "
- "ein Fehler aufgetreten.")
+ _("Error"),
+ _("There was an error deleting the files on the "
+ "Revolution Pi."),
+ parent=self.master
+ )
return False
- # Flag setzen, weil ab hier Veränderungen existieren
+ # Aktuell konfiguriertes Programm lesen (für uploaded Flag)
+ opt_program = self.xmlcli.get_config()
+ opt_program = opt_program.get("plcprogram", "none.py")
self.uploaded = True
ec = 0
@@ -595,7 +684,7 @@ class RevPiProgram(tkinter.Frame):
if fname == rscfile:
continue
- # TODO: Fehlerabfang bei Dateilesen
+ # FIXME: Fehlerabfang bei Dateilesen
with open(fname, "rb") as fh:
# Dateinamen ermitteln
@@ -604,6 +693,10 @@ class RevPiProgram(tkinter.Frame):
else:
sendname = fname.replace(dirselect, "")[1:]
+ # Prüfen ob Dateiname bereits als Startprogramm angegeben ist
+ if sendname == opt_program:
+ self.uploaded = False
+
# Datei übertragen
try:
ustatus = self.xmlcli.plcupload(
@@ -618,17 +711,21 @@ class RevPiProgram(tkinter.Frame):
if ec == 0:
tkmsg.showinfo(
- parent=self.master, title="Erfolgreich",
- message="Die Übertragung war erfolgreich.")
+ _("Success"),
+ _("The PLC program was transferred successfully."),
+ parent=self.master
+ )
if self.var_picup.get():
if rscfile is not None:
self.setpictoryrsc(rscfile)
else:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Es wurde im Archiv keine piCtory "
- "Konfiguration gefunden")
+ _("Error"),
+ _("There is no piCtory configuration in this "
+ "archive."),
+ parent=self.master
+ )
# Einstellungen speichern
if tup == 0:
@@ -644,21 +741,21 @@ class RevPiProgram(tkinter.Frame):
elif ec == -1:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Der Revoluton Pi konnte Teile der Übertragung nicht "
- "verarbeiten.")
+ _("Error"),
+ _("The Revolution Pi could not process some parts of the "
+ "transmission."),
+ parent=self.master
+ )
elif ec == -2:
tkmsg.showerror(
- parent=self.master, title="Fehler",
- message="Bei der Übertragung traten Fehler auf")
+ _("Error"),
+ _("Errors occurred during transmission"),
+ parent=self.master
+ )
# Temp-Dir aufräumen
if dirtmp is not None:
rmtree(dirtmp)
-
-if __name__ == "__main__":
- root = tkinter.Tk()
- myapp = RevPiProgram(root, None, "test")
- root.mainloop()
+ return True
diff --git a/revpipycontrol/revpipycontrol.py b/revpipycontrol/revpipycontrol.py
index 4f518d6..0dd4773 100755
--- a/revpipycontrol/revpipycontrol.py
+++ b/revpipycontrol/revpipycontrol.py
@@ -1,55 +1,52 @@
#!/usr/bin/python3
#
# RevPiPyControl
-# Version: 0.2.12
+# Version: see global var pycontrolverion
#
# Webpage: https://revpimodio.org/revpipyplc/
# (c) Sven Sager, License: LGPLv3
#
# -*- coding: utf-8 -*-
+import revpicheckclient
+import revpiinfo
import revpilogfile
import revpioption
import revpiplclist
import revpiprogram
import socket
-import sys
import tkinter
import tkinter.messagebox as tkmsg
-from functools import partial
-from os.path import dirname
-from os.path import join as pathjoin
+import webbrowser
+from mytools import addroot, gettrans
from xmlrpc.client import ServerProxy
+# Übersetzung laden
+_ = gettrans()
-def addroot(filename):
- u"""Hängt root-dir der Anwendung vor Dateinamen.
-
- Je nach Ausführungsart der Anwendung muss das root-dir über
- andere Arten abgerufen werden.
-
- @param filename: Datei oder Ordnername
- @returns: root dir
-
- """
- if getattr(sys, "frozen", False):
- return pathjoin(dirname(sys.executable), filename)
- else:
- return pathjoin(dirname(__file__), filename)
+pycontrolversion = "0.4.1"
class RevPiPyControl(tkinter.Frame):
def __init__(self, master=None):
+ u"""Init RevPiPyControl-Class.
+ @param master tkinter master"""
super().__init__(master)
+ self.master.protocol("WM_DELETE_WINDOW", self._closeapp)
self.pack(fill="both", expand=True)
self.cli = None
self.dict_conn = revpiplclist.get_connections()
self.errcount = 0
self.revpiname = None
+ self.xmlfuncs = []
self.xmlmode = 0
+ # Debugger vorbereiten
+ self.debugframe = None
+
# Globale Fenster
+ self.tkcheckclient = None
self.tklogs = None
self.tkoptions = None
self.tkprogram = None
@@ -61,14 +58,38 @@ class RevPiPyControl(tkinter.Frame):
self.tmr_plcrunning()
def _btnstate(self):
+ u"""Setzt den state der Buttons."""
stateval = "disabled" if self.cli is None else "normal"
self.btn_plclogs["state"] = stateval
self.btn_plcstart["state"] = stateval
self.btn_plcstop["state"] = stateval
self.btn_plcrestart["state"] = stateval
+ self.btn_debug["state"] = stateval
+
+ def _closeall(self):
+ u"""Schließt alle Fenster."""
+ if self.tkcheckclient is not None:
+ self.tkcheckclient.destroy()
+ if self.tklogs is not None:
+ self.tklogs.master.destroy()
+ if self.tkoptions is not None:
+ self.tkoptions.destroy()
+ if self.tkprogram is not None:
+ self.tkprogram.destroy()
+ if self.debugframe is not None:
+ self.plcdebug()
+ self.debugframe.destroy()
+ self.cli.psstop()
+ self.debugframe = None
+
+ def _closeapp(self, event=None):
+ u"""Räumt auf und beendet Programm.
+ @param event tkinter Event"""
+ self._closeall()
+ self.master.destroy()
def _createwidgets(self):
- """Erstellt den Fensterinhalt."""
+ u"""Erstellt den Fensterinhalt."""
# Hauptfenster
self.master.wm_title("RevPi Python PLC Loader")
self.master.wm_iconphoto(
@@ -81,36 +102,44 @@ class RevPiPyControl(tkinter.Frame):
self.master.config(menu=self.mbar)
menu1 = tkinter.Menu(self.mbar, tearoff=False)
- menu1.add_command(label="Connections...", command=self.plclist)
+ menu1.add_command(label=_("Connections..."), command=self.plclist)
menu1.add_separator()
- menu1.add_command(label="Exit", command=self.master.destroy)
- self.mbar.add_cascade(label="Main", menu=menu1)
+ menu1.add_command(label=_("Exit"), command=self.master.destroy)
+ self.mbar.add_cascade(label=_("Main"), menu=menu1)
self._fillmbar()
self._fillconnbar()
+ # Hilfe Menü
+ menu1 = tkinter.Menu(self.mbar, tearoff=False)
+ menu1.add_command(
+ label=_("Visit website..."), command=self.visitwebsite)
+ menu1.add_separator()
+ menu1.add_command(label=_("Info..."), command=self.infowindow)
+ self.mbar.add_cascade(label=_("Help"), menu=menu1)
+
self.var_conn = tkinter.StringVar(self)
- self.txt_connect = tkinter.Entry(
- self, textvariable=self.var_conn, state="readonly", width=30)
+ self.txt_connect = tkinter.Entry(self, state="readonly", width=40)
+ self.txt_connect["textvariable"] = self.var_conn
self.txt_connect.pack(fill="x")
self.btn_plcstart = tkinter.Button(self)
- self.btn_plcstart["text"] = "PLC Start"
+ self.btn_plcstart["text"] = _("PLC start")
self.btn_plcstart["command"] = self.plcstart
self.btn_plcstart.pack(fill="x")
self.btn_plcstop = tkinter.Button(self)
- self.btn_plcstop["text"] = "PLC Stop"
+ self.btn_plcstop["text"] = _("PLC stop")
self.btn_plcstop["command"] = self.plcstop
self.btn_plcstop.pack(fill="x")
self.btn_plcrestart = tkinter.Button(self)
- self.btn_plcrestart["text"] = "PLC Restart"
+ self.btn_plcrestart["text"] = _("PLC restart")
self.btn_plcrestart["command"] = self.plcrestart
self.btn_plcrestart.pack(fill="x")
self.btn_plclogs = tkinter.Button(self)
- self.btn_plclogs["text"] = "PLC Logs"
+ self.btn_plclogs["text"] = _("PLC logs")
self.btn_plclogs["command"] = self.plclogs
self.btn_plclogs.pack(fill="x")
@@ -120,28 +149,48 @@ class RevPiPyControl(tkinter.Frame):
self.txt_status["textvariable"] = self.var_status
self.txt_status.pack(fill="x")
+ self.btn_debug = tkinter.Button(self)
+ self.btn_debug["text"] = _("PLC watch mode")
+ self.btn_debug["command"] = self.plcdebug
+ self.btn_debug.pack(fill="x")
+
+ def _fillconnbar(self):
+ u"""Generiert Menüeinträge für Verbindungen."""
+ self.mconn.delete(0, "end")
+ for con in sorted(self.dict_conn.keys(), key=lambda x: x.lower()):
+ self.mconn.add_command(
+ label=con, command=lambda con=con: self._opt_conn(con)
+ )
+
def _fillmbar(self):
+ u"""Generiert Menüeinträge."""
# PLC Menü
self.mplc = tkinter.Menu(self.mbar, tearoff=False)
- self.mplc.add_command(label="PLC log...", command=self.plclogs)
- #self.mplc.add_command(label="PLC monitor...", command=self.plcmonitor)
- self.mplc.add_command(label="PLC options...", command=self.plcoptions)
- self.mplc.add_command(label="PLC program...", command=self.plcprogram)
+ self.mplc.add_command(
+ label=_("PLC log..."), command=self.plclogs)
+ self.mplc.add_command(
+ label=_("PLC options..."), command=self.plcoptions)
+ self.mplc.add_command(
+ label=_("PLC program..."), command=self.plcprogram)
+ self.mplc.add_separator()
+
+ self.mplc.add_command(
+ label=_("Disconnect"), command=self.serverdisconnect)
self.mbar.add_cascade(label="PLC", menu=self.mplc, state="disabled")
# Connection Menü
self.mconn = tkinter.Menu(self.mbar, tearoff=False)
- self.mbar.add_cascade(label="Connect", menu=self.mconn)
+ self.mbar.add_cascade(label=_("Connect"), menu=self.mconn)
- def _fillconnbar(self):
- self.mconn.delete(0, "end")
- for con in sorted(self.dict_conn.keys(), key=lambda x: x.lower()):
- self.mconn.add_command(
- label=con, command=partial(self._opt_conn, con)
- )
+ def _opt_conn(self, text, reconnect=False):
+ u"""Stellt eine neue Verbindung zu RevPiPyLoad her.
+ @param text Verbindungsname
+ @param reconnect Socket Timeout nicht heruntersetzen"""
+ if reconnect:
+ socket.setdefaulttimeout(15)
+ else:
+ socket.setdefaulttimeout(2)
- def _opt_conn(self, text):
- socket.setdefaulttimeout(2)
sp = ServerProxy(
"http://{}:{}".format(
self.dict_conn[text][0], int(self.dict_conn[text][1])
@@ -149,6 +198,7 @@ class RevPiPyControl(tkinter.Frame):
)
# Server prüfen
try:
+ self.xmlfuncs = sp.system.listMethods()
self.xmlmode = sp.xmlmodus()
except:
self.servererror()
@@ -166,86 +216,157 @@ class RevPiPyControl(tkinter.Frame):
))
self.mbar.entryconfig("PLC", state="normal")
- def _closeall(self):
- if self.tklogs is not None:
- self.tklogs.master.destroy()
- if self.tkoptions is not None:
- self.tkoptions.destroy()
- if self.tkprogram is not None:
- self.tkprogram.destroy()
-
- def plclist(self):
+ def infowindow(self):
+ u"""Öffnet das Fenster für die Info."""
win = tkinter.Toplevel(self)
- revpiplclist.RevPiPlcList(win)
win.focus_set()
win.grab_set()
+ revpiinfo.RevPiInfo(win, self.cli, pycontrolversion)
+ self.wait_window(win)
+ self.dict_conn = revpiplclist.get_connections()
+ self._fillconnbar()
+
+ def plcdebug(self):
+ u"""Baut den Debugframe und packt ihn."""
+ self.btn_debug["state"] = "disabled"
+
+ if "psstart" not in self.xmlfuncs:
+ tkmsg.showwarning(
+ _("Warning"),
+ _("The watch mode ist not supported in version {} "
+ "of RevPiPyLoad on your RevPi! You need at least version "
+ "0.4.0. Or the python3-revpimodio module is not installt"
+ "on your RevPi at least version 0.15.0."
+ "").format(self.cli.version()),
+ parent=self.master
+ )
+ else:
+ # Debugfenster laden
+ if self.debugframe is None \
+ or self.debugframe.err_workvalues >= \
+ self.debugframe.max_errors:
+ self.debugframe = revpicheckclient.RevPiCheckClient(
+ self, self.cli, self.xmlmode
+ )
+
+ # Show/Hide wechseln
+ if self.debugframe.winfo_viewable():
+ self.debugframe.hideallwindows()
+ if self.debugframe.autorw.get():
+ self.debugframe.autorw.set(False)
+ self.debugframe.toggleauto()
+ self.debugframe.dowrite.set(False)
+ self.debugframe.pack_forget()
+ else:
+ self.debugframe.pack(fill="x")
+
+ self.btn_debug["state"] = "normal"
+
+ def plclist(self):
+ u"""Öffnet das Fenster für die Verbindungen."""
+ win = tkinter.Toplevel(self)
+ win.focus_set()
+ win.grab_set()
+ revpiplclist.RevPiPlcList(win)
self.wait_window(win)
self.dict_conn = revpiplclist.get_connections()
self._fillconnbar()
def plclogs(self):
+ u"""Öffnet das Fenster für Logdateien.
+ @return None"""
+ if "load_plclog" not in self.xmlfuncs:
+ tkmsg.showwarning(
+ _("Warning"),
+ _("This version of Logviewer ist not supported in version {} "
+ "of RevPiPyLoad on your RevPi! You need at least version "
+ "0.4.1.").format(self.cli.version()),
+ parent=self.master
+ )
+ return None
+
if self.tklogs is None or len(self.tklogs.children) == 0:
win = tkinter.Toplevel(self)
self.tklogs = revpilogfile.RevPiLogfile(win, self.cli)
else:
self.tklogs.focus_set()
- def plcmonitor(self):
- # TODO: Monitorfenster
- pass
-
def plcoptions(self):
+ u"""Startet das Optionsfenster."""
if self.xmlmode < 2:
tkmsg.showwarning(
- parent=self.master, title="Warnung",
- message="Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch "
- "genug eingestellt, um diesen Dialog zu verwenden!"
+ _("Warning"),
+ _("XML-RPC access mode in the RevPiPyLoad "
+ "configuration is too small to access this dialog!"),
+ parent=self.master
)
else:
win = tkinter.Toplevel(self)
- self.tkoptions = \
- revpioption.RevPiOption(win, self.cli, self.xmlmode)
win.focus_set()
win.grab_set()
+ self.tkoptions = \
+ revpioption.RevPiOption(win, self.cli)
self.wait_window(win)
- self.xmlmode = self.tkoptions.xmlmode
+ if self.tkoptions.dc is not None and self.tkoptions.dorestart:
+
+ # Wenn XML-Modus anders und Dienstneustart
+ if self.xmlmode != self.tkoptions.dc["xmlrpc"]:
+ self.serverdisconnect()
+ self._opt_conn(self.revpiname, True)
+
+ if self.debugframe is not None:
+ self.cli.psstart()
def plcprogram(self):
+ u"""Startet das Programmfenster."""
if self.xmlmode < 2:
tkmsg.showwarning(
- parent=self.master, title="Warnung",
- message="Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch "
- "genug eingestellt, um diesen Dialog zu verwenden!"
+ _("Warning"),
+ _("XML-RPC access mode in the RevPiPyLoad "
+ "configuration is too small to access this dialog!"),
+ parent=self.master
)
else:
win = tkinter.Toplevel(self)
- self.tkprogram = revpiprogram.RevPiProgram(
- win, self.cli, self.xmlmode, self.revpiname)
win.focus_set()
win.grab_set()
+ self.tkprogram = revpiprogram.RevPiProgram(
+ win, self.cli, self.xmlmode, self.revpiname)
self.wait_window(win)
def plcstart(self):
+ u"""Startet das PLC Programm."""
self.cli.plcstart()
def plcstop(self):
+ u"""Beendet das PLC Programm."""
self.cli.plcstop()
def plcrestart(self):
+ u"""Startet das PLC Programm neu."""
self.cli.plcstop()
self.cli.plcstart()
- def servererror(self):
- """Setzt alles auf NULL."""
+ def serverdisconnect(self):
+ u"""Trennt eine bestehende Verbindung."""
+ self._closeall()
socket.setdefaulttimeout(2)
self.cli = None
self._btnstate()
self.mbar.entryconfig("PLC", state="disabled")
self.var_conn.set("")
- self._closeall()
- tkmsg.showerror("Fehler", "Server ist nicht erreichbar!")
+
+ def servererror(self):
+ u"""Setzt alles zurück für neue Verbindungen."""
+ self.serverdisconnect()
+ tkmsg.showerror(
+ _("Error"),
+ _("Can not reach server!"),
+ parent=self.master
+ )
def tmr_plcrunning(self):
+ u"""Timer der den Status des PLC Programms prüft."""
self._btnstate()
if self.cli is None:
self.txt_status["readonlybackground"] = "lightblue"
@@ -267,16 +388,22 @@ class RevPiPyControl(tkinter.Frame):
plcec = "RUNNING"
elif plcec == -2:
plcec = "FILE NOT FOUND"
+ elif plcec == -3:
+ plcec = "NOT RUNNING (NO STATUS)"
elif plcec == -9:
plcec = "PROGRAM KILLED"
elif plcec == -15:
- plcec = "PROGRAMS TERMED"
+ plcec = "PROGRAM TERMED"
elif plcec == 0:
plcec = "NOT RUNNING"
self.var_status.set(plcec)
self.master.after(1000, self.tmr_plcrunning)
+ def visitwebsite(self):
+ u"""Öffnet auf dem System einen Webbrowser zur Projektseite."""
+ webbrowser.open("https://revpimodio.org")
+
if __name__ == "__main__":
root = tkinter.Tk()
diff --git a/setup.py b/setup.py
index 0af49cd..65c5ec7 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ globsetup = {
"author_email": "akira@narux.de",
"url": "https://revpimodio.org/revpipyplc/",
"license": "LGPLv3",
- "version": "0.2.12",
+ "version": "0.4.1",
"name": "revpipycontrol",
@@ -82,7 +82,7 @@ elif platform == "win32":
options={"build_exe": {
"include_files": [
"revpipycontrol/revpipycontrol.png",
- # "m4server/locale"
+ "revpipycontrol/locale"
]
}},
executables=[exe],