Funktion ausführen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Kornfeld67
User
Beiträge: 7
Registriert: Montag 7. Dezember 2015, 15:02

Hi Leute ich habe folgende Funktion:

Code: Alles auswählen

    
    def senden(Befehl):
        
        if(not(UART.isOpen())):
            Aktiv = True
            UART.open()
        UART.write(Befehl)
        if (Aktiv):
            UART.close()
            Aktive = False
            
..diese möchte ich jetzt aufrufen.. das probiere ich so:

Code: Alles auswählen

    
    def Parabutton(self,event):
        Motortyp = ('#' + ':CL_motor_type' +'\r')
        senden(Motortyp)
        lesen()
        
dabei bekomme ich den Fehler das die globale Variable senden nicht definiert ist..
Wo liegt da mein Denkfehler?

MfG
Kornfeld67
User
Beiträge: 7
Registriert: Montag 7. Dezember 2015, 15:02

komplette Fehlermeldung als Ergänzung..

line 182, in Parabutton
senden(Motortyp)
NameError: global name 'senden' is not defined
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Kornfeld67 hat geschrieben:dabei bekomme ich den Fehler das die globale Variable senden nicht definiert ist.
Dein Code ist aus dem Zusammenhang gerissen, daher ist die Frage nicht wirklich ordentlich zu beantworten. Befinden sich beide Funktionen im gleichen Modul? Ist Parabutton eine Methode einer Klasse? Befindet sich die Funktion senden auf der "Hauptebene" des Moduls?
Kornfeld67
User
Beiträge: 7
Registriert: Montag 7. Dezember 2015, 15:02

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: cp1252 -*-

import wx
import time
import serial

global Aktiv
Aktiv = False 

class gui(wx.Frame):

    def __init__(self,parent,id):

        #Parameter fuer serielle Kommunikation
        COMNUM = 6                                                                          #Festlegen des COM-PORTS
        global UART
        UART = serial.Serial()                          
        UART.Baudrate = 115200
        UART.port = COMNUM - 1
        UART.timeout = 0.2
        UART.dsrdtr = False
        
        
        # Oeffnen eines Fensters / Panel
        wx.Frame.__init__(self,parent,id,'IRBAN',size=(1000,800))
        Bedienung = wx.Panel(self)
        
