Werteübergabe SIGNAL/SLOT

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Hi,
also ich habe mir ein ganz einfaches Fenster mit einem Button erstellt.

Code: Alles auswählen

class Klasse_1(QtGui.QMainWindow):  #QtGui.QWidget
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(200, 300, 400, 300)                #Fenstergeometrie
 

        self.button = QtGui.QPushButton("Person Klasse", self)  # Buttons's
        self.button.move(30, 50)

        #self.button1.clicked.connect(self.action)
        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.action)   # so funktiniert es   
        #self.connect(self.button, QtCore.SIGNAL('clicked()'), self.action(13)) # so möchte ich es !!!!!!!!!!!
           

    def action(self): #so gehts
        print("hallo")  # so gehts

    def action(self, int q):  # So mag ichs aber machen -> Gleiche Funktion allerdings mit Werteübergabe
        print(q)  # so gehts nicht.... ich möchte das dann "13" ausgegeben wird
Soweit paßt es. Jetzt möchte ich das der Funktion self.action ein Wert übergeben werden kann, also self.action(13) und 13 dann in der "Konsole" ausgegeben wird.
Das geht aber nicht. Hab Riverbank nach einem passenden Beispiel gesucht aber nichts gefunden.
Wie baue ich eine Werteübergabe ein damit das funktioniert? Ich mach das hier nur beispielhaft damit ich ein Problem in einem großen Programm lösen kann....
Habt ihr eine Lösung? Oder könnt ihr ein Beispiel schreiben wie das geht?
Müßte recht simple sein aber ich weiß nicht wie die Syntax aussehen muss....

mfg Cascoin
deets

Was spricht dagegen, einfach einen keyword-parameter zu nehmen? Oder aus der action eine "worker"-methode aufzurufen?

Code: Alles auswählen

def action(self, parameter=13):
     ...

# oder
def action(self):
      self.work(13)

BlackJack

Ansonsten ist `functools.partial()` nützlich um aus Funktionen und Argumenten neue Funktionen zu erstellen.
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Ja ich möchte ja NICHT das die Methode "action" als Parameter immer 13 bekommt.
Der Methode "action" sollen beliebige Parameter (beispielsweise über ein Input) übergeben werden.
Das hab ich nur der Einfachheit halber so verkürzt. Mir ist klar das ich wenig Ahnung in Qt und Python habe aber das Problem muss ich lösen.
Jetzt hab ich weiter gesucht und das Thema "QT: Selbst definierte Signale" / http://www.python-forum.de/viewtopic.php?f=24&t=15386 gefunden.

So wie ichs in diesem Thema verstanden habe kann ich keine SLOT-Funktion verwenden mit Klammern weil sonst nicht die Funktion verwendet wird sondern ihr Rückgabewert.
Das Programm das ich allerdings korrigieren soll macht genau das und bis zur Qt Version 4.5 hat das anscheinend auch funktioniert.

Hier der Code wie das connected wurde:

Code: Alles auswählen

self.connect(self.Modul,QtCore.SIGNAL("modif_Signal(eigenes_Datenformat)"),self.Modul,QtCore.SLOT("update_Action(eigenes_Datenformat)"))
Als Beispiel hier mal der Code wie die Funktion definiert wurde:

Code: Alles auswählen

	@QtCore.pyqtSignature("update_Action(eigenes_Datenformat)")    #eigenes_Datenformat ist ein eigenes def. Datenformat
	def update_Action(self,eigenes_Datenformat):
Also so HAT das bis Version Qt4.5 funktioniert.... Jetzt gehts nicht mehr. Hier ist aber bei der SLOT-Methode eine Funktion mit Übergabeparameter hinterlegt.

Was ist hieran falsch????

Bzw. ich will das anhand eines sehr simplen beispiels nachbauen. Damit ich des Problem verstehe und das eigenltich Problem lösen kann....

Danke für die Hilfe.

Cascoin
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Generell: Wenn der Parameter nicht "konstant" ist, sondern aus einem anderen teil der GUI stammt, wieso musst Du diesen dem Slot übergeben? Lies ihn doch im Slot einfach aus ;-)

Generell: Du solltest Dir mal die neue Syntax für Signals und Slots angucken:

Code: Alles auswählen

self.button.clicked.connect(self.function)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Cascoin: Da ist keine Funktion mit Parameter als SLOT hinterlegt. Das ist eine Zeichenkette die eine Funktionssignatur beschreibt. Das ist doch etwas anderes als Du im ersten Beitrag machen willst. Dort willst Du eine Konstante als Argument übergeben lassen. Das Beispiel im letzten Beitrag übergibt dort was immer auch das Signal als Wert sendet an den Slot.
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

