Das Benennen der Widgets

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
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute, folgendes Szenario, ich habe mit Qt-Designer einfach einen Dialog erstellt, mit einer Textbox und einem pushButton drauf. Den pushbutton habe ich nicht beschriftet, wollte dies über den Code erledigen, einfach weil ich Lust darauf hatte. Also keine Diskussion darüber ob es sinnvoll ist oder nicht :-) Hier der Code:

Code: Alles auswählen

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.uic import *

class Fenster(QDialog):
    def __init__(self):
        QDialog.__init__(self)

        self.ui = loadUi("NurSehen.ui", self)

        # Slots einrichten
        self.ui.pushButton.clicked.connect(self.onTest)
        self.ui.pushButton("Testen") # Hier wollte ich den PushButton mit "Testen" beschriften


    def onTest(self):
        print("Super, klappt")

app = QApplication(sys.argv)
window = Fenster()
window.show()
sys.exit(app.exec_())
Fehlermeldungen sind jedoch:
Traceback (most recent call last):
File "D:/Dan/Python/Qt-Design/test.py", line 27, in <module>
window = Fenster()
File "D:/Dan/Python/Qt-Design/test.py", line 18, in __init__
self.ui.pushButton("Testen")
TypeError: 'QPushButton' object is not callable
Ich ging davon aus, sobald ich den pushButton ansprechen kann, dass ich ihn auch über den Code selbst beschriften kann?
BlackJack

@Sophus: Das kannst Du natürlich machen. Aber wie kommst Du darauf dass das *so* geht? Also dass man das Wigdet wie eine Funktion aufrufen kann? Und selbst wenn das ginge, warum sollte dass dann ausgerechnet den Text setzen. Und nicht die Farbe? Oder irgendetwas anderes. Du musst halt in der Dokumentation nachlesen welche Methoden so ein `QPushbutton` hat und mit welcher man die Beschriftung der Schaltfläche setzen kann.

Das zweite Argument bei `loadUi()` angeben *und* den Rückgabewert an ein Attribut binden ist übrigens etwas zu viel des guten. Man macht entweder das eine oder das andere aber nicht beides. Wenn die Klasse selbst ein Widget ist, dann üblicherweise die Variante mit dem zweiten Argument und ohne ein `ui`-Attribut.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

BlackJack hat geschrieben:@Sophus: Das kannst Du natürlich machen. Aber wie kommst Du darauf dass das *so* geht? Also dass man das Wigdet wie eine Funktion aufrufen kann? Und selbst wenn das ginge, warum sollte dass dann ausgerechnet den Text setzen. Und nicht die Farbe? Oder irgendetwas anderes. Du musst halt in der Dokumentation nachlesen welche Methoden so ein `QPushbutton` hat und mit welcher man die Beschriftung der Schaltfläche setzen kann.[/code]

Hallo BlackJack, vielen Dank für den Hinweis. Ich habs so gelöst:

Code: Alles auswählen

self.ui.pushButton.setText("Testen")
Da habe ich doch glatt dieses setText unterschlagen :-)
BlackJack hat geschrieben:@Sophus:
Das zweite Argument bei `loadUi()` angeben *und* den Rückgabewert an ein Attribut binden ist übrigens etwas zu viel des guten. Man macht entweder das eine oder das andere aber nicht beides. Wenn die Klasse selbst ein Widget ist, dann üblicherweise die Variante mit dem zweiten Argument und ohne ein `ui`-Attribut.
Beides macht man nicht? Angenommen, man würde es theoretisch so belassen. Hätte ich im späteren Verlauf dadurch massive Nachteile oder überhaupt Nachteile?

Aber um dein Anliegen umzusetzen, meinst du, dass das zweite Argument bei loadUi() weglassen soll? Also so?

Code: Alles auswählen

self.ui = loadUi("NurSehen.ui") # anstatt self.ui = loadUi("NurSehen.ui", self)
BlackJack

