Hallo,
ich habe ein Problem. Ich will eine Funktion aus einer anderen Python
Datei aufrufen. Der Code sieht folgendermaßen aus:
import Functions as fts ##Importieren der Datei.
...
Button1 = Button(window, text="Move", bg="white", fg="black",
command=fts.moveforward)
Button1.grid(column=3, row=1)
In der Functions.py sieht es so aus:
def moveforward():
....
Wenn ich das Programme starte, kommt folgende Fehlermeldung:
AttributeError: module 'Functions' has no attribute 'moveforward'
So wie ich die Fehlermeldung verstehe, findet er die Funktion nicht,
obwohl diese eindeutig vorhanden ist. Woran könnte es liegen? Danke für
eure Hilfe im Voraus.
Die Ursache liegt möglicherweise in dem was Du uns nicht erzählst.
Poste doch einfach mal den vollständigen Code so wie er nicht
funktioniert, gerne um alles unnötige gekürzt, soweit gekürzt bis nur
noch das Nichtfunktionieren als solches enthalten und klar ersichtlich
ist, poste nicht in umständlicher Prosa umschrieben was Du denkst was im
Code steht sondern poste einfach den echten nackten Code unverfälscht.
Es könnte auch geschehen daß Du beim Kürzen aufs Wesentliche, beim
Vorbereiten eines Minimalbeispiels den Fehler selbst findest. Deshalb
ist das immer der erste Schritt den man tun sollte!
Max A. schrieb:>Button1=Button(window,text="Move",bg="white",fg="black",command=fts.mov eforward)
Du rufst hier moveforward als Variable und nicht als Funktion bzw.
Methode auf.
Max A. schrieb:> Fehlermeldung:>> AttributeError: module 'Functions' has no attribute 'moveforward'
was gibt denn dir(Function) aus ? ist in der liste moveforward dabei.
Chris M. schrieb:> Du rufst hier moveforward als Variable und nicht als Funktion bzw.> Methode auf.
Das ist vollkommen in Ordnung.
Der Fehler liegt in Code, der wieder nicht gezeigt wird.
Chris M. schrieb:> Max A. schrieb:>>Button1=Button(window,text="Move",bg="white",fg="black",command=fts.mo v
eforward)
>> Du rufst hier moveforward als Variable und nicht als Funktion bzw.> Methode auf.>> Button1 = Button(window, text="Move", bg="white", fg="black",> command=fts.moveforward())>> Probier das mal> Edit: Deine Funktion moveforward hat keinen Rückgabeparamter?!
Hab ich versucht. Hat leider nicht funktioniert. Nein meine Funktion hat
keinen Rückgabewert.
Brezel schrieb:> Es wird lauffähiger Code zur Analyse benötigt.> In deinem Dump fehlt Config.py.
Zum Testen braucht man noch folgende Funktion auf aus der Function.py
import gui
def check(pos,mm,pin):
P=pos+mm
if min<=P<=max:
print("ok")
else:
print("nicht ok")
pass
return pos
config.py:
min=0
max=30
#
pinsM1=[17, 4,27,22]
textfile = open("Position_Motor_1.txt","r")
pos1= textfile.read()
pos1 = int(pos1)
textfile.close()
Ich habe noch die entsprechende Textdatei angelegt. Dort hab ich z.B 12
eingetragen
imonbln schrieb:> Max A. schrieb:>> Fehlermeldung:>>>> AttributeError: module 'Functions' has no attribute 'moveforward'>> was gibt denn dir(Function) aus ? ist in der liste moveforward dabei.
Die Funktion soll erstmal überprüfen, ob die neue Position im
gewünschten Bereich liegt. Wenn ja, soll sich der Motor bewegen.
Anschließend wird die neue Position in die Textdatei geschrieben. Einen
Rückgabewert gibt es nicht. Welche Liste?
Karl schrieb:> Max A. schrieb:>> command=fts.moveforward)>> Wie wär es mit einer Klammer?> command=fts.moveforward()
Hab ich versucht. Geht leider nicht.
Eric B. schrieb:> Max A. schrieb:>> hier der relevante Quellcode:>> Nein, der relevante Code würde wie folgt aussehen:>> gui.pyfrom tkinter import *> import functions as fts>> window = Tk()> window.title("Steuerung")> window.geometry("640x480")>> button_mv = Button(window, text="Move", bg="white", fg="black",> command=fts.move_fwd)> button_mv.pack()>> window.mainloop()>> functions.pydef move_fwd():> print("Boom!")> Und das funktioniert anstandslos
Hier nochmal wie meine drei Pythondateien aufgebaut sind:
gui.py:
from tkinter import *
import Functions as fts
from config import *
window = Tk()
window.title("Steuerung")
window.geometry("640x480")
# Steigung1= Steigung
Label1 = Label(window, text="Motor, Position:", font=("Arial", 14))
Label1.grid(column=0, row=1)
Labelp1 = Label(window, text=pos1, font=("Arial", 14))
Labelp1.grid(column=1, row=1)
Spin1 = Spinbox(window, from_=-20, to=20, width=5)
Spin1.grid(column=2,row=1)
Button1 = Button(window, text="Move", bg="white", fg="black",
command=fts.moveforward)
Button1.grid(column=3, row=1)
Button1.pack()
window.mainloop()
Functions.py:
from config import *
import gui
import sys
import os
import re
def check(pos,mm,pin):
P=pos+mm
if min<=P<=max:
print("ok")
else:
print("nicht ok")
pass
return pos
def moveforward():
textfile = open("Position_Motor_1.txt","r")
pos1= textfile.read()
pos1 = int(pos1)
print('Motor 1 bewegt sich',spin1.get(), 'mm')
mm=int(spin1.get())
pos= check(pos1, mm,pinsM1)
Labelp1.configure(text=pos)
textfile = open('Position_Motor_1.txt','w')
pos = str(pos)
textfile.write(pos)
textfile.close()
pos = int(pos)
config.py:
min=0
max=30
pinsM1=[17, 4,27,22]
textfile = open("Position_Motor_1.txt","r")
pos1= textfile.read()
pos1 = int(pos1)
textfile.close()
Fehlermeldung:
AttributeError: module 'Functions' has no attribute 'moveforward'
Wenn ich die Funktion in die gui.py kopiere, funktioniert es problemlos.
Nur wenn diese Funktion in einer anderen Pythondatei ist dann
funktioniert es nicht. Bei meinem Programm will ich die Oberfläche,
Funktionen und Variablen getrennt haben.
Max A. schrieb:>gui.py:>>from tkinter import *>import Functions as fts> Functions.py:>> from config import *> import gui
Du hast eine Zirkelbezug gebaut!
Karl schrieb:> Max A. schrieb:>>gui.py:>>>>from tkinter import *>>import Functions as fts>>> Functions.py:>>>> from config import *>> import gui>> Du hast eine Zirkelbezug gebaut!
Stimmt.
importiere alle Module in der Config und greife von deinen Dateien dann
über die Config drauf zu. Dafür ist sie da.
Beispiel config.py:
Chris M. schrieb:> importiere alle Module in der Config und greife von deinen Dateien dann> über die Config drauf zu. Dafür ist sie da.
Nein struckturiere deine Module, sodass man so einen Quatsch nicht
braucht, oder mach gleich ordentlich objektorientiert!
Karl schrieb:> Max A. schrieb:>>gui.py:>>>>from tkinter import *>>import Functions as fts>>> Functions.py:>>>> from config import *>> import gui>> Du hast eine Zirkelbezug gebaut!
Auf die gui muss ich nur zugreifen, da ich den Wert von der Spinbox
brauche
Karl schrieb:> Dann übergibt der Funktion die Spinbox oder den Wert!
Ganz so einfach wirds nicht gehen, Python kann erstmal kein currying, da
braucht er noch ne Hilfsfunktion die eine neue Funktion erzeugt und
zurückliefert.
Bernd K. schrieb:> Ganz so einfach wirds nicht gehen, Python kann erstmal kein currying, da> braucht er noch ne Hilfsfunktion die eine neue Funktion erzeugt und> zurückliefert.
Ich verstehe dein Problem nicht. Natürlich geht das, so wie ich es
geschrieben habe. Man kann in Python einer Funktion einfach ein Objekt
übergeben.
Und in Python gibt es noch lambda, was man hier aber nicht braucht.
Abgesehen davon hat der Zugriff auf die Spinbox eigentlich nichts in der
Funktion zu suchen, es würde reichen, wenn man den Wert übergibt. Auch
der Dateiname hat in der Funktion nichts zu suchen, sonst schreibt man
für jeden Motor eine Funktion.
Karl schrieb:> Ich verstehe dein Problem nicht. Natürlich geht das, so wie ich es> geschrieben habe. Man kann in Python einer Funktion einfach ein Objekt> übergeben.
Du sollst dem Button-Konstruktor aber eine Funktion übergeben und dabei
nicht die selbige schon aufrufen. Also mußt Du die Funktion mitsamt
ihrem Argument aber im noch nicht aufgerufenen Zustand übergeben! Also
eine Funktion die den Parameter schon hat aber noch nicht aufgerufen
wurde! So daß der Button sie später aufrufen kann!
Der Fachbegriff dafür ist Currying und partielle Funktionsauswertung
Google das! Und dann probier Deinen eigenen naiven Vorschlag ruhig
einfach mal aus und schau Dir die Fehlermeldungen an!
Ich hab Deinen Code mal entsprechend geändert so daß er die
teilangewendete Funktion zurückliefert, so könnts gehen:
1
def moveforward_curry(MySpin):
2
def moveforward():
3
textfile = open("Position_Motor_1.txt","r")
4
pos1= textfile.read()
5
pos1 = int(pos1)
6
print('Motor 1 bewegt sich',MySpin.get(), 'mm')
7
mm=int(MySpin.get())
8
pos= check(pos1, mm,pinsM1)
9
Labelp1.configure(text=pos)
10
textfile = open('Position_Motor_1.txt','w')
11
pos = str(pos)
12
textfile.write(pos)
13
textfile.close()
14
pos = int(pos)
15
return moveforward
Das liefert eine Closure zurück die den korrekten Wert für MySpin
bereits enthält aber selbst noch nicht aufgerufen wurde. Diese kann dann
wiederum an den Button übergeben werden so daß er sie später bei Klick
aufrufen kann.
Und schon hast Du wieder was gelernt, man lernt nie aus! Das macht die
Sache interessant!
Dann muss moveforward ein Argument akzeptieren (so wie in Deinem
Beispiel) und partial() liefert dann eine neue Funktion zurück bei der
das erste Argument schon ausgefüllt ist und die man dann später ohne
Argumente aufrufen kann.
Bernd K. schrieb:> Du sollst dem Button-Konstruktor aber eine Funktion übergeben und dabei> nicht die selbige schon aufrufen.
Jetzt verstehe was du von mir willst. Ich habe mir das Programm nicht
wirklich angeschaut, dass command= eine Funktion sein soll habe ich
nicht angenommen, ich dacht command soll ein (Rückgabe-)Wert sein.
Bernd K. schrieb:> Und dann probier Deinen eigenen naiven Vorschlag ruhig> einfach mal aus und schau Dir die Fehlermeldungen an!
War nicht mein Vorschlag, wie gesagt hätte ich das ganze Programm
komplett anders strukturiert. Ich hätte wahrscheinlich irgendein Objekt
dem man beim Initialisieren die Spinbox mitgibt und dann halt eine
Funktion, die man einfach aufrufen kann wenn der Button gedrückt wird.