#        try:
#           # Bild laden
#            image_file = 'Sonne.jpg'
#            bmp1 = wx.Image(image_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
#            # Bild oben links platzieren
#            self.bitmap1 = wx.StaticBitmap(self, -1, bmp1, (0, 0))
#        except IOError:
#            print "Image file %s not found" % imageFile
#            raise SystemExit
       
        #Startbutton erstellen
        Startbutton = wx.Button(Bedienung,label="Bewegung starten",pos=(800,100),size=(150,50))
        self.Bind(wx.EVT_BUTTON,self.Startbutton,Startbutton)

        #Button für Abbruch --> spaeter Interruptsteuerung!
        Abbrechen = wx.Button(Bedienung,label="STOPP!!",pos=(800,200),size=(150,50))
        self.Bind(wx.EVT_BUTTON,self.Abbrechen,Abbrechen)

        #Exit Button erstellen
        Exitbutton = wx.Button(Bedienung,label="Fenster schliessen",pos=(800,300),size=(150,50))
        self.Bind(wx.EVT_BUTTON,self.Exitbutton,Exitbutton)

        # Button fuer Uebergabe der Parameter erstellen
        Parabutton = wx.Button(Bedienung,label="Paramter schreiben",pos=(800,400),size=(150,50))
        self.Bind(wx.EVT_BUTTON,self.Parabutton,Parabutton)           

        #Fenster schliessen bei Fenster schliessen (Kreuz)
        self.Bind(wx.EVT_CLOSE, self.closewindow)

        #Menuebar erstellen
        status=self.CreateStatusBar()
        Menuebar=wx.MenuBar()
        Erste = wx.Menu()
        Zweite = wx.Menu()
        Erste.Append(wx.NewId(),"Was auch immer", "Zeig mir was auch immer")
        Erste.Append(wx.NewId(),"Was auch immer1", "Zeig mir was auch immer1")
        Zweite.Append(wx.NewId(),"Was auch immer", "Zeig mir was auch immer")
        Zweite.Append(wx.NewId(),"Was auch immer1", "Zeig mir was auch immer1")
        Menuebar.Append(Erste,"Erste")
        Menuebar.Append(Zweite,"Zweite")
        self.SetMenuBar(Menuebar)

        #Slider

        Uhrzeit = wx.Slider(Bedienung, -1, 1, 1, 12,pos=(100,100), size=(350,1), style=wx.SL_AUTOTICKS | wx.SL_LABELS)
        Uhrzeit.SetTickFreq(5)
        Neigung = wx.Slider(Bedienung, -1, 0, -15, 15,pos=(100,300), size=(350,1), style=wx.SL_AUTOTICKS | wx.SL_LABELS)
        Neigung.SetTickFreq(5)
        Frequenz = wx.Slider(Bedienung, -1, 0, 0, 115,pos=(100,500), size=(350,1), style=wx.SL_AUTOTICKS | wx.SL_LABELS)
        Frequenz.SetTickFreq(5)


        #Statischer Text zur Beschriftung der Slider
        font = wx.Font(18, wx.FONTFAMILY_DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False)
        BezUhr = wx.StaticText(Bedienung,-1, "Position der Bewegung (Modell Uhr)",(75,50),(260,-1),wx.ALIGN_CENTER)
        BezUhr.SetForegroundColour('black')
        BezUhr.SetFont(font)
        BezNei = wx.StaticText(Bedienung,-1, "Neigewinkel zur vollen h",(135,250),(260,-1),wx.ALIGN_CENTER)
        BezNei.SetForegroundColour('black')
        BezNei.SetFont(font)
        BezFre = wx.StaticText(Bedienung,-1, "Schritte pro min",(135,450),(260,-1),wx.ALIGN_CENTER)
        BezFre.SetForegroundColour('black')
        BezFre.SetFont(font)

        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
        #Hintergrundbild

    def senden(Befehl):
        if(not(UART.isOpen())):
            Aktiv = True
            UART.open()
        UART.write(Befehl)
        if (Aktiv):
            UART.close()
            Aktiv = False

    def lesen():
        if(not(UART.isOpen())):
            Aktiv = True
            UART.open()
        line = []
        while True:
            for c in ser.read():
                line.append(c)
                if c == '\r':
                    print (line)
                    break
        
    def OnEraseBackground(self, evt):
        """
        Add a picture to the background
        """
        # yanked from ColourDB.py
        dc = evt.GetDC()
 
        if not dc:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()
        bmp = wx.Bitmap("Sonne.jpg")
        dc.DrawBitmap(bmp, 0, 0)
         
        
    def Startbutton(self,event):
        #self.Close(True)
        Aktiv = False
        if(not(UART.isOpen())):
            Aktiv = True
            UART.open()
            UART.write("Start" + '\r' )
        if(Aktiv):
            UART.close()
            Aktiv = False
            
            
            
            
        if(not(UART.isOpen())):
            Aktiv = True
            UART.open()
        Antwort = UART.readline()
        time.sleep(0.1)
        if(Aktiv):
           UART.close()
        print Antwort
        
    
    def Abbrechen(self,event):
        #self.Close(True)
        Aktiv = False
        if(not(UART.isOpen())):
            Aktiv = True
            UART.open()
            UART.write("Stopp" + '\r' )
            time.sleep(0.1)
        if(Aktiv):
            UART.close()
            Aktiv = False
            
            
        if(not(UART.isOpen())):
            Aktiv = True
            UART.open()
        Antwort = UART.readline()
        time.sleep(0.1)
        if(Aktiv):
            UART.close()
        print Antwort

    def Parabutton(self,event):
        Motortyp = ('#' + ':CL_motor_type' +'\r')
        senden(Motortyp)
        lesen()
    
    def Exitbutton(self,event):
        self.Close(True)

    def closewindow(self,event):
        self.Destroy()


                
            

if __name__ == '__main__':
    app=wx.App()
    frame = gui(parent=None,id=-1)
    frame.Show()
    app.MainLoop()
Das ist der vollständige code.. Ich möchte in der GUI den Button "Parameter schreiben" (Funktion Parabutton) drücken. Daraufhin wird der zu sendende String definiert. Als Argument Befehl an die Funktion senden übergeben und über die serielle Schnittstelle gesendet.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Die Funktion "senden" ist nicht im globalen Namensraum (auf Modulebene) definiert sondern im Namensraum der Klasse "gui". Anzusprechen wäre diese mit gui.senden(...). Du solltest deine Funktionen aus der Klasse lösen und auf Modulebene definieren.

Darüber hinaus die obligatorischen Hinweise auf PEP8 und "Vergiss, dass es global gibt!"
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Und weil `senden` und `lesen` innerhalb der Klasse definiert sind, sind es Methoden und deshalb fehlt ihnen noch der `self` Parameter.
Wenn es dann tatsaechlich Methoden sind, kannst du auch `UART` als `self.UART` definieren und brauchst kein `global` mehr.

Eine Korrektur zu bwbg: Die Methode kann man zwar mit `gui.senden` ansprechen, doch benutzen musst du sie tatsaechlich mit `self.senden`.
Den Hinweis auf PEP 8 kann ich gar nicht genug unterschreiben.
BlackJack