@Sophus: Man hat halt wenn man beides macht alles aus der GUI doppelt erreichbar und dann benutzt man entweder immer nur einen Weg, dann kann man halt eines von beidem weglassen, oder man benutzt beide Wege, was ich eher verwirrend finde.

Wenn Du das `self` weglässt, dann macht es keinen Sinn mehr von `QDialog` zu erben, das sollte man dann konsequenterweise auch weglassen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

BlackJack hat geschrieben:@Sophus: Man hat halt wenn man beides macht alles aus der GUI doppelt erreichbar und dann benutzt man entweder immer nur einen Weg, dann kann man halt eines von beidem weglassen, oder man benutzt beide Wege, was ich eher verwirrend finde.

Wenn Du das `self` weglässt, dann macht es keinen Sinn mehr von `QDialog` zu erben, das sollte man dann konsequenterweise auch weglassen.
Jetzt kapiere ich es:

Vorher:

Code: Alles auswählen

self.ui = loadUi("NurSehen.ui", self)
Nachher

Code: Alles auswählen

loadUi("NurSehen.ui", self)
Aber wenn ich die Vorher-Variante benutze, so kann ich ja gezielt mit dem Attribut ui arbeiten oder? Du hast natürlich Recht, in diesem Fall ergibt es keinen Sinn, wenn man es nicht braucht. Macht der "Gewohnheit", dass ich nahezu dazu geneigt bin, immer "Objekte" verwenden zu wollen, denn ich denke mir immer, sobald ich etwas an einen Namen (Attribute) binde, hier in diesem Fall ui, so mache ich etwas zum Objekt. Also ist ui in diesem Fall ein Objekt. Oder denke ich da einfach zuviel? Man will eben in Python alles richtig machen, schließlich will ich es ja von der Pike auf richtig machen :-)
BlackJack

@Sophus: In Python ist *jeder* Wert ein Objekt, da kann man also nichts zu einem Objekt machen was nicht sowieso schon eines ist. Und dadurch das man ein Objekt über zwei verschiedene Namen erreichen kann wird es nicht mehr oder weniger zum Objekt. ``self.ui`` macht nur Sinn wenn man dadurch das Objekt `self` von dem geladenen UI-Objekt trennen würde. Also am Ende wirklich zwei Objekte mit unterschiedlichen Attributen und Verhalten haben würde. Und dann braucht die Klasse wie gesagt auch nicht mehr von `QDialog` erben, weil sie dann ja gar nicht mehr das `Widget`-Objekt ist, sondern eher eine Art Controller.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@ BlackJack. Besten Dank für die Aufklärung. Da ich nicht für jedes kleine Bisschen ein weiteres Thema öffnen will, mache ich hier mal weiter. Ich bin gerade dabei, so einige Widgets auf PyQt4 auszuprobieren. Nun ist die Combobox dran. Auf der GUI ist nun eine leere Combobox platziert.

Code: Alles auswählen

[...]
        loadUi('NurSehen.ui', self)

        list1 = [
                self.tr('First Item'),
                self.tr('Second Item'),
                self.tr('Third Item'),
                ]

        self.comboBox.clear()
        self.comboBox.addItems(list1)

[...]
    def onTest(self):
        text = str(self.comboBox.currentIndex())
        if text == "0":
            print "First Item was selected!"
        if text == "1":
            print "Second Item was selected!"
        if text == "2":
            print "Third Item was selected!"
Mit Hilfe der List Comprehensions (hier: list1 ) habe ich also die Combobox gefüllt (Zeile 11). Alles in Ordnung soweit. Nun wählt man in der Combobox ein Item aus. Durch das Betätigen einer Schaltfläche (pushButton) wird dann die onTest-Funktion aufgerufen. Im Attribut text wird dann das zur Zeit ausgewählte Index in Form eines Strings gespeichert, und durch die IF-Abfragen frage ich nach, welcher Index gewählt wurde und dementsprechend verhält sich das Programm. Funktioniert auch alles super. Aber wenn man am Ende mehr als 10 Items hat, dann wird das ein "Spaghetti"-Code.