kurz zu deiner 2.ten Anmerkung: Ich hab mir den neuen Style von connect schon angeguckt, aber in dem von mir zu korrigierenden Programm ist er so noch nicht aktuallisiert. Werd ich bei Gelegenheit machen...

Zum wichtigeren Punkt: Ich kann das ganze Programm nicht ändern sondern muss nur die Fehler korrigieren. Und in dem Programm ist es eben so wie oben beschrieben gecodet. Das komplette Programm ist grob 15MB groß deswegen wäre auch der Änderungsaufwand enorm... Und da ich ein Anfänger bin werd ich wohl mehr zerstören als wiederherstellen wenn ich probiere alles zu ändern.
Außerdem hat es ja funktiniert.... Gibts doch net das das jetzt auf einmal nicht mehr funktionieren soll....

mfg Cascoin
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

@BlackJack

Ganz genau. Ich möchte auch in meinem obrigen Beispiel keine Konstante, sondern eine Variable an die Slotfunktion übergeben. Der Einfachheit halber hab ich aus der Variablen eine Konstante gemacht. Dachte so wärs leichter verständlich aber hat wohl mehr verwirrt.

Wie auch immer, ich schaffe es nicht oder ich weiß einfach nicht wie die Syntax aussieht wenn ich ein Signal emittiere (z.B. clicked) dann eine gewünschte Variable gesendet wird, die dann von der Slot-Funktion aufgenommen und verarbeitet wird.

Hat da jemand eine Idee? -> Wie gesagt, ich möchte mir so ein Bsp aufbauen wodurch ich dann auf das Problem im eigenltich Programm näherkomme.

mfg
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Ja aber bei mir funktioniert es trotzdem nicht.... Ich bekomms irgendwie gar net hin.....

Guckt mal:

Des soll mein SLOT sein:

Code: Alles auswählen

    @pyqtSlot(int)
    def action(self, i):
        print("aserhg" + i)
Des mein Connect:

Code: Alles auswählen

self.connect(self.button, QtCore.SIGNAL('clicked()'), self.action(int))
Also wie ersichtlich klicke ich auf einen Button und möchte etwas ausgegeben bekommen.....
Also das ein Button auf eine Funktion reagiert bekomm ich hin.... Nur möchte ich der SLOT-Funktion eben einen Parameter übergeben.... So wie es oben beschrieben ist.... Ich weiß das es auch anderes funktionieren würde aber so ist numal die Aufgabenstellung.... Nochmal genauer das es klar ist: Der Parameter der übergeben werden soll muss in dem connect stehen....so z.B. -> self.connect(self.button, QtCore.SIGNAL('clicked()'), self.action(int))

Aber es funzt net... Wenn ihr den/die Fehler sieht bitte melden

mfg cascoin
BlackJack

@Cascoin: Das was Du da stehen hast ruft zuerst `self.action()` mit dem Argument `int` auf, also mit dem eingebauten Typ `int` und übergibt dann den *Rückgabewert* dieses Aufrufs als drittes Argument an den `self.connect()`-Aufruf. Das passiert also zu dem Zeitpunkt wo die `connect()`-Methode aufgerufen wird. Das war auch noch nie anders — also auch bei PyQt 4.5 war das schon so, weil das einfach die Semantik von Aufrufen ist. Elemente einer Argumentliste für einen Aufruf werden ausgewertet bevor der Aufruf passiert. Da kann keine externe Bibliothek etwas dran ändern.

Wenn ich das Problem richtig verstanden habe, dann ist `functools.partial()` die Lösung. Das habe ich ziemlich weit oben in diesem Thread schon einmal gesagt.
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Hi, ich wollt nur nochmal kurz mein Problem darstellen (so simple wie möglich)

Ich möchte ein button mit einem Slot/Funktion verbinden.

Daszu erstelle ich mir eine Funktion die ich mit connect mit dem Button verbinde....
Jetzt muß die SLOT-Funktion so geschrieben sein, dass ihr ein Wert übergeben werden kann. Ungefähr so:

Code: Alles auswählen

self.button.clicked.connect(self.action(self.i))
die Variable i wurde erstellt....
die Funktion action gibt es auch....

Code: Alles auswählen

    @pyqtSlot(int)  
    def action(self, arg1):
        print(arg1)
Aber es kommt ein Fehler und ich weiß echt nicht was da falsch sein könnte....
Wenn jemand pyqt gut kann dann wär ich über jeden tipp total dankbar
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

