Funktion nach Button connect funktioniert nicht...

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Guten Morgen!
ich bin grade etwas am verzweifeln. Irgendwo habe ich einen gravierenden Denkfehler eingebaut aber verstehe nicht so recht was ich falsch mache. Vielleicht kann mir jemand auf die Sprünge helfen?

[Codebox=python ]

import sys
from PyQt5.QtWidgets import *
from PyQt5.uic import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

def window():
app = QApplication(sys.argv)
try:
widget = loadUi('lm_tauch.ui')
except:
print("Fehler beim Laden der Datei")
widget.show()
widget.btnCalc.clicked.connect(berechnung)
sys.exit(app.exec_())

def berechnung():
print ("berechnung geöffnet")
try:
widget.labelMittelwert.setText('nix')
except:
print ("hat nicht geklappt")

if __name__ == '__main__':
window()




[/Codebox]

Danke schon mal..
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Für GUI-Programmmierung muss man normalerweise schon objektorientiert arbeiten. Im konkreten hieße das eine Klasse App einzuführen, in welcher du

- widget als self.widget dauerhaft aufbewahrst.
- berechnen als Methode definierst, und darin dann eben auf self.widget zugreifst.

Alternativ kann man in einem so simplen Fall auch mit functools partial arbeiten. Auf Dauer wirst du aber um OO nicht rumkommen.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Vielen Dank für die Antwort. Im Moment geht es mir tatsächlich "nur" darum, mir ein kleines Tool zu schreiben, welches mir ein paar einfache Dinge abnimmt, also eine Email auf Knopfdruck verschicken, eine typische Berechnung mal eben durchzuführen usw. Da ich nicht wirklich ausschließen kann, dass es mal etwas größeres wird, werde ich also versuchen es von Anfang an richtig zu machen.
Andererseits verwundert es mich, dass es nicht so einfach möglich ist wie ich es vor hatte. Ich bin davon ausgegangen, dass die eingebundene ui und die Funktionalitäten überdauern.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@tt-web: mit Deiner "Fehlerbehandlung" machst Du es ja auch unmöglich, Fehler zu finden. Niemals nackte excepts benutzen. Nur die Fehler abfangen, von denen man auch die Ursache kennt. ein "hat nicht geklappt" sagt nur, da hat was nicht geklappt.

Wenn Du die try-excepts ersatzlos gelöscht hast, kannst Du Dir die Fehlermeldung die kommt mal anschauen und überlegen, warum sie erscheint.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Nur zu meiner "verteidigung" :D Also ich hatte die Try/Except blöcke ursprünglich nicht im Quellcode, allerdings führte der auftretende Fehler leider nicht zu einer Fehlermeldung sondern zu einem Absturz von IDLE.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du solltest GUI Programme nicht in IDLE starten. Das ist selber ein GUI Programm und kommt dann mit mehreren Mainloops durcheinander. Dazu immer eine Shell benutzen.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Okay... also das ist mir komplett neu :?

Danke für den Hinweis :/
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Also ich habe das alles mal so umgesetzt wie ich es für richtig halte ---> Bedeutet es kann trotz Funktionalität hässlich sein und nur zufällig funktionieren (ich bin durchaus selbstkritisch..)

Könnt ihr bitte mal drüber schauen, ob ich grobe Fehler gemacht habe oder ungünstig gearbeitet habe, was mich später dann in irgendwelche Probleme rennen lässt? Das wäre echt super. Danke schon mal..

import sys
from PyQt5.QtWidgets import *
from PyQt5.uic import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *



# Erstellen einer Klasse abgeleitet aus QMainWindow
class test(QMainWindow):
#Erstellen eines Objektes der Klasse
def __init__(self):
super().__init__()
self.ui = loadUi('lm_tauch.ui')
self.ui.show()
# Aufrufen von dem was nach dem Knopfdruck passieren soll
self.ui.btnCalc.clicked.connect(self.berechnung)

def berechnung(self):
print ("berechnung geöffnet")
self.ui.labelMittelwert.setText('nix')