In VB6 konnte ich das eleganter mit der Select Case-Anweisung lösen - ungefähr so:

Code: Alles auswählen

Private Sub Command1_Click() ' Schaltfläche wird betätigt.
    Select Case Combo1.ListIndex
        Case 0
            Text1.Text = "Index 0"
        Case 1
            Text1.Text = "Index 1"
        Case 2
            Text1.Text = "Index 2"
        Case 3
            Text1.Text = "Index 3"
        Case 4
            Text1.Text = "Index 4"
    End Select
End Sub
Ließe sich das auch so in Etwa bei Python lösen, auch so hübsch und elegant, ohne Spagetti-Programmierung?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: warum wandelst Du einen Index in einen String um? Case ist auch nicht eleganter als eine if-Kette, man spart sich nur das "text=" der Rest ist gleich, Du solltest übrigens "elif" verwenden. Je nachdem was man machen möchte, nimmt man üblicherweise ein Wörterbuch oder eine Liste; hier ganz klar eine Liste.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Sirius3 hat geschrieben:@Sophus: warum wandelst Du einen Index in einen String um? Case ist auch nicht eleganter als eine if-Kette, man spart sich nur das "text=" der Rest ist gleich, Du solltest übrigens "elif" verwenden. Je nachdem was man machen möchte, nimmt man üblicherweise ein Wörterbuch oder eine Liste; hier ganz klar eine Liste.
Es führt also keinen Weg an eine IF-Abfrage vorbei? Also, ich finde, dass die Seelct Case-Anweisung in VB6 wesentlich "hübscher" aussieht. Zumindest wirkt das so auf meine Augen so elegant und hübsch :-) Denn ich als VB6-Programmierer sehe "Stopp, hier ist eine Select Case-Anweisung" und schaue dann nur nach den bestimmten Cases, die ich brauche. Ich muss mich also gar nicht erst durch die ganzen If-Anweisungen prügeln, ehe ich was gefunden habe. Was ich also auf Anhieb sehe ist Case 0, Case 1, Case 2 etc, und kann sofort mit meinem Cursor dort hin springen, ohne zu berücksichtigen, was was da alles außerhalb von Case steht - hat mich nicht zu interessieren. Ist so etwa wie mit der With-Anweisung. Alles sauer strukturiert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Häufig verwendet man statt der if/elif-Kaskaden einfach eine Liste oder ein Dictionary.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

EyDu hat geschrieben:Häufig verwendet man statt der if/elif-Kaskaden einfach eine Liste oder ein Dictionary.
Wie sähe sowas in einem Pseudo-Code aus? Ich kann mir gerade nicht vorstellen, wie ich anstellte der If-Frage eine Liste einsetzen soll.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

In diesem konkreten Fall ist das recht einfach:

Code: Alles auswählen

try:
    value = ["First", "Second", "Third"][self.comboBox.currentIndex()]
except IndexError:
    print "Error"
else:
    print value
Das Leben ist wie ein Tennisball.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

EyDu hat geschrieben:In diesem konkreten Fall ist das recht einfach:

Code: Alles auswählen

try:
    value = ["First", "Second", "Third"][self.comboBox.currentIndex()]
except IndexError:
    print "Error"
else:
    print value
Mit deinem Beispiel habe ich ja dann die doppelte Arbeit, nicht wahr?

Code: Alles auswählen

[...]
        list1 = [
                self.tr('First Item'),
                self.tr('Second Item'),
                self.tr('Third Item'),
                ]

        self.comboBox.clear()
        self.comboBox.addItems(list1)


    def onTest(self):
        try:
            value = ["First", "Second", "Third"][self.comboBox.currentIndex()]
        except IndexError:
            print "Error"
        else:
            print value
