Aktion beim anzeigen des Fensters ausführen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Pacnos
User
Beiträge: 19
Registriert: Sonntag 8. Mai 2011, 20:17

Hallo zusammen,

ich fange gerade an mich ein bischen in PyQT einzuarbeiten und bin jetzt auf ein kleines Problem gestossen.

Ich habe mir einen QDialog gebastelt und möchte sobald der aufgerufen wird eine Felder in dem Dialog mit inhalt füllen. Ich kann Sie nicht direkt in der __init__ methode definieren, da ich diese fürher aufrufe als ich die benötigten werte habe. Was wäre denn da eine saubere Lösung?

Schonmal danke für eure Hilfe ....
deets

Na, warum denn nicht da, wo du den Dialog zur Anzeige bringst?
Pacnos
User
Beiträge: 19
Registriert: Sonntag 8. Mai 2011, 20:17

Also müsste ich die exec_() funktion überschreiben?

Weil von außen kann ich ja nicht auf die privaten elemtne zugreifen ?
deets

Was für private Elemente? Ich denke doch mal du willst Daren in Widgets einfügen. Und das geht doch direkt. Bzw du kannst eine Methode einführen, die das macht. Obwohl afaik Ableitung in Qt nicht mehr üblich ist.

Überladen würde ich nix. Bringt doch auch nix, du müsstest doch die Signatur ändern, um die Daten entgegenzunehmen.
Pacnos
User
Beiträge: 19
Registriert: Sonntag 8. Mai 2011, 20:17

Ok daran hatte ich überhaupt nicht gedacht. Danke für deine Hilfe, ich hatte es vorher so geläst, weiß aber nicht ob das ein wirklich sauberer Stil ist ..:

Code: Alles auswählen

def exec_(self):
        '''
        Overrides the Exec Function
        '''       
        #COM Port
        if Config.getValue("com") != None:
            self.spinBox_2.setValue(int(Config.getValue("com")))
       
        return QtGui.QDialog.exec_(self)

deets

Ohne mehr Kontext ist das nicht so klar mit ja oder nein zu beantworten. Mir widerstreben Ableitungen bei GUIs grundsaetzlich, ausser, ich baue wirklich neue Widgets - nicht einfach nur komplexe Widget-Hierarchien.

Wichtiger waere denke ich, dass du versuchst mit vernuenftigen Namen zu arbeiten, nich "spinBox_2". Denn wenn du dein Widget umstellst (rein layout-maessig), dann sollte der Code, der die Werte einfuellt davon nicht betroffen sein. Und so durchnumerierte Dinger sind da denke ich problematisch, und ich weiss jetzt auch nicht aus dem Kopf, ob die so flach benamt zur Verfuengung stehen, oder nicht in Unterhierarichien wandern.

Dann waere naemlich sowas wie "findChild('com_port_number')" besser.
Pacnos
User
Beiträge: 19
Registriert: Sonntag 8. Mai 2011, 20:17

Ok, dann erstmal vielen Dank für die Tipps.

Das mit den Namen ist mir schon bewust, bei kleineren Dialog mache ich mir aber meist keine gedanken drüber, weil ich da normaler weise nicht viel umstellen muss ...
deets

Pacnos hat geschrieben:Das mit den Namen ist mir schon bewust, bei kleineren Dialog mache ich mir aber meist keine gedanken drüber, weil ich da normaler weise nicht viel umstellen muss ...
Dafuer das du dir keine Gedanken machst, machst du dir ja schon ne Menge Gedanken, wie es denn richtig waere - sonst haettest du ja nicht gefragt ;)

Und es ist eine der ueblichen "jaaaa, *eigentlich* mache ich das ja auch ganz anders, aber hier war ne Ausnahme"-Argumentationen, wie man sie zB auch beim (fehlen) von Tests findet. Meiner Erfahrung nach gewoehnt man sich genau *eine* Art zu arbeiten an - umzuschalten von "maessigem Code" auf "guten Code" bei Bedarf klappt nicht. Aus einem kleinen Dialog wird ein grosser, und ein Haufen Code, der sich damit quaelt.
Benutzeravatar
Trüffelschwein
User
Beiträge: 12
Registriert: Donnerstag 23. Juni 2011, 08:50

