diff --git a/.hgignore b/.hgignore index fe4cdbb..a10df60 100644 --- a/.hgignore +++ b/.hgignore @@ -6,3 +6,4 @@ revpipycontrol.egg-info/* deb/* .eric6project/* *.directory +*.mo diff --git a/doc/revpicheckclient.html b/doc/revpicheckclient.html index 79c32c2..3e8cd4b 100644 --- a/doc/revpicheckclient.html +++ b/doc/revpicheckclient.html @@ -10,7 +10,7 @@ revpicheckclient

Global Attributes

- +
None
_

Classes

@@ -35,7 +35,7 @@ tkinter.Frame

Class Attributes

- +
cli
tk
None

Class Methods

@@ -49,11 +49,20 @@ 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. @@ -61,20 +70,35 @@ Methods _createwidgets Erstellt den Fensterinhalt. -_readvalues +_onfrmconf +Erstellt Fenster in einem Canvas. + +_warnwrite +Warnung für Benutzer über Schreibfunktion einmal fragen. + +_workvalues Alle Werte der Inputs und Outputs abrufen. -chval - - -onfrmconf - +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. 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.

@@ -88,49 +112,147 @@ 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) - -

-RevPiCheckClient._createiogroup

-_createiogroup(device, frame, iotype)

-Erstellt IO-Gruppen. -

+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._createwidgets

_createwidgets()

Erstellt den Fensterinhalt. -

+

-RevPiCheckClient._readvalues

-_readvalues() +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 +
+

-RevPiCheckClient.chval

-chval(device, io) - -

-RevPiCheckClient.onfrmconf

-onfrmconf(canvas) - +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.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. +

Up

\ No newline at end of file diff --git a/doc/revpilogfile.html b/doc/revpilogfile.html index e97a54a..79fb54b 100644 --- a/doc/revpilogfile.html +++ b/doc/revpilogfile.html @@ -10,7 +10,7 @@ revpilogfile

Global Attributes

- +
None
_

Classes

@@ -47,28 +47,28 @@ Methods + + + - + + + + - + - - - - + - - - - + - +
RevPiLogfileInit RevPiLogfile-Class.
_closewin
_createwidgetsErstellt alle Widgets.
_load_logLäd die angegebenen Logfiles herunter.
btn_clearappLeert die Logliste der App.
btn_clearplc
get_applinesLeert die Logliste des PLC.
get_applog
get_plclinesRuft App Logbuch ab.
get_plclogRuft PLC Logbuch ab.

@@ -80,35 +80,78 @@ Static Methods

RevPiLogfile (Constructor)

RevPiLogfile(master, xmlcli) +

+Init RevPiLogfile-Class. +

+

+RevPiLogfile._closewin

+_closewin(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() - -

-RevPiLogfile.get_applines

-get_applines() - +

+Leert die Logliste des PLC. +

RevPiLogfile.get_applog

-get_applog() - -

-RevPiLogfile.get_plclines

-get_plclines() - +get_applog(full=False) +

+Ruft App Logbuch ab. +

+
full:
+
+Ganzes Logbuch laden +
+

RevPiLogfile.get_plclog

-get_plclog() - +get_plclog(full=False) +

+Ruft PLC Logbuch ab. +

+
full:
+
+Ganzes Logbuch laden +
+
Up

\ No newline at end of file diff --git a/doc/revpioption.html b/doc/revpioption.html index 417e9fd..2a321f8 100644 --- a/doc/revpioption.html +++ b/doc/revpioption.html @@ -10,7 +10,7 @@ revpioption

Global Attributes

- +
None
_

Classes

@@ -47,22 +47,28 @@ Methods - + + + + + + + - + - + - + - + - +
RevPiOptionInit RevPiOption-Class.
_changesdonePrüft ob sich die Einstellungen geändert haben.
_checkclosePrüft ob Fenster beendet werden soll.
_createwidgetsErstellt Widgets.
_loadappdataLäd aktuelle Einstellungen vom RevPi.
_setappdataSpeichert geänderte Einstellungen auf RevPi.
askxmlonFragt Nuter, ob wirklicht abgeschaltet werden soll.
xmlmodsPasst XML-Optionszugriff an.

@@ -74,27 +80,66 @@ Static Methods

RevPiOption (Constructor)

RevPiOption(master, xmlcli, xmlmode) - +

+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() - +

+Läd aktuelle Einstellungen vom RevPi. +

RevPiOption._setappdata

_setappdata() - +

+Speichert geänderte Einstellungen auf RevPi. +

RevPiOption.askxmlon

askxmlon() - +

+Fragt Nuter, ob wirklicht abgeschaltet werden soll. +

RevPiOption.xmlmods

xmlmods() - +

+Passt XML-Optionszugriff an. +

Up

\ No newline at end of file diff --git a/doc/revpiplclist.html b/doc/revpiplclist.html index 9d27221..b7c66e1 100644 --- a/doc/revpiplclist.html +++ b/doc/revpiplclist.html @@ -10,7 +10,7 @@ revpiplclist

Global Attributes

- +
savefile
_
savefile

Classes

@@ -52,6 +52,9 @@ Methods RevPiPlcList +_checkclose + + _createwidgets @@ -67,9 +70,6 @@ Methods evt_btnadd -evt_btnclose - - evt_btnnew @@ -95,6 +95,10 @@ Static Methods

RevPiPlcList (Constructor)

RevPiPlcList(master) + +

+RevPiPlcList._checkclose

+_checkclose(event=None)

RevPiPlcList._createwidgets

@@ -115,10 +119,6 @@ RevPiPlcList.build_listconn

RevPiPlcList.evt_btnadd

evt_btnadd() - -

-RevPiPlcList.evt_btnclose

-evt_btnclose()

RevPiPlcList.evt_btnnew

diff --git a/doc/revpiprogram.html b/doc/revpiprogram.html index a00d6cf..d61ebd0 100644 --- a/doc/revpiprogram.html +++ b/doc/revpiprogram.html @@ -10,7 +10,7 @@ revpiprogram

Global Attributes

- +
savefile
_
savefile

Classes

@@ -35,7 +35,7 @@ tkinter.Frame

Class Attributes

- +
myapp
root
None

Class Methods

@@ -47,6 +47,9 @@ Methods + + + @@ -71,22 +74,22 @@ Methods - + - + - + - + - + - +
RevPiProgramInit RevPiProgram-Class.
_checkclose
_createwidgets Erstellt eine Dateiliste von einem Verzeichnis.
getpictoryrscLäd die piCtory Konfiguration herunter.
getprocimgLäd das aktuelle Prozessabbild herunter.
picontrolresetFürt ein Reset der piBridge durch.
plcdownloadLäd das aktuelle Projekt herunter.
plcuploadLädt das angegebene Projekt auf den RevPi.
setpictoryrscÜberträgt die angegebene piCtory-Konfiguration.

@@ -98,6 +101,12 @@ Static Methods

RevPiProgram (Constructor)

RevPiProgram(master, xmlcli, xmlmode, revpi) +

+Init RevPiProgram-Class. +

+

+RevPiProgram._checkclose

+_checkclose(event=None)

RevPiProgram._createwidgets

@@ -130,7 +139,7 @@ RevPiProgram.check_replacedir 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. + und ob es eine piCtory Konfiguration im rootdir gibt.

rootdir:
@@ -161,27 +170,39 @@ 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. +

RevPiProgram.setpictoryrsc

setpictoryrsc(filename=None) - +

+Überträgt die angegebene piCtory-Konfiguration. +

Up

\ No newline at end of file diff --git a/doc/revpipycontrol.html b/doc/revpipycontrol.html index 11750e5..819a6c4 100644 --- a/doc/revpipycontrol.html +++ b/doc/revpipycontrol.html @@ -10,7 +10,7 @@ revpipycontrol

Global Attributes

- +
None
_

Classes

@@ -23,10 +23,7 @@ Classes

Functions

- - - - +
addrootHängt root-dir der Anwendung vor Dateinamen.
None


@@ -50,55 +47,67 @@ Methods - + - + - + + + + - + - + + + + + + + - + - - - - + - + - + - + - + + + + - + + + +
RevPiPyControlInit RevPiPyControl-Class.
_btnstateSetzt den state der Buttons.
_closeallSchließt alle Fenster.
_closeappRäumt auf und beendet Programm.
_createwidgets Erstellt den Fensterinhalt.
_fillconnbarGeneriert Menüeinträge für Verbindungen.
_fillmbarGeneriert Menüeinträge.
_opt_conn
infowindowÖffnet das Fenster für die Verbindungen.
plcdebugBaut den Debugframe und packt ihn.
plclistÖffnet das Fenster für die Verbindungen.
plclogs
plcmonitorStartet das Monitorfenster.Öffnet das Fenster für Logdateien.
plcoptions Startet das Optionsfenster.
plcprogramStartet das Programmfenster.
plcrestartStartet das PLC Programm neu.
plcstartStartet das PLC Programm.
plcstopBeendet das PLC Programm.
serverdisconnectTrennt eine bestehende Verbindung.
servererrorSetzt alles auf NULL.Setzt alles zurück für neue Verbindungen.
tmr_plcrunning
visitwebsiteÖffnet auf dem System einen Webbrowser zur Projektseite.

@@ -110,15 +119,37 @@ 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() @@ -128,28 +159,42 @@ 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) - + +

+RevPiPyControl.infowindow

+infowindow() +

+Öffnet das Fenster für die Verbindungen. +

+

+RevPiPyControl.plcdebug

+plcdebug() +

+Baut den Debugframe und packt ihn. +

RevPiPyControl.plclist

plclist() - +

+Öffnet das Fenster für die Verbindungen. +

RevPiPyControl.plclogs

plclogs() - -

-RevPiPyControl.plcmonitor

-plcmonitor()

-Startet das Monitorfenster. +Öffnet das Fenster für Logdateien.

RevPiPyControl.plcoptions

@@ -160,50 +205,49 @@ 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 auf NULL. +Setzt alles zurück für neue Verbindungen.

RevPiPyControl.tmr_plcrunning

tmr_plcrunning() - -
Up
-

- -

addroot

-addroot(filename) + +

+RevPiPyControl.visitwebsite

+visitwebsite()

-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 -
-
+Öffnet auf dem System einen Webbrowser zur Projektseite. +

Up

\ No newline at end of file diff --git a/revpipycontrol.api b/revpipycontrol.api index ae08326..91279e4 100644 --- a/revpipycontrol.api +++ b/revpipycontrol.api @@ -1,35 +1,54 @@ +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._readvalues?5() -revpicheckclient.RevPiCheckClient.chval?4(device, io) -revpicheckclient.RevPiCheckClient.cli?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.tk?7 +revpicheckclient.RevPiCheckClient.refreshvalues?4() revpicheckclient.RevPiCheckClient.toggleauto?4() +revpicheckclient.RevPiCheckClient.togglewrite?4() +revpicheckclient.RevPiCheckClient.validatereturn?4(returnlist) +revpicheckclient.RevPiCheckClient.writevalues?4() revpicheckclient.RevPiCheckClient?1(master, xmlcli, xmlmode=0) +revpicheckclient._?8 +revpiinfo.RevPiInfo._closewin?5(event=None) +revpiinfo.RevPiInfo._createwidgets?5() +revpiinfo.RevPiInfo.app?7 +revpiinfo.RevPiInfo.root?7 +revpiinfo.RevPiInfo?1(master) +revpiinfo._?8 +revpilogfile.RevPiLogfile._closewin?5(event) 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._setappdata?5() revpioption.RevPiOption.askxmlon?4() revpioption.RevPiOption.xmlmods?4() revpioption.RevPiOption?1(master, xmlcli, xmlmode) +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,34 @@ 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.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 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 4e778ae..24fdc69 100644 --- a/revpipycontrol.e4p +++ b/revpipycontrol.e4p @@ -1,7 +1,7 @@ - + en_US @@ -22,6 +22,7 @@ revpipycontrol/revpioption.py revpipycontrol/revpiprogram.py revpipycontrol/mytools.py + revpipycontrol/revpiinfo.py diff --git a/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po b/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po index 0fd8feb..521bfb6 100644 --- a/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po +++ b/revpipycontrol/locale/de/LC_MESSAGES/revpipycontrol.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: RevPiPyControl 0.4.0\n" -"POT-Creation-Date: 2017-06-29 20:10+0200\n" -"PO-Revision-Date: 2017-06-29 20:10+0200\n" +"POT-Creation-Date: 2017-06-30 13:50+0200\n" +"PO-Revision-Date: 2017-06-30 13:50+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" @@ -15,34 +15,72 @@ msgstr "" "X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SearchPath-0: .\n" -#: revpicheckclient.py:143 +#: revpicheckclient.py:103 revpicheckclient.py:395 revpioption.py:288 +#: revpiplclist.py:198 revpiprogram.py:306 revpiprogram.py:337 +#: revpiprogram.py:400 revpiprogram.py:433 revpiprogram.py:515 +#: revpiprogram.py:587 revpiprogram.py:618 revpiprogram.py:631 +#: revpiprogram.py:686 revpiprogram.py:706 revpiprogram.py:714 +#: revpipycontrol.py:342 +msgid "Error" +msgstr "Fehler" + +#: revpicheckclient.py:104 +msgid "" +"Given value for Output '{}' is not valid! \n" +"Reset to '{}'" +msgstr "" +"Angegebener Wert bei Output '{}' ist nicht gültig! \n" +"Setze auf '{}' zurück" + +#: revpicheckclient.py:202 msgid "Devices of RevPi" msgstr "Devices vom RevPi" -#: revpicheckclient.py:175 +#: revpicheckclient.py:234 msgid "Control" msgstr "Kontrolle" -#: revpicheckclient.py:179 +#: revpicheckclient.py:238 msgid "Read all IOs" msgstr "IOs aktualisieren" -#: revpicheckclient.py:184 +#: revpicheckclient.py:243 msgid "Read just Inputs" msgstr "Inputs lesen" -#: revpicheckclient.py:189 +#: revpicheckclient.py:248 msgid "Write Outputs" msgstr "Outputs schreiben" -#: revpicheckclient.py:195 +#: revpicheckclient.py:254 msgid "Autorefresh values" msgstr "Aktualisiere automatisch" -#: revpicheckclient.py:201 +#: revpicheckclient.py:262 msgid "Write values to RevPi" msgstr "Schreibe Werte auf RevPi" +#: revpicheckclient.py:276 revpiprogram.py:407 revpipycontrol.py:228 +#: revpipycontrol.py:267 revpipycontrol.py:285 revpipycontrol.py:303 +msgid "Warning" +msgstr "Warnung" + +#: revpicheckclient.py:277 +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:392 +msgid "Error set value of device '{}' Output '{}': {} \n" +msgstr "Fehler beim Setzen auf Device '{}' bei Output '{}': {}\n" + #: revpilogfile.py:37 msgid "RevPi Python PLC Logs" msgstr "RevPi Python PLC Logdaten" @@ -184,14 +222,6 @@ msgstr "Information" msgid "Settings saved" msgstr "Einstellungen gespeichert" -#: revpioption.py:288 revpiplclist.py:198 revpiprogram.py:306 -#: revpiprogram.py:337 revpiprogram.py:400 revpiprogram.py:433 -#: revpiprogram.py:515 revpiprogram.py:587 revpiprogram.py:618 -#: revpiprogram.py:631 revpiprogram.py:686 revpiprogram.py:706 -#: revpiprogram.py:714 revpipycontrol.py:325 -msgid "Error" -msgstr "Fehler" - #: revpioption.py:289 msgid "The settings could not be saved. This can happen if values are wrong!" msgstr "" @@ -345,7 +375,7 @@ msgstr "piControlReset ausführen" msgid "execute" msgstr "ausführen" -#: revpiprogram.py:216 revpipycontrol.py:95 +#: revpiprogram.py:216 revpipycontrol.py:107 msgid "Exit" msgstr "Beenden" @@ -412,11 +442,6 @@ msgstr "" "Die piCtory Konfiguration konnte auf dem Revolution Pi nicht geschrieben " "werden." -#: revpiprogram.py:407 revpipycontrol.py:211 revpipycontrol.py:250 -#: revpipycontrol.py:268 revpipycontrol.py:286 -msgid "Warning" -msgstr "Warnung" - #: revpiprogram.py:408 msgid "" "The piCtroy configuration has been saved successfully. \n" @@ -486,67 +511,67 @@ msgstr "Der Revoluton Pi konnte Teile der Übertragung nicht verarbeiten." msgid "Errors occurred during transmission" msgstr "Bei der Übertragung traten Fehler auf" -#: revpipycontrol.py:93 +#: revpipycontrol.py:105 msgid "Connections..." msgstr "Verbindungen..." -#: revpipycontrol.py:96 +#: revpipycontrol.py:108 msgid "Main" msgstr "Datei" -#: revpipycontrol.py:104 +#: revpipycontrol.py:116 msgid "Visit website..." msgstr "Webseite besuchen..." -#: revpipycontrol.py:106 +#: revpipycontrol.py:118 msgid "Info..." msgstr "Info..." -#: revpipycontrol.py:108 +#: revpipycontrol.py:119 msgid "Help" msgstr "Hilfe" -#: revpipycontrol.py:116 +#: revpipycontrol.py:127 msgid "PLC start" msgstr "PLC Start" -#: revpipycontrol.py:121 +#: revpipycontrol.py:132 msgid "PLC stop" msgstr "PLC Stopp" -#: revpipycontrol.py:126 +#: revpipycontrol.py:137 msgid "PLC restart" msgstr "PLC Neustart" -#: revpipycontrol.py:131 +#: revpipycontrol.py:142 msgid "PLC logs" msgstr "PLC Logs" -#: revpipycontrol.py:142 +#: revpipycontrol.py:153 msgid "PLC watch mode" msgstr "PLC watch Modus" -#: revpipycontrol.py:159 +#: revpipycontrol.py:170 msgid "PLC log..." msgstr "PLC Log..." -#: revpipycontrol.py:161 +#: revpipycontrol.py:172 msgid "PLC options..." msgstr "PLC Optionen..." -#: revpipycontrol.py:163 +#: revpipycontrol.py:174 msgid "PLC program..." msgstr "PLC Programm..." -#: revpipycontrol.py:167 +#: revpipycontrol.py:178 msgid "Disconnect" msgstr "Trennen" -#: revpipycontrol.py:172 +#: revpipycontrol.py:183 msgid "Connect" msgstr "Verbinden" -#: revpipycontrol.py:212 +#: revpipycontrol.py:229 msgid "" "The watch mode ist not supported in version {} of RevPiPyLoad on your RevPi! " "You need at least version 0.4.0." @@ -554,7 +579,7 @@ msgstr "" "Der Watch-Modus wird von der RevPiPyLoad Version {} auf dem RevPi nicht " "unterstützt! Sie benötigen mindestens Version 0.4.0." -#: revpipycontrol.py:251 +#: revpipycontrol.py:268 msgid "" "This version of Logviewer ist not supported in version {} of RevPiPyLoad on " "your RevPi! You need at least version 0.4.1." @@ -562,7 +587,7 @@ 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:269 revpipycontrol.py:287 +#: revpipycontrol.py:286 revpipycontrol.py:304 msgid "" "XML-RPC access mode in the RevPiPyLoad configuration is to small to access " "this dialog" @@ -570,6 +595,10 @@ msgstr "" "Der XML-RPC Modus ist beim RevPiPyLoad nicht hoch genug eingestellt, um " "diesen Dialog zu verwenden!" -#: revpipycontrol.py:326 +#: revpipycontrol.py:343 msgid "Can not reach server!" msgstr "Server ist nicht erreichbar!" + +#~ 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/revpicheckclient.py b/revpipycontrol/revpicheckclient.py index ca6941a..a9b4f27 100644 --- a/revpipycontrol/revpicheckclient.py +++ b/revpipycontrol/revpicheckclient.py @@ -9,8 +9,9 @@ import pickle import tkinter -from threading import Lock +import tkinter.messagebox as tkmsg from mytools import gettrans +from threading import Lock from xmlrpc.client import ServerProxy, MultiCall # Übersetzung laden @@ -27,12 +28,15 @@ class RevPiCheckClient(tkinter.Frame): 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.cli.ps_devices()} self.dict_inps = pickle.loads(self.cli.ps_inps().data) self.dict_outs = pickle.loads(self.cli.ps_outs().data) self.lk = Lock() - self.lst_wins = [] + self.dict_wins = {} + self.__checkwrite = True + self.__lockedvar = None + self.__oldvalue = None self.autorw = tkinter.BooleanVar() self.dowrite = tkinter.BooleanVar() @@ -43,27 +47,76 @@ class RevPiCheckClient(tkinter.Frame): # Aktuelle Werte einlesen self.refreshvalues() - def __chval(self, device, io): - if self.dowrite.get(): + 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.cli.ps_setvalue(device, io[0], io[5].get()) + self.validatereturn( + self.cli.ps_setvalue(device, io[0], io[5].get()) + ) # Alles neu einlesen wenn nicht AutoRW aktiv ist if not self.autorw.get(): self.refreshvalues() + 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 _createiogroup(self, device, frame, iotype): - """Erstellt IO-Gruppen.""" + 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 > 255 * io[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] + ) + + # Focus zurücksetzen + event.widget.focus_set() + + def _createiogroup(self, device, frame, iotype): + u"""Erstellt IO-Gruppen. + + @param device: Deviceposition + @param frame: tkinter Frame + @iotype: 'inp' oder 'out' als str() + + """ # IO-Typen festlegen if iotype == "inp": lst_io = self.dict_inps[device] @@ -120,10 +173,16 @@ class RevPiCheckClient(tkinter.Frame): check.grid(column=1, row=rowcount) else: var = tkinter.IntVar() - - # FIXME: Mehrere Bytes möglich txt = tkinter.Spinbox(s_frame, to=255 * io[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" @@ -131,7 +190,7 @@ class RevPiCheckClient(tkinter.Frame): txt["textvariable"] = var txt.grid(column=1, row=rowcount) - # Steuerelementvariable in IO übernehmen + # Steuerelementvariable in IO übernehmen (mutabel) io.append(var) rowcount += 1 @@ -143,31 +202,31 @@ class RevPiCheckClient(tkinter.Frame): devgrp["text"] = _("Devices of RevPi") devgrp.pack(fill="y", side="left") - for dev in self.lst_devices: + for dev in self.dict_devices: win = tkinter.Toplevel(self) - win.wm_title(dev[1]) + 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.lst_wins.append(win) + self.dict_wins[dev] = win # Devicegruppe erstellen group = tkinter.LabelFrame(win) - group["text"] = dev[1] + group["text"] = self.dict_devices[dev] group.pack(side="left", fill="both", expand=True) for iotype in ["inp", "out"]: frame = tkinter.Frame(group) frame.pack(side="left", fill="both", expand=True) - self._createiogroup(dev[0], frame, iotype) + self._createiogroup(dev, frame, iotype) # Button erstellen btn = tkinter.Button(devgrp) btn["command"] = lambda win=win: self.__showwin(win) - btn["text"] = dev[1] + btn["text"] = self.dict_devices[dev] btn.pack(fill="x", padx=10, pady=5) # Steuerungsfunktionen @@ -196,17 +255,38 @@ class RevPiCheckClient(tkinter.Frame): check["variable"] = self.autorw check.pack(anchor="w") - check = tkinter.Checkbutton(cntgrp) - check["state"] = "disabled" if self.xmlmode < 3 else "normal" - check["text"] = _("Write values to RevPi") - check["variable"] = self.dowrite - check.pack(anchor="w") + 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") 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")) + def _warnwrite(self): + u"""Warnung für Benutzer über Schreibfunktion einmal fragen. + @returns: 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 + def _workvalues(self, io_dicts=None, writeout=False): - """Alle Werte der Inputs und Outputs abrufen.""" + u"""Alle Werte der Inputs und Outputs abrufen. + @param io_dicts: Arbeit nur für dieses Dict() + @param writeout: Änderungen auf RevPi schreiben""" # Abfragelisten vorbereiten if io_dicts is None: @@ -220,13 +300,17 @@ class RevPiCheckClient(tkinter.Frame): if writeout: xmlmc = MultiCall(self.cli) - for dev in self.lst_devices: + 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[0]]: + for io in iotype[dev]: + + # Gesperrte Variable überspringen + if io[5] == self.__lockedvar: + continue # Bytes umwandeln int_byte = int.from_bytes( @@ -236,55 +320,81 @@ class RevPiCheckClient(tkinter.Frame): # Bit-IO new_val = bool(int_byte & 1 << io[4]) if writeout and new_val != io[5].get(): - xmlmc.ps_setvalue(dev[0], io[0], 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[0], io[0], 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: - xmlmc() + self.validatereturn(xmlmc()) if self.autorw.get(): self.master.after(200, self._workvalues) def hideallwindows(self): - for win in self.lst_wins: - win.withdraw() + u"""Versteckt alle Fenster.""" + for win in self.dict_wins: + self.dict_wins[win].withdraw() def readvalues(self): + u"""Ruft nur Input Werte von RevPi ab und aktualisiert Fenster.""" if not self.autorw.get(): self._workvalues([self.dict_inps]) def refreshvalues(self): + u"""Ruft alle IO Werte von RevPi ab und aktualisiert Fenster.""" if not self.autorw.get(): self._workvalues() def toggleauto(self): + 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 + self.chk_dowrite["state"] = "normal" if self.xmlmode >= 3 \ + and self.autorw.get() else "disabled" + if self.autorw.get(): self._workvalues() + else: + 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): - if not self.autorw.get(): + u"""Schreibt geänderte Outputs auf den RevPi.""" + if self._warnwrite() and not self.autorw.get(): self._workvalues([self.dict_outs], True) - - -# Testdrive -if __name__ == "__main__": - cli = ServerProxy("http://192.168.50.35:55123") - cli.psstart() - - tk = tkinter.Tk() - RevPiCheckClient(tk, cli, 3) - tk.mainloop() diff --git a/revpipycontrol/revpiprogram.py b/revpipycontrol/revpiprogram.py index 269eddd..d9cd70c 100644 --- a/revpipycontrol/revpiprogram.py +++ b/revpipycontrol/revpiprogram.py @@ -646,7 +646,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 @@ -719,9 +719,3 @@ class RevPiProgram(tkinter.Frame): # Temp-Dir aufräumen if dirtmp is not None: rmtree(dirtmp) - - -if __name__ == "__main__": - root = tkinter.Tk() - myapp = RevPiProgram(root, None, "test") - root.mainloop() diff --git a/revpipycontrol/revpipycontrol.py b/revpipycontrol/revpipycontrol.py index a46989b..8f234d0 100755 --- a/revpipycontrol/revpipycontrol.py +++ b/revpipycontrol/revpipycontrol.py @@ -1,13 +1,14 @@ #!/usr/bin/python3 # # RevPiPyControl -# Version: 0.4.1 +# 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 @@ -15,6 +16,7 @@ import revpiprogram import socket import tkinter import tkinter.messagebox as tkmsg +import webbrowser from functools import partial from mytools import addroot, gettrans from xmlrpc.client import ServerProxy @@ -22,6 +24,8 @@ from xmlrpc.client import ServerProxy # Übersetzung laden _ = gettrans() +pycontrolversion = "0.4.1" + class RevPiPyControl(tkinter.Frame): @@ -29,6 +33,7 @@ class RevPiPyControl(tkinter.Frame): 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 @@ -74,8 +79,15 @@ class RevPiPyControl(tkinter.Frame): self.tkprogram.destroy() if self.debugframe is not None: 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): u"""Erstellt den Fensterinhalt.""" # Hauptfenster @@ -104,7 +116,6 @@ class RevPiPyControl(tkinter.Frame): label=_("Visit website..."), command=self.visitwebsite) menu1.add_separator() menu1.add_command(label=_("Info..."), command=self.infowindow) - # TODO: Menü einbauen self.mbar.add_cascade(label=_("Help"), menu=menu1) self.var_conn = tkinter.StringVar(self) @@ -199,8 +210,14 @@ class RevPiPyControl(tkinter.Frame): self.mbar.entryconfig("PLC", state="normal") def infowindow(self): - # TODO: Infofenster aufrufen - pass + u"""Öffnet das Fenster für die Verbindungen.""" + win = tkinter.Toplevel(self) + revpiinfo.RevPiInfo(win) + win.focus_set() + win.grab_set() + self.wait_window(win) + self.dict_conn = revpiplclist.get_connections() + self._fillconnbar() def plcdebug(self): u"""Baut den Debugframe und packt ihn.""" @@ -362,8 +379,9 @@ class RevPiPyControl(tkinter.Frame): self.master.after(1000, self.tmr_plcrunning) def visitwebsite(self): - # TODO: Webseite besuchen - pass + u"""Öffnet auf dem System einen Webbrowser zur Projektseite.""" + webbrowser.open("https://revpimodio.org") + if __name__ == "__main__": root = tkinter.Tk()