@Kornfeld67: Ich würde dringend empfehlen die Programmlogik von der GUI zu trennen und erst die Programmlogik zu implementieren, ohne die GUI. Und die GUI dann später auf die Logik aufzusetzen.

In dem Quelltext ist so viel falsch das man im Grunde komplett von Null anfangen sollte. Lerne Funktionen, und danach dann Objektorientierung *richtig*, nicht während Du versuchst das alles irgendwie in eine GUI zu basteln wo *alles* komplett in einer Gott-Klasse steckt und zusätzlich immer noch mit ``global`` versucht wird sich an Objektorientierung irgendwie vorbeizumogeln. ``global`` hat in einem sauberen Programm nichts zu suchen. Das Schlüsselwort hat auf Modulebene übrigens auch überhaupt gar keinen Effekt.

Ein Blick in den Style Guide for Python Code kann nicht schaden. `wx` hält sich da nicht dran weil sich das zugrundeliegende wxWidgets an die Schreibweise der C++-Bibliothek hält, also kann man selbst auch bei Methoden die mit der GUI zu tun haben aus Konsistenzgründen diese Schreibweise verwenden, aber das ist kein Grund Klassen komplett in Kleinbuchstaben zu bennenen und Variablen komplett in Grossbuchstaben.

Leerzeichensetzung nach den Richtlinien, insbesondere nach Kommas würde dem Quelltext auch gut tun. Das ist teilweise schwer zu lesen.

GUI sollte man nicht mit absoluten Pixelangaben für die Elemente erstellen. Das sieht auf verschiedenen Rechnern/Einstellungen unterschiedlich aus und kann bis hin zur Unbedienbarkeit gehen. Solche GUIs sind auch nur sehr umständlich umzugestalten weil man potentiell eine Menge Positionen/Grössen von Hand anpassen muss wenn man etwas einfügt oder entfernt, oder auch schon wenn man eine Beschriftung länger oder kürzer macht. `wx` kennt dynamische Layouts die sich den Inhalten automatisch anpassen. Vergiss `pos` und `size`.

``if`` ist keine Funktion also sollte man das auch nicht so schreiben als wäre es eine. Zwischen das Schlüsselwort und die Bedingung gehört ein Leerzeichen und Klammern um die gesamte Bedingung sind überflüssig wenn die auf die aktuelle Zeile passt. Ebenso ist ``not`` keine Funktion.

Was Du da mit `Aktiv` anstellst sieht alles sehr merkwürdig aus, teilweise offensichtlich unsinnig, wenn beispielsweise `Aktiv` gegen Ende einer Methode/Funktion an einen Wert gebunden wird, der danach nie wieder verwendet wird. An der Stelle dachtest oder denkst Du vielleicht noch das ``global`` auf Modulebene irgendetwas bewirkt, aber das tut es nicht und ``global`` ist sowieso keine Lösung sondern ein Problem.

Im Zusammenhang mit `Aktiv` scheint sich auch in mehreren Methoden/Funktionen ein sehr ähnliches Codemuster zu wiederholen: Das verletzt das „Don't Repeat Yourself“-Prinzip (DRY-Prinzip). Wenn man bei wiederholtem Code oder Daten einen Fehler entdeckt oder eine Änderung vornehmen will, dann muss man daran denken das bei jeder Kopie zu tun und bei jeder Kopie das auch konsistent gleich ändern. Das ist eine Fehlerquelle. Man kann leicht Kopien vergessen oder übersehen oder bei Änderungen aus versehen Unterschiede in den Kopien hinterlassen.

Bei `lesen()` erscheint mir die Schleife überflüssig. Man kann von `Serial`-Exemplaren bereits eine Zeile lesen lassen. Wenn man spezielle Zeilenenden dafür verwenden will gibt es dafür Wrapper im `io`-Modul.

`OnEraseBackground()` sieht ineffizient aus weil dort jedes mal wenn mindestens ein Teil des Fensterhintergrunds neu gezeichnet werden muss erneut die JPG-Datei geladen und dekodiert wird.

Anstelle von ``('#' + ':CL_motor_type' +'\r')`` kann man auch einfach ``'#:CL_motor_type\r'`` schreiben, ohne die überflüssigen Klammern und Konkatenationen.
BlackJack