Pacnos hat geschrieben:Hallo zusammen,

ich fange gerade an mich ein bischen in PyQT einzuarbeiten und bin jetzt auf ein kleines Problem gestossen.

Ich habe mir einen QDialog gebastelt und möchte sobald der aufgerufen wird eine Felder in dem Dialog mit inhalt füllen. Ich kann Sie nicht direkt in der __init__ methode definieren, da ich diese fürher aufrufe als ich die benötigten werte habe. Was wäre denn da eine saubere Lösung?

Schonmal danke für eure Hilfe ....
Hallo,

ich komme mal auf deine Frage zurück und versuche einen anderen Ansatz. Muss dazu sagen, dass ich Python und PyQt-Neuling bin. Aber ich kenne aus anderen Programmiersprachen die Möglichkeit, beim Öffnen einer Form/eines Windows usw. einen Event abzufangen (so in der Art 'onLoad'-Event).
Ich habe mal in der PyQt-Doku nachgeschaut und das QEvent.spontaneous() gefunden, welches just dann ausgelöst wird, wenn das Betriebssytem das Window anzeigt (http://www.riverbankcomputing.com/stati ... ml#details:
The QShowEvent class provides an event that is sent when a widget is shown. More...

Inherits QEvent.

Methods
•__init__ (self)
•__init__ (self, QShowEvent)

--------------------------------------------------------------------------------

Detailed Description
The QShowEvent class provides an event that is sent when a widget is shown.

There are two kinds of show events: show events caused by the window system (spontaneous), and internal show events. Spontaneous (QEvent.spontaneous()) show events are sent just after the window system shows the window; they are also sent when a top-level window is redisplayed after being iconified. Internal show events are delivered just before the widget becomes visible.


--------------------------------------------------------------------------------
(Auszug aus der Doku)

Wäre das nicht genau die Situation, die du brauchst? Das Window und damit auch das zu füllende Feld ist dann ja schon da, oder?
Ich weiß nicht, ob das klappt, aber vielleicht kannst du diesen Event einfach abfangen, um dein Feld zu füllen.
Pacnos
User
Beiträge: 19
Registriert: Sonntag 8. Mai 2011, 20:17

@ Trüffelschwein genau sowas habe ich gesucht, Danke ...

@deets Zu den Namen muss ich wirklich sagen, dass es einfach die Faulheit ist ;) Der Dialog ist mit den QT - Designer erstellt und mann mit pyuic auf python "übersetzt". Daher kommt dann auch die tolle Namensgebung, da ich einfach zu faul bin das ganze mit vernueftigen Namen zu füllen ;)
deets

Pacnos hat geschrieben:@ Trüffelschwein genau sowas habe ich gesucht, Danke ...
Ich halte dieses Event fuer genauso wenig geeignet wie die exec_-Methode. Es funktioniert zwar in deinem konkreten Fall, weil du eine Dialog anzeigst, der durch eine Konfiguration mit Werten befuellt wird, und du einen Mechanismus hast, auf diese Konfiguration von beliebigem Code aus zuzugreifen.

Aber das ist ja die Ausnahme, nicht die Regel. Meistens wird der Dialog ja zB Daten aus einer Datenbank enteggennehmen, oder von anderen Objekten in deinem Programm. Und dafuer ist dieses Event voellig ungeeignet. Denn wenn es kommt, dann weisst du ja gar nicht, welche Daten du anzeigen sollst. Das kommt mit dem Event ja nicht mit...

Zusaetzlich dazu ist es "magisch". Ich bin durchaus ein grosser Freund von Events, wenn man loose coupling erreichen will. Das ist hier aber nicht gefragt. Es ist eine ganz einfach Sache:

1) Dialog erzeugen
2) Dialog mit Daten fuellen
3) Dialog anzeigen
4) Mit den Ergebnissen des Dialoges arbeiten

Und genau so sollte das auch im Code stehen. Du versteckst jetzt Schritt zwei, und wenn du (oder wer anders) das jemals wieder ansieht, fragt er sich "wie zur Hoelle kommen die Daten da rein?"