hmmm,
ja ich hab keine Ahnung wie das bei Version 4.5 war.
Ich muss das bsp. ja nur aufbauen um einen großes Programm wieder zum laufen zu bringen das seit einem Update nicht mehr funktiniert.... Und der hat das eben so "ähnlich" gemacht wie ich unten beschrieben hab.... Bei dem steht nirgends im code functools.partial()...... Deswegen mag ich das auch nicht verwenden...

so sieht der connect Befehl von dem großen Prg. aus:

Code: Alles auswählen

self.connect(self.guiQueueHandler,QtCore.SIGNAL("updateVisionData(VisionData)"),self.imageGuiInterface,QtCore.SLOT("updateVisionData(VisionData)"))
das wäre der Slot:

Code: Alles auswählen

	@QtCore.pyqtSignature("updateVisionData(VisionData)")	# signal from GuiQueueHandler
	def updateVisionData(self,visionData):	
        .......#code
und analog dazu brauche ich ein einfaches bsp. damit ich lern wie das ganze funktioniert...
BlackJack

@Cascoin: Derjenige hat dort ein Signal das einen Wert emittiert mit einem Slot verbunden das diesen Wert entgegen nimmt. Du willst auf ein Signal einen Slot mit einem Konstanten Wert aufrufen. Das ist etwas völlig anderes. Und muss dementsprechend auch anders gelöst werden. Mit `functools.partial()`, einer ``lambda``-Funktion, einem Closure, oder einer Indirektion wie sie deets ganz am Anfang vorgeschlagen hat.
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Ok,
sagmal wärs vielleicht möglich wenn mir hier irgendjemand ein Mini-Bsp schreiben könnte wie ich das Problem lösen könnte? Ist nicht eure Aufgabe aber ich dachte ich frag einfach mal frech.

Also ein einfachs PyQt Beispiel wo man ein Signal mit irgendeinem Wert übergibt dieses Signal von einem Slot empfangen wird und dann einfach mit print etwas ausgibt....
Wichtig wäre halt nur das es irgendwie so aussieht:

Code: Alles auswählen

self.connect(self.guiQueueHandler,QtCore.SIGNAL("sendeSignal(Integerwert)"),self,QtCore.SLOT("print(Integerwert)"))
Wäre cool aber wenns net geht auch nicht tragisch.... Ich komm hiermit eh nicht wirklich klar deswegen sollt ich sowieso vielleicht was anderes machen....

mfg Cascoin
BlackJack

@Cascoin: Welches Problem denn? Ich sehe da keines. Das hier funktioniert mit PyQt 4.7.2:

Code: Alles auswählen

from PyQt4.QtCore import *


class Test(QObject):
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.connect(self, SIGNAL('send(int)'), self, SLOT('receiver(int)'))
    
    def send(self, value):
        self.emit(SIGNAL('send(int)'), value)
    
    @pyqtSignature('receiver(int)')
    def receiver(self, value):
        print value


def main():
    test = Test()
    test.send(42)


if __name__ == '__main__':
    main()
Also sehe ich jetzt erst einmal keinen Grund warum das in dem Programm das Du wieder zum laufen bekommen sollst, nicht funktioniert.
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Hey BlackJack,

ich danke dir !!!!! und probier jetzt deinen Code weiter umzusetzen... könnte sein das ich mich nochmal melde....wenns sein muss..
Danke bis dahin
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Hallo,
also der Code oben paßt ziemlich gut zu dem was ich jetzt dann "korrigieren" soll.... Also es ist vergleichbar.

Der Unterschied ist allerdings das ich einen eigenen Datentyp als Signal "emitieren" möchte, was allerdings nicht funktioniert....
Also als Test hab ich in dem obrigen Code einfach überall wo "int" steht mal
"float" und "QString" geschrieben. Dann muss man unten in dem main()-Funktionsaufruf noch das Argument anpassen, also für QString dann

Code: Alles auswählen

test.send("haerghaer")
und dann wird mir genau das ausgegeben was ich möchte....

Code: Alles auswählen

self.emit(SIGNAL('send(int)'), value) 
---> Hier wird ja das Signal das gesendet wird mit der Variable "value" "übergeben.

Ich muss das aber so hinbekommen, dass ich eine eigens def. Klasse übergebe.
Die sieht beispielsweise so aus und sowas in der Art möchte ich als Signal versenden:

Code: Alles auswählen

class  Student:
    def __init__(self, name, Studium, Nr):
        self.name = name
        self.Fach = Studium
        self.Matrikel = Nr