Mal als Beispiel warum man Layouts nicht selber mit `pos` und `size` machen sollte, so sieht die GUI nämlich bei mir aus:
Bild
Die drei Überschriften und Schieberegler links sind nicht wirklich schön ausgerichtet. Von den Schiebereglern wird jeweils nur diese eine Zahl angezeigt weil Du denen per `size` nur *ein* Pixel Höhe zugestanden hast. Das führt bei meinem System und Theme dazu, dass die Regler selbst gar nicht erst gezeichnet werden. Unterschiedliche Systeme, und dort auch unterschiedliche Themes und/oder Systemeinstellungen, benötigen unterschiedlich viel Platz um Anzeigeelemente darzustellen. Darum sollte man das nicht per Hand selber vorgeben sondern `wx` so viel Platz wählen lassen wie die Anzeigeelemente nun mal benötigen auf dem System und mit dem Theme was beim Benutzer verwendet wird. Nicht mehr, aber auch nicht weniger.
Kornfeld67
User
Beiträge: 7
Registriert: Montag 7. Dezember 2015, 15:02

Vielen Dank erstmal für die sehr ausführlichen Antworten! Sind sehr hilfreich!

Wenn ich dann ohne global Variablen arbeite und die Variablen aus der jeweiligen Funktion auch außerhalb der Funktion nutzen möchte dann mache ich das mit einem return xy ?! Und diese übergebe ich dann einfach in die anderen jeweiligen Funktionen?

Das mit dem Aktiv habe ich mal in einem Buch gesehen wo diese Variable als Flag genutzt wird um den Zustand der Schnittstelle definiert zu haben. Dachte ich ist eine gute Idee... Wobei die Abfrage ja dann auch immer über .isOpen() geschehen könnte.. Aber dauert das vielleicht dann länger?

Das Problem mit den Positionen der jeweiligen widgets etc. ist schlecht (für mich)... Aber was gibt es denn für alternativen zu pos & size?
Wenn ich das jetzt entferne dann packt er alles übereinander in die obere linke Ecke.

Und ganz allgemein mal die Frage.. Wie fuchst ihr euch in die libs ein? indem ihr euch die komplette doc anguckt oder gibt es da auch einfachere Wege? Oder zumindest übersichtlicher.
BlackJack

@Kornfeld67: Ja, Werte betreten Funktionen und Methoden als Argumente und verlassen sie als Rückgabewerte. Nur so kann man langfristig den Überblick behalten.

`isOpen()` würde in diesem Fall nicht das gleiche Vehalten zur Folge haben. Allerdings frage ich mich sowieso ob Du diese Fallunterscheidungen überhaupt brauchst. Entweder Du öffnest die Verbindung einmal und arbeitest dann damit, oder Du öffnest und schliesst die Verbindung jedes mal. Das sollte man dann aber auch mit ``try``/``finally`` absichern oder ``with`` verwenden, damit auch tatsächlich immer wieder geschlossen wird. In beiden Fällen braucht man dann weder `Aktiv` noch `isOpen()` weil man ja *weiss* in welchem Zustand sich die serielle Verbindung befindet. Und sollte sie das nicht tun, dann kommt auch ohne Prüfung eine Ausnahme.

Die Alternative zu `pos` und `size` sind „Sizer“. Wobei das nicht ganz stimmt, denn als Alternative würde ich das nicht bezeichnen, sondern „Sizer“ sind der übliche Weg `wx`-GUIs zu layouten.

Wie man sich in eine Bibliothek einarbeitet hängt von der Bibliothek und deren Dokumentation ab. `wxPython` hat doch gleich auf der Projektseite Links für Leute die's lernen wollen. Bei GUI-Toolkits ist oft auch ein Blick in die Dokumentation der zugrundeliegenden Bibliothek sinnvoll, weil nicht immer alle Information von dort in die Dokumentation der Python-Anbindung ”kopiert” wird. Ist ja auch eine undankbare Arbeit wenn man das nicht irgendwie automatisieren kann. Bei `wx` wäre das `wxWidgets`. Wobei die `wx`-Dokumentation auch viele Beispiele in C++ enthält, eben weil die Abschnitte kopiert wurden. Man muss also mindestens so viel C++ lesen lernen dass man versteht wie man das auf die Python-Anbindung übertragen kann/muss.

Die Dokumentation der meisten GUI-Toolkits enthält neben der reinen API-Dokumentation zu den Funktionen, Klassen, und Methoden auch Texte über generelles vorgehen für bestimmte Teilbereiche wo man einen Überblick über das Zusammenspiel der Komponenten bekommen kann. Bei `wx` ist das unter der Überschrift „Programming Guides“ zusammengefasst. Für das Layout sollte man den „Sizers Overview“ mal durchgearbeitet haben. Da wird das Konzept und die überwiegend eingesetzten Unterklassen von `wx.Sizer` erklärt.

Bei `wx` ist die Demoanwendung, die so ziemlich alle Widgets beinhalte, neben der Dokumentation eine nützliche Quelle.
Antworten