Diese Ueberlegung gewinnt noch extra an Gewicht, weil du eben eh nur nen Spezialfall abdecken kannst mit dem Event.
Benutzeravatar
Trüffelschwein
User
Beiträge: 12
Registriert: Donnerstag 23. Juni 2011, 08:50

Zusaetzlich dazu ist es "magisch". Ich bin durchaus ein grosser Freund von Events, wenn man loose coupling erreichen will. Das ist hier aber nicht gefragt. Es ist eine ganz einfach Sache:

1) Dialog erzeugen
2) Dialog mit Daten fuellen
3) Dialog anzeigen
4) Mit den Ergebnissen des Dialoges arbeiten

Und genau so sollte das auch im Code stehen. Du versteckst jetzt Schritt zwei, und wenn du (oder wer anders) das jemals wieder ansieht, fragt er sich "wie zur Hoelle kommen die Daten da rein?"
Hmmm....
Ich verstehe, was du meinst, auch wenn ich als armes Neu-Python-Würstchen erstmal googlen musst, was zu Hölle loose coupling ist. Im Prinzip sind diese "magischen" Gimmicks genau die Sachen, die verhindert haben, dass ich mit C# und der ganzen .Net-Geschichte warm werden konnte: Man hat einen Haufen Klassen, Events und der Code ist quer über die ganzen Forms verteilt... eben in Dutzenden Events, von denen jedes Objekt einen Riesen Haufen hat. Naja, wenn Pacnos ein kleines Hobbyprojekt basteln will, würde ich das für verschmerzbar halten. Die Bedeutung von Wartungsfreundlichkeit steigt meiner Meinung nach mit der Größe des Projektes... dafür aber exponentiell.

Wie dem auch sei, wenn die Event-Masche keine gute Lösung ist, dann merk ich mir das und hab wenigstens was dazugelernt. Wie man Daten direkt in ein Widget einfügt, weiß ich noch nicht, aber ich werd's schon noch rausfinden. Und Pacnos sicher auch :wink:
deets

Das einfuellen der Daten ist so simpel wie nervig ;) Man sucht sich die jeweiligen Kinder des Dialogs raus, und setzt deren Werte. ZB mit dem slot setText auf einem QTextEdit.

Mir gefaellt da der Ansatz von Cocoa unter OSX deutlich besser, da wird sogenanntes key-value-coding verwandt, und man sagt einem Widget, welches seiner Attribute (nicht nur der Text, sondern zB auch enabled/disabled-Zustaende) unter welchem "Pfad" eines Datenobjektes zu finden sind. Dann setzt man nur noch einmal das Objekt, und jede Aenderung im Widget modifiziert das Datenobjekt, und umgekehrt.

Aber das geht mit C++ auch nicht so einfach. Leider. Ich wuerde mir aber fuer PyQt da eine Abstraktion schreiben, wenn ich das naechste mal damit zu tun habe.
Benutzeravatar
Trüffelschwein
User
Beiträge: 12
Registriert: Donnerstag 23. Juni 2011, 08:50

deets hat geschrieben:Das einfuellen der Daten ist so simpel wie nervig ;) Man sucht sich die jeweiligen Kinder des Dialogs raus, und setzt deren Werte. ZB mit dem slot setText auf einem QTextEdit.
Verstehe ich nicht :K .
Wenn ich über einen Slot (setText) die Einstellungen von Dialogchilds verändere, dann brauche ich doch auch ein Signal, das der Slot abonniert hat. Damit bin ich doch wieder bei den Events, oder habe ich da was noch nicht geschnallt? Moment. Ich vermute, mein Denkfehler ist, dass ich QEvent und Signal in einen Topf schmeiße. Ich muss mal sehen, ob ich dazu was in der Doku finde.
deets

Da hast du einen Denkfehler. Ein Slot ist zwar eine Signal-Senke, wenn er das denn sein soll. Aber im Grunde ist der nichts anderes als eine simple Funktion, und genauso rufst du den auch auf.

Es als event zu verwenden macht dann Sinn, wenn man als Datenobjekt ein QObject hat, und sich darin ein String aendert. Dann kann man da ein Signal schicken, und das dann mit dem Slot verbinden.