Die doppelte, ja sogar die dreifache Arbeit liegt darin begründet, dass ich erst einmal zwei Liste pflegen muss. Kommt in Liste list1 (Zeile 2) was hinzu, so muss ich die Liste value (Zeile 14) anpassen. Und dann ist noch keine Anweisung vorhanden, also ab welchem Index der Combobox sich das Programm wie verhalten soll. Das heißt, ich müsste wieder eine IF-Abfrage schreiben, damit er dann Anweisungen geben kann? Also eine dreifache Arbeit? Oder übersehe ich was?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Sophus hat geschrieben:Oder übersehe ich was?
Ja. Dass gemeinsame Daten zusammengehören. Wenn du zwei Listen parallel führst, dann musst du daraus eine machen. Und schon ist dein Programm besser wartbar und du hast weniger Arbeit.

Ich glaube, es wurde dir schon einmal gesagt: Unterlasse doch bitte die ganzen Fullquotes und zitiere nur die Dinge, auf die du dich beziehst.
Das Leben ist wie ein Tennisball.
BlackJack

@Sophus: Dann musst Du halt die Informationen in *einer* Liste vorhalten. Zum Beispiel als Objekte die den Text für die Anzeige im Widget enthalten und den für die ``print``-Ausgabe nach dem auswählen. Und wenn je nach Auswahl etwas anderes gemacht werden soll, kommt der Code dafür halt auch mit in die Objekte.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

EyDu hat geschrieben: Ja. Dass gemeinsame Daten zusammengehören. Wenn du zwei Listen parallel führst, dann musst du daraus eine machen. Und schon ist dein Programm besser wartbar und du hast weniger Arbeit.
Ich werde beim zitieren nun darauf achten, dass ich nicht komplett alles zitieren werde.

Meinst du etwa so? Hierbei setze ich zwei Liste in eine Liste compound_list zusammen. Aber ob dies auch den Effekt hat, was ich möchte? Bezweifle ich kaum :-(

Code: Alles auswählen

liste1=[2,4]
liste2=['a','b']
compound_list=[]
 
for i in range(0,len(liste1)):
   compound_list.append([liste1[i][0],liste2[i])
 
print biglist
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Viel zu kompliziert und du führst weiterhin zwei verschiedene Listen.

Code: Alles auswählen

compound_list = [
  ("a", 2),
  ("b", 4)]
Es wurde dir mit Sicherheit schon einmal gesagt:

Code: Alles auswählen

range(len(list))
ist in Python ein Anit-Pattern. Iteriere direkt über die Liste. Wenn du einen Index brauchst, benutze die enumerate-Funktion. Wenn du über mehrere Listen parallel iterieren willst, verwende die zip-Funktion.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@Sophus: So ganz hast Du mein Tutorial wohl doch nicht durchgearbeitet, verstanden und vor allem nicht verinnerlicht... denn da gehe ich iirc auf Themen wie diesen ``for i in len(range(collection))`` Anti-Pattern ein, dass man zusammenghörige Daten auch in einer passenden Struktur verarbeiten sollte usw.

Ok, ist auch wirklich schwer für einen Anfänger dies alles sofort zu begreifen, aber evtl. schaust Du es Dir unter solchen Gesichtspunkten noch einmal an. Ja klar geht es darin zielorientiert um Textmenüs, aber daneben werden (hoffentlich) auch allgemein gültige Konzepte vermittelt, die immer wieder in allen möglichen Situationen auftreten :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Hyperion: Du wirst lachen. Später, als mir klar wurde, dass die Zusammensetzung das gleiche bzw. ähnliche Phänomen mit dem Text-Menu ist, musste ich an dich denken und habe mächtig an meine Stirn geklatscht und gemeint : "Man, das hattest du doch bei Text-Menu super hingekriegt, und jetzt machst du sowas". Aber es ist mir alles später erst klar geworden. :-) Ich hatte schlicht und einfach nicht mehr daran gedacht :-)
Antworten