# Wird das Programm direkt aufgerufen wird geprüft ob die mainfunktion vorhanden
# ist. Ansonsten wird dieser Block ausgeführt

if __name__ == '__main__':
# Erstellen eines QApplication Objekts, das in 'app' gespeichert wird
app = QApplication(sys.argv)

# Erstellen eines Objektes abgeleitet aus der Klasse test
myapp=test()

# Liefert den Programmexitcode
sys.exit(app.exec_())
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte lern den Umgang mit dem Code formatting hier. Das ist mit der Vorschaufunktion schnell getan.

Ansonsten ist der Code soweit ok, ein Problem aber ist das laden der ui Datei. Dabei gehts du implizit davon aus, dass das current working directory stimmt. Das tut es aber nicht automatisch. Besser ist, sich Anhand der Variable __name__ mittels os.path.dirname und os.path.join dahin zu hangeln. Das klappt dann immer.

Oh, und du solltest eine echte main Funktion definieren, die du dann nur noch aufrufst. Denn so legts du ungewollt globale Variablen an.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

danke für die Antwort... verdammt ja :D Da hab ich es so schön kommentiert und das wichtigste dann nicht getan :D

Ich muss gestehen das mit dem Codeformating ... ja du hast recht... Entschuldigung, ich war da schlampig und hab dir/euch unabsichtlich mehr Arbeit beim Lesen des Codes gemacht.

Vielen Dank für die großartige Hilfe!
LG Thomas
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Guten Morgen,
jetzt hab ich am Wochenende mal etwas ausprobiert und bin direkt ins nächste Problem reingerannt. Ich wollte dass auf druck des Knopfes sich ein Dialogfeld öffnet:

Code: Alles auswählen

# Erstellen einer Klasse abgeleitet aus QMainWindow
class test(QMainWindow):
    #Erstellen eines Objektes der Klasse
    def __init__(self):
        super().__init__()
        self.lm = loadUi('lm_tauch.ui')
        self.lm.show()
        # Aufrufen von dem was nach dem Knopfdruck passieren soll
        self.lm.actionTauch.triggered.connect(self.Tauch)
    
    def Tauch(self):
        print("run tauchapp")
        tauchapp()
   
# Wird das Programm direkt aufgerufen wird geprüft ob die mainfunktion vorhanden
# ist. Ansonsten wird dieser Block ausgeführt
            
if __name__ == '__main__':
    # Erstellen eines QApplication Objekts, das in 'app' gespeichert wird
    app = QApplication(sys.argv)
    
    # Erstellen eines Objektes abgeleitet aus der Klasse test
    myapp=test()

    # Liefert den Programmexitcode
    sys.exit(app.exec_())
Zur Information: Den Knopf habe ich in diesem Beispiel durch einen Menüeintrag ersetzt, was aber letztendlich egal sein sollte. Ich sehe, dass sowohl Tauch() als auch tauchapp() aufgerufen wird. Soweit so gut.
In "Tauchapp" habe ich im Prinzip den Code kopiert nur mit dem Unterschied, dass ich eine andere ui-datei lade.
Mir ist aufgefallen, dass es prinzipiell funktioniert, aber der neue Dialog nur kurz angezeigt wird (zumindest ist für Millisekunden ein Fenster zu sehen) und danach gleich wieder geschlossen wird.
Mir ist nicht ganz klar, warum das so passiert und ich würde es gerne verstehen.

Danke schon mal für die Unterstützung, ihr habt mich bisher echt weiter gebracht.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@tt-web: Deine Kommentare sind zum größten Teil überflüssig, weil sie nur das beschreiben, was in der nächsten Zeile sowieso steht, im Falle von Zeile 15f sogar einfach nur falsch, da es in Python keine spezielle mainfunktion gibt und somit auch nicht darauf geprüft werden kann. Auch wenn Qt eine andere Namenskonvention hat wie Python, sollte man sich wenigstens an eine von beiden halten, also Klassen werden groß geschrieben und Methoden klein_mit_unterstrich oder kleinUndGroß. Keine Methode darf in einem GUI-Programm blockieren, wenn Du also versuchst eine zweite QApplication mit app.exec_ aufzurufen, wird das schief gehen. Zeig den Dialog mit show an und lass den Rest die Hauptschleife machen.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Guten Morgen,
naja mag sein, dass sie überflüssig sind, aber wenn ich das Projekt liegen lasse und mich in nem Jahr wieder damit beschäftige dann freue ich mich wenn ich auch an einfache Dinge erinnert werde. Den Hinweis mit der Namenskonvention werde ich sofort umsetzen, das ist einfach etwas was ich mir noch angewöhnen muss.