Aber das macht nur dann Sinn, wenn dieses QObject auch irgendwie "lebt". Einfach nur

- einen Dialog erzeugen
- ein QObject erzeugen
- lauter signal-slot beziehungen herzustellen
- und dann das QObject mit Daten zu fuellen

ist Unfug, wenn das Ziel eben nur ist, den Dialog mit Daten zu fuellen - das kann man dann auch direkt machen.
Benutzeravatar
Trüffelschwein
User
Beiträge: 12
Registriert: Donnerstag 23. Juni 2011, 08:50

Ich glaube, allmählich steige ich ein wenig dahinter. Und das Problem mit dem setText-Slot habe ich mit etwas Rumprobieren auch gelöst. Ist vielleicht nicht besonders schön, aber es funktioniert :mrgreen:

... eigentlich ganz easy :shock:

Code: Alles auswählen

# Dialogfeldfuellen.py

import sys
from PyQt4 import QtGui, QtCore

class MyDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)

        self.resize(500, 250)
        self.setWindowTitle('DialogFüllen')

        self.Name = QtGui.QLineEdit(self)
        self.Name.setGeometry(10,10,150,20)
        self.Name.setMaxLength(30)
        self.Name.setText('Ich bin ein Demotext')

app = QtGui.QApplication(sys.argv)
main = MyDialog()
main.show()
sys.exit(app.exec_())

deets

Hmja, so halb ;)

Das funktioniert natuerlich so, und insofern - so weit, so gut.

Aber es ist nicht die Art, wie man mit Qt umgeht. Schon vor einigen Jahren hat sich die Erkenntnis durchgesetzt, dass Widgets (wie deinen Dialog) durch Ableitung zu bauen eine unschoene Verquickung von Logik & Layout bedeutet.

Darum erzeugt der Qt-Designer inzwischen ja auch reine resource-files in XML-Form, und die kann man sogar wenn man will zur Laufzeit erst interpretieren lassen.

Dementsprechend hast du den Konstruktor nicht zur Verfuegung fuer das befuellen der Daten.

Also saehe der Code eher so aus:

Code: Alles auswählen

dlg = MyDialog()
dlg.findChild("Name").setText("demotext")
Dann kannst du zB das "Name"-widget in einen anderen Container packen, und der code wuerde immer noch funktionieren.
Benutzeravatar
Trüffelschwein
User
Beiträge: 12
Registriert: Donnerstag 23. Juni 2011, 08:50

Klar, das macht die Sache flexibler und trennt die Gui sauber vom Business-Layer. Mein Code stammt überwiegend von einem PyQt-Tutorial, das ich auf unser Diskussionsthema angepasst habe. Da hat man das wohl noch auf einer simplen Alles-in-einer-Datei-Schiene gelassen, um die Newbies nicht zu verwirren :D .

Aber eine Frage zum Verständnis:
Wenn ich dieses Paradigma (Trennung von Logik und Layout) konsequent anwende, dann würde ich alle Logik-Einstellungen (wie zB die Textvorbelegung) in Child-Verweise packen und das am besten von einer Datei aus aufrufen, die zum Businesslayer gehört und die ich in der PyQt-Datei inkludiere, während alle Layouteinstellungen (Größe, Font, Farbe) durchaus im Konstruktur der Fenster-Klasse angesiedelt werden. Sehe ich das richtig oder drehe ich jetzt ab :? ?
deets

Fast richtig. Zum einen kommen diese Voreinstellungen eigentlich nicht mehr so direkt vor - die sind ja im Designer erstellt.

Und zum anderen redest du davon, die Logik zum befuellen des Dialoges *innerhalb* seiner Klasse irgendwie anzuziehen. Und das ist natuerlich nicht richtig.

Stattdessen wirst du ja eine Controller-Schicht oAe haben, und da entscheidest "ok, ich brauch' jetzt den und den Dialog, und befuelle den mit diesen Daten hier".
Pacnos
User
Beiträge: 19
Registriert: Sonntag 8. Mai 2011, 20:17

Hättest du da vieleicht ein kleines Beispiel deets, wo auch diese Sache mit den XML Dateien erwähnt wird?
Antworten