Jetzt dachte ich - was allerdings falsch ist - das ich wieder nur überall wo "int" steht einfach "Klasse.Student" reinschreibe und dann wird die komplette Klasse gesendet und empfangen....

Code: Alles auswählen

def main():
    Andi = Klasse.Student("Andi","MB",123413)
    test = Verbindung()
    test.send(Andi)
So hätte ich dann meine main() Funktion umgeschrieben und dann der "send-Funktion" einfach die Instanz der Klasse Student übergeben.....

Aber dann kommt die Fehlermeldung das der Interpreter diese pyqt Signatur nicht unterstützt..... Also das der Interpreter dann bei der receiver-Funktion nicht weiterläuft weil er nicht versteht was er unter print value überhaupt alles ausgeben soll ist verständlich.... Ich glaub nur einfach das die Syntax zum übergeben einer Klasse falsch ist....

Was nun? Wie kann ich das hinbekommen?

danke soweit....bis dennsen

Cascoin
BlackJack

@Cascoin: Es wäre hilfreich wenn Du den Quelltext und die genaue Fehlermeldung zeigen würdest. Bei mir funktioniert es nämlich mit einer Klasse. Hast Du wirklich überall die Signatur angepasst?

Code: Alles auswählen

from PyQt4.QtCore import *
import klasse


class Test(QObject):
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.connect(
            self,
            SIGNAL('send(Student)'),
            self,
            SLOT('receiver(Student)')
        )
    
    def send(self, value):
        self.emit(SIGNAL('send(Student)'), value)
    
    @pyqtSignature('receiver(Student)')
    def receiver(self, value):
        print value


def main():
    test = Test()
    test.send(klasse.Student('Andi', 'MB', 12345))


if __name__ == '__main__':
    main()
Wie man sieht habe ich in den Signaturzeichenketten nur 'Student' stehen. Aber es funktioniert auch wenn ich da überall 'klasse.Student' schreibe.
Cascoin
User
Beiträge: 24
Registriert: Dienstag 17. Mai 2011, 18:12

Hallo,
danke für die schnelle Antwort...
Ja ich habe alles angepaßt....

Hab jetzt grad mal das was du gepostet hast in eine .py Datei abgespeichert und in dem gleichen Ordner dann das Modul Klasse.py
Habs es aufgerufen aber es kommt dieser Fehler...

-----------------------Fehlermeldung--------------------------------
Traceback (most recent call last):
File "C:\Dokumente und Einstellungen\faankesk\Desktop\test\test.py", line 5, in <module>
class Test(QObject):
File "C:\Dokumente und Einstellungen\faankesk\Desktop\test\test.py", line 18, in Test
@pyqtSignature('receiver(Student)')
TypeError: C++ type 'Student' is not supported as a pyqtSlot signature argument type
-------------------------------------------------------


Kann das an einer PyQt Version liegen? Genau der gleiche Fehler kommt in dem Programm das ich korrigieren soll....
Anscheinend hat irgend ein Hiwi ein Qt-Versionsupdate und seit dem läuft es nicht mehr.... Davor gings auch....

Ich poste nochmal meinen Code aber eigneltich ist er von dir kopiert:

Name der Datei : ../test.py

Code: Alles auswählen

from PyQt4.QtCore import *
import klasse


class Test(QObject):
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.connect(self,SIGNAL('send(Student)'),self,SLOT('receiver(Student)'))
   
    def send(self, value):
        self.emit(SIGNAL('send(Student)'), value)
   
    @pyqtSignature('receiver(Student)')
    def receiver(self, value):
        print value


def main():
    test = Test()
    test.send(klasse.Student('Andi', 'MB', 12345))


if __name__ == '__main__':
    main()
Name des Modul's Klasse : ../klasse.py

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-


from PyQt4 import QtGui


class  Student:
    def __init__(self, name, Studium, Nr):
        self.name = name
        self.Fach = Studium
        self.Matrikel = Nr

Aber wie gesagt, das funkioniert nicht.... Fehlermeldung steht ja oben

Wollt ich noch kurz anmerken falls es hilft:
Und zwar wollte ich die Version angeben mit der ich arbeite:
In meinem Startmenü wo ich Verknüpfungen zum QtDesigner etc. habe steht das ich mit Version "PyQt GPL v4.8.2 for Python v2.7 (x86)" arbeite.....
Wenn ich allerdings im Qt Designer auf Hilfe->Über Qt klicke, steht das ich mit Version Qt4.71 arbeite....
Widerspricht sich zwar aber anders konnt ich nicht rausfinden welche Version installiert ist...
Dachte das es euch vielleicht hilf...
Antworten