QT Designer Widgets aus dem Source Code in die GUI

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

Hallo Zusammen,

sorry ich bin gerade total am Anfang mit der GUI Programmierung :shock: . Ich nutze den Qt5 Designer, mit dem sich schöne GUIs bauen lassen. Ich kann damit auch schon in eine Richtung gut experimentieren, d.h. die erstellte *.ui Datei kann ich in meinem Source Code gut einbinden und die Daten, die ich aus den Input Widgets der GUI eingeben, auslesen und weiterverarbeiten. Jetzt mag es mir aber nicht gelingen, aus dem Quellcode neue Widgets zu erstellen (wofür ich ja den Designer nutze). Konkret gibt der Nutzer z.B. eine 3 ein und da soll ins frame_2 dann 3 neue Lineedit-Widgets erzeugt werden. Bei tkinter wäre das einfach zu realisieren. Wie geht das bei QT? PS: Ich bin auch in der OOP noch nicht sooo fortgeschritten :| So sieht meine entsprechende Implementierung aus:

Code: Alles auswählen

import sqlite3, os, sys
from PyQt5 import QtWidgets, QtCore, uic


class db_manager_GUI(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = uic.loadUi("db_manager.ui", self)
        # Slots einrichten
        self.ui.b1_create_database.clicked.connect(self.create_database)
        self.setWindowTitle("db manager python")

    def create_database(self):
        db_name = self.ui.database_name.text().format() +".db"
        connection = sqlite3.connect(db_name)
        cursor = connection.cursor()

        name_of_columns = self.ui.name_of_columns.text().format()
        #name_of_colums_list = name_of_columns.split(";")

        db_table_name = self.ui.database_table.text().format()
        sql_statement = "CREATE TABLE IF NOT EXISTS "+db_table_name + " (" + name_of_columns + ")"

        cursor.execute(sql_statement)

        
        
    
app = QtWidgets.QApplication(sys.argv)
dialog = db_manager_GUI()
dialog.show()
sys.exit(app.exec_())
Über die Variable self.ui kann ich auf alle Objekte der GUI zugreifen und diese auslesen...nur eben gelingt es mir noch nicht, aus dem Sourcecode z.B. 3 Widgets im Frame 2 zu erzeugen. Könnt ihr mir da einen Tip geben bzw. sagen, wo der Fehler in meinem Programm ist?

Besten Dank,
m.g.o.d
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@m.g.o.d: Der Klassenname entspricht nicht den Namenskoventionen. Bei Klassen sind sich Python und Qt einig: MixedCase.

Es ist verwirrend sowohl den Rückgabewert von `loadUi()` an einen Namen zu binden *und* ein zweites Argument zu übergeben, weil die Attribute auch alle auf diesem zweiten Argument gesetzt werden. Du kommst jetzt an alles über `self.der_name_des_widgets` und über `self.ui.der_name_des_widgets` heran. Entscheide Dich für eines. Beziehungsweise lass das ``self.ui = …`` weg wenn Deine Klasse von einem Widget erben soll.

Was sollen denn die `format()`-Aufrufe? Die machen keinen Sinn wenn da keine Platzhalter sind und keine Werte übergeben werden.

In dem Code wird doch nirgends versucht Widgets in einem Frame zu erzeugen, also kann man da auch schlecht einen Fehler in dem nicht vorhandenen Code finden. Die Problembeschreibung, der Nutzer gibt eine 3 ein, passt irgendwie auch nicht zum Code. Ich sehe jedenfalls nicht wo der Nutzer hier eine Zahl einegegen hätte, beziehungsweise wo im Code diese Zahl verarbeitet werden sollte.

Was soll damit denn gelöst werden? Sicher das Du da nicht irgendetwas von `QAbstractItemView` abgeleitetes haben willst, statt dynamisch Widgets zu erzeugen?

Ansonsten musst Du Dir halt das Layout holen wo Du die neuen Elemente hinzufügen möchtest, und das *tun*. Ob Du nun selbst ein Layout per Code erzeugst oder das aus der geladenen *.ui-Datei nimmst, ändert an dem Schritt nichts. Das Layout von einem Widget kann man abfragen. Die Methode heisst wenig überraschend `layout()`. Falls der Frame noch kein Layout im Designer gesetzt bekommen hat, muss man es halt auch per Code erstellen, so wie man das ohne Designer machen würde.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

__blackjack__ hat geschrieben: Dienstag 21. Juli 2020, 22:21 @m.g.o.d: Der Klassenname entspricht nicht den Namenskoventionen. Bei Klassen sind sich Python und Qt einig: MixedCase.

Danke für den Hinweis! Mache ich in Zukunft der Konvention entsprechend.

Es ist verwirrend sowohl den Rückgabewert von `loadUi()` an einen Namen zu binden *und* ein zweites Argument zu übergeben, weil die Attribute auch alle auf diesem zweiten Argument gesetzt werden. Du kommst jetzt an alles über `self.der_name_des_widgets` und über `self.ui.der_name_des_widgets` heran. Entscheide Dich für eines. Beziehungsweise lass das ``self.ui = …`` weg wenn Deine Klasse von einem Widget erben soll.

Okay. Thx.

Was sollen denn die `format()`-Aufrufe? Die machen keinen Sinn wenn da keine Platzhalter sind und keine Werte übergeben werden.

Okay also die format() Aufrufe waren bei mir nötig, weil ich sonst keinen nutzbaren String bekam. Ich glaube in der Variable war dann das Objekt gebunden aber nicht der Inhalt. Als ich dann format() probiert habe, war auf einmal der übergebene String zu sehen. Ich probiere nochmal etwas herum.

In dem Code wird doch nirgends versucht Widgets in einem Frame zu erzeugen, also kann man da auch schlecht einen Fehler in dem nicht vorhandenen Code finden. Die Problembeschreibung, der Nutzer gibt eine 3 ein, passt irgendwie auch nicht zum Code. Ich sehe jedenfalls nicht wo der Nutzer hier eine Zahl einegegen hätte, beziehungsweise wo im Code diese Zahl verarbeitet werden sollte.

Was soll damit denn gelöst werden? Sicher das Du da nicht irgendetwas von `QAbstractItemView` abgeleitetes haben willst, statt dynamisch Widgets zu erzeugen?

Ansonsten musst Du Dir halt das Layout holen wo Du die neuen Elemente hinzufügen möchtest, und das *tun*. Ob Du nun selbst ein Layout per Code erzeugst oder das aus der geladenen *.ui-Datei nimmst, ändert an dem Schritt nichts. Das Layout von einem Widget kann man abfragen. Die Methode heisst wenig überraschend `layout()`. Falls der Frame noch kein Layout im Designer gesetzt bekommen hat, muss man es halt auch per Code erstellen, so wie man das ohne Designer machen würde.

Also es gibt halt ein SpinBox Widget, wo der Nutzer z.B. eine 3 wählt. Hier soll ausgewählt werden, wieviel Spalten in der SQL Datenbank erzeugt werden soll. Für diese Eingabe sollen dann halt entsprechend 3 Eingabewidgets sowie 3 Dropdown Felder (für den Datentyp) im Frame 2 erzeugt werden. Eigentlich eine einfache Sache, aber mit QT bin ich, wie gesagt, noch nicht so richtig fit.

Also das Layout hole ich mir über die self. Variable? Und an dieses Layout binde ich dann entsprechend der Menge der Eingabe in einer for-Schleife die Widgets? I will try.

So habe ich das in Tkinter mal gemacht (wobei es geht auch eleganter):

Code: Alles auswählen

  c = 0
    while c != anzahl_Eingabefelder:
        # Erzeugung Entries 
        e = tk.Entry(fr1)
        e.pack()
        eingabe_url.append(e)
        c = c+1
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@m.g.o.d: Wie gesagt, die `format()`-Methode auf Zeichenketten ohne Argumente macht nichts, die liefert dann einfach nur die gleiche Zeichenkette:

Code: Alles auswählen

In [8]: "täst".format()                                                         
Out[8]: 'täst'
Und wie gesagt würde man dafür eher keine Widgets erzeugen, sondern ein beispielsweise ein `QTableView` mit zwei Spalten verwenden, eine für den Namen, und eine für den Typ. Wobei da vielleicht noch ein paar mehr dazu kommen. Beispielsweise um den Primärschlüssel festzulegen, zu wählen ob die Spalte ”nullable” sein darf, ob sie UNIQUE ist, und so weiter.

Bei dem `tkinter`-Code sind am schlimmsten erst mal die Namen. Und dann dass das eine ``for``-Schleife sein sollte.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
m.g.o.d
User
Beiträge: 75
Registriert: Samstag 4. April 2020, 13:17

__blackjack__ hat geschrieben: Mittwoch 22. Juli 2020, 10:18 @m.g.o.d: Wie gesagt, die `format()`-Methode auf Zeichenketten ohne Argumente macht nichts, die liefert dann einfach nur die gleiche Zeichenkette:

Code: Alles auswählen

In [8]: "täst".format()                                                         
Out[8]: 'täst'
Und wie gesagt würde man dafür eher keine Widgets erzeugen, sondern ein beispielsweise ein `QTableView` mit zwei Spalten verwenden, eine für den Namen, und eine für den Typ. Wobei da vielleicht noch ein paar mehr dazu kommen. Beispielsweise um den Primärschlüssel festzulegen, zu wählen ob die Spalte ”nullable” sein darf, ob sie UNIQUE ist, und so weiter.

Bei dem `tkinter`-Code sind am schlimmsten erst mal die Namen. Und dann dass das eine ``for``-Schleife sein sollte.
Ich möchte deine Antwort mal kurz in meiner Auffassung wiedergeben: Du bist auf meine eigentlich Frage kaum eingegangen und inhaltlich war deine Aussage sehr herablassend. Leider bestätigt das die üblichen Klischees von Nerds, die nicht selten bei Fragen von weniger erfahrenen Leuten (und ich bin 100% weniger erfahren als du) damit antworten, wie toll und erfahren sie sind. Ich weiss selber, dass das Beispiel mit Tkinter keine for-Schleife ist und davon war auch nie die Rede, wenn du meine Frage richtig gelesen hättest. Und wo wir gerade bei der Qualität von Informationen, repräsentativ der Qualität von Antworten sind, wäre es in Zukunft für Python Neulinge attraktiver, nicht herablassen behandelt zu werden. Wie toll du dich findest interessiert mich in etwa so viel, wie wenn in China ein Sack Reis umfällt, daher meine Bitte, sowas in Zukunft auf meine Fragen einfach herauszulassen. Dafür ist meine Zeit zu Schade. Von deiner Zeit kann ich nicht reden.

Besten Gruß und danke.
m.g.o.d
Benutzeravatar
sparrow
User
Beiträge: 4361
Registriert: Freitag 17. April 2009, 10:28

@m.g.o.d.: Deine Frage wurde doch zwei Mal beantwortet: Nimm eine Tabelle. Denn du versuchst aus Elementen eine Tabelle nachzubauen. Und das macht keinen Sinn.

Und die Antworten von __blackjack__ haben einen enormen Informationsgehalt, den du erkennst, dass dich hier niemand herablassend behandelt. Mit der Einstellung stehst du dir selbst im Weg.

Es ist übrigens unnötig einen kompletten Beitrag zu zitieren. Der steht ja bereits im Thread.
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@m.g.o.d: Wo habe ich denn geschrieben ich sei toll und erfahren? Ich habe konkret gesagt was man verbessern kann/sollte. Klar das kommt von Erfahrung, aber die gebe ich doch auf die Art weiter, damit andere auch etwas davon haben. Damit die genau so toll werden wie ich. 😉

Und die Frage(n) habe ich denke ich schon beantwortet. Direkt — das man sich das Layout von dem Widget über die `layout()`-Methode holen kann, sofern im Designer eines gesetzt wurde. Und dass man dann dynamisch Widgets zu dem Layout-Objekt hinzufügen kann. Das funktioniert dann genau so als wenn man GUIs ohne den Designer erstellen würde.

Und indirekt habe ich darauf hingewiesen, dass man dafür eher einen `QTableView` verwenden würde, nach dem Du den Einsatzzweck beschrieben hast. Davor war die Empfehlung etwas allgemeiner mit `QAbstractItemView` weil mir nicht klar war ob beispielsweise eine Liste, eine Tabelle, oder ein Baum geeignet gewesen wäre.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
Antworten