Kannst du mir denn erklären wie die Zeile if __name__ == '__main__': sonst zu interpretieren ist?

Danke schön
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@tt-web: Kommentare geben sinnvollerweise Zusatzinformationen und beschreiben offensichtliches nicht erneut. Die Zeile

Code: Alles auswählen

if __name__ == '__main__':
ergibt True, wenn das Modul direkt ausgeführt, hingegen False, wenn es per import eingebunden wird. In letzterem Fall enthält __name__ den Namen des Moduls.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Danke für die Erklärung. Ich blicke so langsam deutlich besser durch. Jetzt noch eine Frage: Ich bekomme mein zweites Formular angezeigt, kann es aber nicht benutzen.

Code: Alles auswählen

class Tauch(QDialog):
    
    def tauch(self):
        self.ui = loadUi('ui/lm_tauch.ui')
        self.ui.show()
        self.ui.btnCalc.clicked.connect(self.berechnung)
   
   def berechnung(self)
        pass
Warum ist es mir nicht möglich auf den Button zuzugreifen?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@tt-web: `Tauch` hat kein __init__. `show` sollte nur von außen aufgerufen werden und dann auch nicht auf der ui-Komponente. Wie sieht denn Dein komplettes Programm aus?
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Hauptfenster:

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import *
from PyQt5.uic import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from modulTauch import *
   
class test(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.lm = loadUi('ui/test.ui')
        self.lm.show()
        self.lm.actionTauch.triggered.connect(self.tauch)
        

    def tauch(self):
        Tauchapp.tauchapp(self)

 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    myapp=test()
    sys.exit(app.exec_())
Aufzurufender Dialog aus modulTauch:

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import *
from PyQt5.uic import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from berechnungTauch import *
   


class Tauchapp(QDialog):
    
    def tauchapp(self):
        print("tauchapp")
        self.ui = loadUi('ui/lm_tauch.ui')
        self.ui.show()
        self.ui.btnCalc.clicked.connect(self.berechnung)
       
        
    def berechnung(self):
        print("enter berechnung")
  
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@tt-web: bevor Du hier anfängst, Module kreuz und quer zu importieren, schreib fürs erste alles in eine Datei. Sollte das irgendwann einmal zu viel für eine Datei werden, trenne Dinge ab, die für sich stehen können. Vermeide *-Importe, da Du nicht kontrollieren kannst, was da alles in den Namensraum geladen wird. `show` sollte nicht in __init__ aufgerufen werden. Du erzeugst keine Tauchapp-Instanz sondern versuchst direkt Methoden aufzurufen. Das sollte Dir eigentlich mit einem TypeError um die Ohren fliegen.

Code: Alles auswählen

import sys
import os
from PyQt5.QtGui import QApplication, QDialog, QMainWindow
from PyQt5.uic import loadUi

BASEPATH = os.path.dirname(__file__)

class Tauchapp(QDialog):
    def __init__(self):
        super().__init__()
        print("tauchapp")
        loadUi(os.path.join(BASEPATH, 'ui/lm_tauch.ui'), self)
        self.btnCalc.clicked.connect(self.berechnung)
       
    def berechnung(self):
        print("enter berechnung")   

class Test(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        loadUi(os.path.join(BASEPATH, 'ui/test.ui'), self)
        self.actionTauch.triggered.connect(self.tauch)
 
    def tauch(self):
        Tauchapp().show()
 
def main():
    app = QApplication(sys.argv)
    myapp = Test()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Antworten