diff --git a/.hgignore b/.hgignore index 86cdd27..9e0632f 100644 --- a/.hgignore +++ b/.hgignore @@ -3,3 +3,6 @@ syntax: glob deb_dist/* dist/* revpipycontrol.egg-info/* +doc/* +deb/* +.eric6project/* diff --git a/data/revpipycontrol.ico b/data/revpipycontrol.ico new file mode 100644 index 0000000..4602e2f Binary files /dev/null and b/data/revpipycontrol.ico differ diff --git a/data/revpipycontrol.png b/data/revpipycontrol.png new file mode 100644 index 0000000..18efbe8 Binary files /dev/null and b/data/revpipycontrol.png differ diff --git a/revpipycontrol.e4p b/revpipycontrol.e4p index 7fdd4cd..74ed6ea 100644 --- a/revpipycontrol.e4p +++ b/revpipycontrol.e4p @@ -1,7 +1,7 @@ - + en_US @@ -14,7 +14,6 @@ akira@narux.de - revpipycontrol/__init__.py revpipycontrol/revpipycontrol.py revpipycontrol/revpicheckclient.py setup.py @@ -29,6 +28,7 @@ data + doc revpipycontrol/revpipycontrol.py @@ -144,6 +144,59 @@ + + + + + ERIC4DOC + + + + + ignoreFilePatterns + + + + + + + + noindex + + + True + + + outputDirectory + + + doc + + + qtHelpEnabled + + + False + + + sourceExtensions + + + + + + + + useRecursion + + + True + + + + + + diff --git a/revpipycontrol/revpilogfile.py b/revpipycontrol/revpilogfile.py index 9113d5d..c7326d2 100644 --- a/revpipycontrol/revpilogfile.py +++ b/revpipycontrol/revpilogfile.py @@ -69,7 +69,7 @@ class RevPiLogfile(tkinter.Frame): def btn_clearapp(self): self.applog.delete(1.0, tkinter.END) - + def btn_clearplc(self): self.plclog.delete(1.0, tkinter.END) diff --git a/revpipycontrol/revpioption.py b/revpipycontrol/revpioption.py index 243b2ba..0d529b3 100644 --- a/revpipycontrol/revpioption.py +++ b/revpipycontrol/revpioption.py @@ -11,18 +11,23 @@ import tkinter.messagebox as tkmsg class RevPiOption(tkinter.Frame): - def __init__(self, master, xmlcli): + def __init__(self, master, xmlcli, xmlmode): + if xmlmode < 2: + return None + super().__init__(master) self.pack(expand=True, fill="both") self.xmlcli = xmlcli + self.xmlmode = xmlmode + self.xmlstate = "normal" if xmlmode == 3 else "disabled" # Fenster bauen self._createwidgets() self._loadappdata() def _createwidgets(self): - self.master.wm_title("RevPi Python PLC Connections") + self.master.wm_title("RevPi Python PLC Options") self.master.wm_resizable(width=False, height=False) cpadw = {"padx": 4, "pady": 2, "sticky": "w"} @@ -40,18 +45,25 @@ class RevPiOption(tkinter.Frame): ckb_start = tkinter.Checkbutton(stst) ckb_start["text"] = "Programm automatisch starten" + ckb_start["state"] = self.xmlstate ckb_start["variable"] = self.var_start ckb_start.grid(**cpadw) + ckb_reload = tkinter.Checkbutton(stst) - ckb_reload["text"] = "Programm nach Absturz neustarten" + ckb_reload["text"] = "Programm nach Beenden neu starten" + ckb_reload["state"] = self.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["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["variable"] = self.var_zerr @@ -65,32 +77,40 @@ class RevPiOption(tkinter.Frame): self.var_pythonver = tkinter.IntVar(prog) self.var_startpy = tkinter.StringVar(prog) self.var_slave = tkinter.BooleanVar(prog) - + self.var_pythonver.set(3) lbl = tkinter.Label(prog) lbl["text"] = "Python Version" lbl.grid(columnspan=2, row=0, **cpadw) rbn = tkinter.Radiobutton(prog) + rbn["state"] = self.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["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.grid(columnspan=2, **cpadw) + lst = self.xmlcli.get_filelist() if len(lst) == 0: lst.append("none") opt_startpy = tkinter.OptionMenu( prog, self.var_startpy, *lst) + opt_startpy["state"] = self.xmlstate opt_startpy.grid(columnspan=2, **cpadwe) + ckb_slave = tkinter.Checkbutton(prog, justify="left") + ckb_slave["state"] = self.xmlstate ckb_slave["text"] = "RevPi als PLC-Slave verwenden" ckb_slave["state"] = "disabled" ckb_slave["variable"] = self.var_slave @@ -109,32 +129,41 @@ 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["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["text"] = \ "Download von piCtory Konfiguration und\nPLC Programm zulassen" 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["text"] = \ "Upload von piCtory Konfiguration und\nPLC Programm zualssen" self.ckb_xmlmod3["variable"] = self.var_xmlmod3 self.ckb_xmlmod3.grid(**cpadw) + lbl = tkinter.Label(xmlrpc) lbl["text"] = "XML-RPC Serverport" lbl.grid(**cpadw) + spb_xmlport = tkinter.Spinbox(xmlrpc) spb_xmlport["to"] = 65535 spb_xmlport["from"] = 1024 + spb_xmlport["state"] = self.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.grid(column=0, row=3) @@ -181,9 +210,10 @@ class RevPiOption(tkinter.Frame): dc["xmlrpc"] += 1 dc["xmlrpcport"] = self.var_xmlport.get() + self.xmlmode = dc["xmlrpc"] ask = tkmsg.askyesnocancel( - "Frage", "Die Einstellungen werden jetzt auf den Revolution Pi " + "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 @@ -191,7 +221,8 @@ class RevPiOption(tkinter.Frame): if ask is not None: if self.xmlcli.set_config(dc, ask): tkmsg.showinfo( - "Information", "Einstellungen gespeichert.", parent=self.master + "Information", "Einstellungen gespeichert.", + parent=self.master ) else: tkmsg.showerror( @@ -204,14 +235,14 @@ class RevPiOption(tkinter.Frame): if not self.var_xmlon.get(): ask = tkmsg.askyesno( "Frage", "Soll der XML-RPC Server wirklich beendet werden? " - "Sie können dann mit diesem Programm NICHT mehr auf den " + "Sie können dann NICHT mehr mit diesem Programm auf den " "Revolution Pi zugreifen.", parent=self.master ) if not ask: self.var_xmlon.set(True) self.xmlmods() - + def xmlmods(self): self.ckb_xmlmod2["state"] = \ "normal" if self.var_xmlon.get() else "disabled" diff --git a/revpipycontrol/revpiplclist.py b/revpipycontrol/revpiplclist.py index 077fe85..320704f 100644 --- a/revpipycontrol/revpiplclist.py +++ b/revpipycontrol/revpiplclist.py @@ -36,6 +36,8 @@ class RevPiPlcList(tkinter.Frame): super().__init__(master) self.pack() + self.changes = False + # Daten laden self._connections = {} @@ -117,6 +119,7 @@ class RevPiPlcList(tkinter.Frame): makedirs(os.path.dirname(savefile), exist_ok=True) fh = open(savefile, "wb") pickle.dump(self._connections, fh) + self.changes = False except: return False return True @@ -133,14 +136,18 @@ class RevPiPlcList(tkinter.Frame): self.build_listconn() self.evt_btnnew() + self.changes = True def evt_btnclose(self): - ask = tkmsg.askyesno( - "Frage...", - "Wollen Sie wirklich beenden?\n" - "Nicht gespeicherte Änderungen gehen verloren", - parent=self.master - ) + 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() @@ -168,6 +175,7 @@ class RevPiPlcList(tkinter.Frame): del self._connections[item] self.build_listconn() self.evt_btnnew() + self.changes = True def evt_btnsave(self): if self._saveappdata(): diff --git a/revpipycontrol/revpiprogram.py b/revpipycontrol/revpiprogram.py index 6c7264f..cabce28 100644 --- a/revpipycontrol/revpiprogram.py +++ b/revpipycontrol/revpiprogram.py @@ -19,7 +19,10 @@ from xmlrpc.client import Binary class RevPiProgram(tkinter.Frame): - def __init__(self, master, xmlcli, revpi): + def __init__(self, master, xmlcli, xmlmode, revpi): + if xmlmode < 2: + return None + super().__init__(master) # master.protocol("WM_DELETE_WINDOW", self._checkclose) self.pack(expand=True, fill="both") @@ -27,6 +30,8 @@ class RevPiProgram(tkinter.Frame): self.uploaded = False self.revpi = revpi self.xmlcli = xmlcli + self.xmlmode = xmlmode + self.xmlstate = "normal" if xmlmode == 3 else "disabled" # Fenster bauen self._createwidgets() @@ -56,7 +61,7 @@ class RevPiProgram(tkinter.Frame): # Gruppe Programm prog = tkinter.LabelFrame(self) prog.columnconfigure(0, weight=1) - prog["text"] = "PLC Pythonprogramm" + prog["text"] = "PLC Python programm" prog.grid(columnspan=2, pady=2, sticky="we") # Variablen vorbereiten @@ -98,22 +103,26 @@ class RevPiProgram(tkinter.Frame): opt = tkinter.OptionMenu( prog, self.var_typeup, *self.lst_typeup, command=self._evt_optup) + opt["state"] = self.xmlstate opt["width"] = 10 opt.grid(column=1, row=r, **cpad) r = 3 ckb = tkinter.Checkbutton(prog) + ckb["state"] = self.xmlstate ckb["text"] = "vorher alles im Uploadverzeichnis löschen" 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["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.grid(column=1, row=r, **cpad) @@ -135,6 +144,7 @@ class RevPiProgram(tkinter.Frame): lbl.grid(column=0, row=1, **cpadw) btn = tkinter.Button(picto) btn["command"] = self.setpictoryrsc + btn["state"] = self.xmlstate btn["text"] = "Upload" btn.grid(column=1, row=1, **cpad) @@ -144,7 +154,7 @@ class RevPiProgram(tkinter.Frame): proc["text"] = "piControl0 Prozessabbild" proc.grid(columnspan=2, pady=2, sticky="we") lbl = tkinter.Label(proc) - lbl["text"] = "Prozessabbild herunterladen" + lbl["text"] = "Prozessabbild-Dump herunterladen" lbl.grid(column=0, row=0, **cpadw) btn = tkinter.Button(proc) btn["command"] = self.getprocimg @@ -282,7 +292,7 @@ class RevPiProgram(tkinter.Frame): ask = tkmsg.askyesno( parent=self.master, title="Frage", message="Soll nach dem Hochladen der piCtory Konfiguration " - "ein reset am piControl Treiber durchgeführt werden?" + "ein Reset am piControl Treiber durchgeführt werden?" ) ec = self.xmlcli.set_pictoryrsc(Binary(fh.read()), ask) @@ -400,7 +410,7 @@ class RevPiProgram(tkinter.Frame): if tup == 0: # Datei fileselect = tkfd.askopenfilenames( - parent=self.master, title="Pythonprogramm übertragen...", + parent=self.master, title="Python Programm übertragen...", filetypes=(("Python", "*.py"), ("All Files", "*.*")) ) if type(fileselect) == tuple and len(fileselect) > 0: diff --git a/revpipycontrol/revpipycontrol.png b/revpipycontrol/revpipycontrol.png new file mode 100644 index 0000000..18efbe8 Binary files /dev/null and b/revpipycontrol/revpipycontrol.png differ diff --git a/revpipycontrol/revpipycontrol.py b/revpipycontrol/revpipycontrol.py index 48bfb82..91f01a0 100755 --- a/revpipycontrol/revpipycontrol.py +++ b/revpipycontrol/revpipycontrol.py @@ -22,6 +22,7 @@ from xmlrpc.client import ServerProxy socket.setdefaulttimeout(2) + def addroot(filename): u"""Hängt root-dir der Anwendung vor Dateinamen. @@ -48,6 +49,7 @@ class RevPiPyControl(tkinter.Frame): self.dict_conn = revpiplclist.get_connections() self.errcount = 0 self.revpiname = None + self.xmlmode = 0 # Globale Fenster self.tklogs = None @@ -148,7 +150,7 @@ class RevPiPyControl(tkinter.Frame): ) # Server prüfen try: - sp.system.listMethods() + self.xmlmode = sp.xmlmodus() except: self.servererror() else: @@ -183,28 +185,46 @@ class RevPiPyControl(tkinter.Frame): self._fillconnbar() def plclogs(self): - # TODO: nicht doppelt starten - win = tkinter.Toplevel(self) - self.tklogs = revpilogfile.RevPiLogfile(win, self.cli) + 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 - #self.tkmonitor = revpicheckclient.RevPiCheckClient(self.master, self.cli) pass def plcoptions(self): - win = tkinter.Toplevel(self) - self.tkoptions = revpioption.RevPiOption(win, self.cli) - win.focus_set() - win.grab_set() - self.wait_window(win) + 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!" + ) + else: + win = tkinter.Toplevel(self) + self.tkoptions = \ + revpioption.RevPiOption(win, self.cli, self.xmlmode) + win.focus_set() + win.grab_set() + self.wait_window(win) + self.xmlmode = self.tkoptions.xmlmode def plcprogram(self): - win = tkinter.Toplevel(self) - self.tkprogram = revpiprogram.RevPiProgram(win, self.cli, self.revpiname) - win.focus_set() - win.grab_set() - self.wait_window(win) + 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!" + ) + else: + win = tkinter.Toplevel(self) + self.tkprogram = revpiprogram.RevPiProgram( + win, self.cli, self.xmlmode, self.revpiname) + win.focus_set() + win.grab_set() + self.wait_window(win) def plcstart(self): self.cli.plcstart() @@ -223,9 +243,7 @@ class RevPiPyControl(tkinter.Frame): self._btnstate() self.mbar.entryconfig("PLC", state="disabled") self.var_conn.set("") - self._closeall() - tkmsg.showerror("Fehler", "Server ist nicht erreichbar!") def tmr_plcrunning(self): @@ -250,6 +268,10 @@ class RevPiPyControl(tkinter.Frame): plcec = "RUNNING" elif plcec == -2: plcec = "FILE NOT FOUND" + elif plcec == -9: + plcec = "PROGRAM KILLED" + elif plcec == -15: + plcec = "PROGRAMS TERMED" elif plcec == 0: plcec = "NOT RUNNING" self.var_status.set(plcec) diff --git a/setup.py b/setup.py index 3dcc4b1..59e732a 100644 --- a/setup.py +++ b/setup.py @@ -24,9 +24,9 @@ globsetup = { "url": "https://revpimodio.org/revpipyplc/", "license": "LGPLv3", "version": "0.2.6", - + "name": "revpipycontrol", - + "description": "PLC Loader für Python-Projekte auf den RevolutionPi", "long_description": "" "Dieses Programm startet beim Systemstart ein angegebenes Python PLC\n"