QT Übergabe von Variable von einem Fenster zum anderen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
_Helmut__
User
Beiträge: 2
Registriert: Dienstag 15. April 2025, 22:57

Hallo,
ich bin neu hier und freue mich auf guten Gedankenaustausch.
Ich bin gerade dabei meine Prospekte mit Wandervorschlägen zu katalogisieren.
Formular 1 : Eingabe von den Informationen
weiter: ==> ab in die SQL Datenbank oder bei Fehlern: ==> Fehlerformular mit Hinwisen
Da ist nun mein Problem. die Fehlerliste (self.falsch ) wird ist im Fehlerformular unbekannt.

kennt"falsch" nicht: class FE(QtWidgets.QMainWindow, QLabel):
...
self.ui.label_foto.setText( self.falsch ) # auch mit Objektnamen probiert,.. :-(
kommt aus class UI(QtWidgets.QmainWindow, QLabel):
<< hier ist innendrin alles ok >>
self.falsch= ['hh','hhh',...]
nach Fenster schließen wird die Variable gedruckt (main)

Vielen Dank im Voraus

Gruß
Helmut
Benutzeravatar
noisefloor
User
Beiträge: 4149
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

... und willkommen im Forum :-)

Die gelieferten Infos sind zu dünn bzw. zu wenig. Bitte den _kompletten_ relevanten Code zeigen und, falls eine Fehlermeldung auftritt, diese auch _vollständig_ hier posten.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@_Helmut__: Ich vermute mal das mit Objekten ist noch nicht so ganz verstanden worden. Klassen die gleichzeitig von `QMainWindow` *und* von `QLabel` erben sind falsch. Nichts ist gleichzeitig ein Hauptfenster und ein Label. Und mehr als ein Hauptfenster in einem Programm ist ungewöhnlich, insbesondere wenn eine Fehlermeldung ein Hauptfenster ist.

`FE` ist kein sinnvoller Name und `falsch` für eine Liste mit Zeichenketten ebenfalls nicht. `UI` könnte man gerade noch so durchgehen lassen, könnte aber auch besser sein.

Wenn `self.falsch` eine Liste mit Zeichenketten ist, dann ist das als Argument für eine `setText()`-Methode auf einem Label falsch.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
_Helmut__
User
Beiträge: 2
Registriert: Dienstag 15. April 2025, 22:57

Hallo,
danke für die Infos. Ja, mit den Klassen und Vererbung kann ich nocht so richtig das Ganze von den Beispielen in mein Problem transferieren.

Wenn ich das Main Window as FE entferne gibt es eine Fehlermeldung.
das 2. Fenster wird bislang ja auch richtig angezeigt , nur ohne: die richtige Reaktion wenn ich auf "weiter" klicke.
Es ist sicher "nur " eine falsche zuordnung , aber welche ????

Anbei der Code:

#Prospekt_test_Designer_01

import sys
from PySide6.QtWidgets import QApplication,QMainWindow,QLabel, QPlainTextEdit
from PySide6 import QtSql,QtWidgets
from PySide6.QtCore import QSize
from PySide6.QtGui import QPixmap
from PIL import Image
from datetime import date
import sqlite3 as sq
import os
import re

from ui_mainwindow import Ui_MainWindow
from ui_Fehlerwindow import Ui_MW_Fehlerfenster
# Organisation:
Prospektdaten= 'DB_Prospekt.db'
D_Pfad = 'Daten'
foto_init= 'HP_logo.jpg'
DBpfad =os.path.join(D_Pfad,Prospektdaten)
datensatz_elemente = ["Titel","Inhalt", "Maßstab","GPS_olo","GPS_ola","GPS_ulo","GPS_ula","FotoName","Datum","Karte","Wanderungen","Radtouren", "Sehenswürdigkeiten","Unterkünfte","Format","Ablageort","GPS_NW","GPS_SO"]
Formate={1: 'A5',2 :'Folder',3:'Karte',4:'12x23',5:'mini',6:'Sonstige'}

datum = date.today().strftime("%d.%m.%Y")
zeigeFehlertext =' dies ist der Fehlertext'+datum


class FE(QtWidgets.QMainWindow, QLabel):
def __init__(self):
super().__init__()

self.ui = Ui_MW_Fehlerfenster()
self.ui.setupUi(self)
self.ui.pTE_Fe.setPlainText(zeigeFehlertext)
self.ui.pB_OK.clicked.connect(self.closeIt)
self.ui.pB_K.clicked.connect(self.weiter)


def weiter(self):
txt=self.ui.pTE_Fe.toPlainText()
fe.show()


def closeIt(self):
self.close() # und tschüss ...



class UI(QtWidgets.QMainWindow, QLabel):
def __init__(self):
super().__init__()

self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setAcceptDrops(True)
self.aktiv =self.ui.rb_1.isChecked()
self.ui.rb_1.toggled.connect(self.knopf)
self.ui.rb_2.toggled.connect(self.knopf)
self.ui.rb_3.toggled.connect(self.knopf)
self.ui.rb_4.toggled.connect(self.knopf)
self.ui.rb_5.toggled.connect(self.knopf)
self.ui.rb_6.toggled.connect(self.knopf)
self.ui.rb_7.toggled.connect(self.knopf)
self.ui.pB_GPS.clicked.connect(self.GPS)
self.GPS_NW = 0
self.GPS_SO = 0
self.format ="ohne"
self.foto= foto_init
pixOrt=QPixmap(D_Pfad+'/'+self.foto)
self.ui.label_foto.setPixmap( pixOrt)
self.ui.label_foto.setScaledContents(True)
self.ui.cBox_Archiv.addItems(['Archivbox GRAU','Archivbox Orange','Archivbox ROT'])
self.Archivbox = 'ohne'
self.ui.cBox_Archiv.currentTextChanged.connect(self.text_changed)
self.ui.pB_f.clicked.connect(self.closeIt)
self.ui.pB_w.clicked.connect(self.weiter)
self.datensatz={}
self.fehler = False
self.falsch =['Die Fehler sind: ']


def text_changed(self, text):
self.Archivbox = text

def GPS(self):
self.gps_c = self.ui.pTE_GPS.toPlainText()
print('def GPS:',self.gps_c)
gps__= self.gps_c.split('=')
print(gps__[2])
gps= gps__[2].split(';')
self.GPS_NW = gps[0]
self.GPS_SO = gps[1]
self.ui.pTE_GPS.setPlainText('Koordinaten sind:'+str(gps))

def closeIt(self):
self.close() # und tschüss ...

def knopf(self):
self.rbtn = self.sender()
self.aktiv =self.ui.rb_1.isChecked()
self.format =self.rbtn.text()
print('Z100 self.rbtn.text():',self.rbtn.text(), self.rbtn.objectName() )

def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()

def dropEvent(self, event):
lines = []
for url in event.mimeData().urls():
lines.append('dropped: %r' % url.toLocalFile())
self.foto = url.toLocalFile()
pic = Image.open(self.foto)
picc =self.foto.split("/")
picPfad=D_Pfad+'/'+ picc[-1]
pic.save(picPfad)
print('pic gespeichert unter:',D_Pfad+'/'+ picc[-1])
pixOrt=QPixmap(picPfad)
self.ui.label_foto.setPixmap( pixOrt)
self.ui.label_foto.setScaledContents(True)
self.ui.tBr_foto.setText(picc[-1])
self.foto = picc[-1]

def weiter(self): # vor Datenspeicherung: Variablen erfassen & prüfen
chb_set =False
self.datensatz["myID"]= self.ui.pTE_ID.toPlainText()
self.datensatz["Titel"]= self.ui.pTE_PrNa.toPlainText()
self.datensatz["Beschreibung"]= self.ui.pTE_Prosp_Beschreibung.toPlainText()
try:
self.datensatz["Format"]= int(str(self.rbtn.objectName())[-1])
except:
self.falsch.append('Kein Format gewählt ')
self.datensatz["Ablageort"]= self.Archivbox
self.datensatz["FotoName"]= self.foto
if len(str(self.GPS_SO))>4:
self.datensatz["GPS_NW"]= self.GPS_NW
self.datensatz["GPS_SO"]= self.GPS_SO
tmp= self.GPS_NW.split(',')
self.datensatz["GPS_ola"]= tmp[0]
self.datensatz["GPS_olo"]= tmp[1]
tmp= self.GPS_SO.split(',')
self.datensatz["GPS_ula"]= tmp[0]
self.datensatz["GPS_ulo"]= tmp[1]
else:
self.fehler = True
self.falsch.append('GPS_Daten fehlen')
auswgewählt = self.ui.cB_K.isChecked()
if auswgewählt:
self.datensatz["Karte"]= 1
chb_set =True
else:
self.datensatz["Karte"]= 0
auswgewählt = self.ui.cB_W.isChecked()
if auswgewählt:
self.datensatz["Wanderungen"]= 1
chb_set =True
else:
self.datensatz["Wanderungen"]= 0

auswgewählt = self.ui.cB_R.isChecked()
if auswgewählt:
self.datensatz["Radtouren"]= 1
chb_set =True
else:
self.datensatz["Radtouren"]= 0
auswgewählt = self.ui.cB_S.isChecked()
if auswgewählt:
self.datensatz["Sehenswürdigkeiten"]= 1
chb_set =True
else:
self.datensatz["Sehenswürdigkeiten"]= 0
auswgewählt = self.ui.cB_U.isChecked()
if auswgewählt:
self.datensatz["Unterkünfte"]= 1
chb_set =True
else:
self.datensatz["Unterkünfte"]= 0
if chb_set == False:
self.fehler= True
self.falsch.append('Kein Merkmal ausgewählt')

self.datensatz["gültig"]= 1

tmp= self.ui.pTE_Mastab.toPlainText()
if tmp== '' : tmp='0'
print('z224 tmp:',tmp, type(tmp))
tmp = int(re.sub('[^0-9]', '',tmp))
if (tmp >10000 or tmp<1):
self.fehler = True
self.falsch.append('Maßstab')
else:
self.datensatz["Maßstab"]= tmp
tmp= self.ui.pTE_Dat.toPlainText()
if tmp== '' : tmp='0'
print('z233 tmp:',tmp, type(tmp))
tmp = int(re.sub('[^0-9]', '',tmp))
if (tmp >3000 or tmp<1000):
self.fehler = True
self.falsch.append('Datum Prospektdruck')
else:
self.datensatz["Datum_Pr"]= tmp
datum = date.today().strftime("%d.%m.%Y")
self.datensatz["myID"]= self.ui.pTE_ID.toPlainText()
if not self.fehler: #Werte in Datenbank schreiben
datum = date.today().strftime("%d.%m.%Y")
self.datensatz["Datum_Erstellung"]= datum
self.datensatz["Datum_Mod"]= datum
self.datensatz["gültig"]= 1
str_Spalte=''
str_Werte=''
for i ,j in enumerate (self.datensatz):
str_Spalte+= j +' ,'
str_Werte+=str(self.datensatz[j]+' ,')
str_Spalte=str_Spalte[:-2]
str_Werte=str_Werte[:-2]
conn= sq.connect(DBpfad)
cursor= conn.cursor()
SQL_action=' insert into Prospektliste ('+str_Spalte+') values('+str_Werte+')'
cursor.execute(SQL_action)
cursor.commit()
cursor.close()
str_tmp = 'Datensatz:'+str(self.datensatz["myID"])+' gespeichert'
pixOrt =''
self.ui.label_foto.setPixmap( pixOrt)
self.ui.label_foto.setScaledContents(True)
self.ui.label_foto.setText(str_tmp)
nProspekt()


else:
zeigeFehlertext = self.falsch
fe.show()
# ....

def nProspekt(self): # alle Eingaben zurücksetzen
init_txt =''
pixOrt=D_Pfad+'/'+foto_init
self.ui.label_foto.setPixmap( pixOrt)
self.ui.label_foto.setScaledContents(True)
self.ui.tBr_foto.setText(init_txt)
self.ui.pTE_GPS.setPlainText(init_txt)
self.ui.pTE_Dat.setPlainText(init_txt)
self.ui.pTE_Mastab.setPlainText(init_txt)
self.ui.pTE_PrNa.setPlainText(init_txt)
self.ui.pTE_Prosp_Beschreibung.setPlainText(init_txt)
self.ui.pTE_ID.setPlainText('P 0')
self.ui.cBox_Archiv.setCurrentIndex(0)




if __name__ == "__main__":
app = QApplication(['Drag & Drop'])
win = UI()
fe=FE()
win.show()
app.exec()
print('Archivbox =',win.Archivbox)
print('Format =',win.format)
print('self.GPS_NW = ',win.GPS_NW)
print(' self.GPS_SO =',win.GPS_SO)
print('Falsch:',win.falsch, type(win.falsch))
sys.exit

[url][/url]
Sirius3
User
Beiträge: 18215
Registriert: Sonntag 21. Oktober 2012, 17:20

Zuerst einmal das eigentliche Problem, wenn Du mehrere Fenster haben möchtest, dann geht das so nicht, hier mal eine Seite, die erklärt, wie es richtig geht: https://www.pythonguis.com/tutorials/py ... e-windows/

Was aber noch viel schwerwiegender ist, sind schlechte Namen, was den Code unmöglich macht, zu verstehen.
Um eine gewisse Übersichtlichkeit zu behalten, werden Variablennamen komplett klein, Konstanten KOMPLETT_GROSS und Klassen mit großem Anfangsbuchstaben geschrieben.
Man benutzt keine Abkürzungen. Was ist D bei D_pfad? Was bedeutet FE? pB_K, pTE_FE, pB_f, usw. eine große Masse an völlig unverständlichen zusammengewürfelten Buchstaben.

Variablen nennt man nach Dingen, Methoden nach Tätigkeiten, `zeigeFehlertext` ist aber keine Methode sondern eine Variable, `knopf` dagegen eine Methode und bei `GPS` oder `nProspekt´ wieder keine Ahnung was die Abkürzungen überhaupt bedeuteten sollen.

Pfade sind keine einfachen Strings, da verwendet man keine Stringoperationen, sondern nutzt pathlib.Path.
Nakte except benutzt man nicht, die Funktion `weiter` ist viel zu lang und ganz am Ende steht noch ein unnötiges `sys.exit`.

Man erzeugt keine python-Dateien aus ui-Dateien sondern lädt diese direkt.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@_Helmut__: Objektorientierte Programmierung (OOP) ist Grundlage für GUI-Programmierung mit einem objektorientiertem Rahmenwerk wie Qt. Da sollte man halbwegs sattelfest sein bevor man sich zusätzlich zu OOP noch die ereignisorientierte Programmierung von GUIs drauf packt.

Schön das es eine Fehlermeldung gibt wenn Du nicht von `QMainWindow` erbst. Und was ist mit `QLabel`? Und selbst eine Fehlermeldung macht ein *Hauptfenster* ja nicht automatisch richtig und man sollte das dann auch nicht einfach weglassen, sondern durch was passenderes ersetzen. Und dann halt nicht nur an der Stelle sondern überall, also auch in der Designer-Datei wo das ja wohl auch ausgewählt wurde.

Attribute eines Objekts sollten alle nach Ablauf der `__init__()` existieren. Spätere Methoden aufrufe sollten keine neuen Attribute erzeugen. Das ist unübersichtlich und fehleranfällig. Das bereits angesprochene nackte ``except`` ist eventuell nur nötig weil das Attribut auf das dort zugegriffen wird, eventuell noch gar nicht existiert. Das wäre ein Programmierfehler, den man nicht zur Laufzeit unterdrückt, sondern behebt, so das der `AttributeError` gar nicht erst auftreten kann.

Da ist sicher auch einiges als Attribut auf dem Objekt was da überhaupt gar nicht hingehört. Wenn man beispielsweise in einer Methode in einer Schleife anfängt in der Schleife das gleiche Attribut immer wieder zu setzen ist das „code smell“ und in 99,99% der Fälle falsch. Oder `datensatz` das nur in einer Methode verwendet wird und auch nicht wieder durch ein frisches, neues Wörterbuch ersetzt wird, wo man also wenn man irgendwo einen Fehler macht Gefahr läuft in einem Datensatz eingaben vom letzten Datensatz wieder zu verwenden.

Der Code der den Datensatz und das das SQL erstellt ist nicht Python. Das ist was anderes als Python verkleidet. Und Werte selber in eine Zeichenkette mit SQL mit Zeichenkettenoperationen basteln ist falsch bis sehr gefährlich. Das hier:

Code: Alles auswählen

            str_Spalte=''
            str_Werte=''
            for i ,j in enumerate (self.datensatz):
                str_Spalte+= j +' ,'
                str_Werte+=str(self.datensatz[j]+' ,')
            str_Spalte=str_Spalte[:-2]
            str_Werte=str_Werte[:-2]
            conn= sq.connect(DBpfad)
            cursor= conn.cursor()
            SQL_action=' insert into Prospektliste ('+str_Spalte+') values('+str_Werte+')'
            cursor.execute(SQL_action) 
            cursor.commit()
            cursor.close()
Wäre in Python und ohne das riesige Sicherheitsloch (ungetestet):

Code: Alles auswählen

            with closing(sqlite3.connect(DB_PATH)) as connection:
                with closing(connection.cursor()) as cursor:
                    column_names_text = ", ".join(COLUMN_NAMES)
                    placeholders_text = ", ".join(["?"] * len(COLUMN_NAMES))
                    cursor.execute(
                        f"INSERT INTO prospekt ({column_names_text})"
                        f" VALUES ({placeholders_text})",
                        [datensatz[name] for name in COLUMN_NAMES]
                    )
                connection.commit()
Mit ordentlichen Namen (auch der Tabellenname in der DB), ``with`` und `contextlib.closing()`, und natürlich mit der Erwartung das die Werte in `datensatz` die passenden Datentypen haben, also Zahlen und Datumsobjekte statt dass das alles Zeichenketten sind.

`self.fehler` ist überflüssig und in `self.falsch` gehört das erste Element nicht rein. Da gehören nur die Fehlertexte rein und dann kann man daran auch einfach testen ob es Fehler gab oder nicht, denn wenn keine Fehlertexte dann kein Fehler und umgekehrt.

Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Wenn man das macht, sind `app`, `fe` und `win` keine globalen Variablen mehr — was sie ja auch nicht sein sollten — stellt dann aber fest, dass `fe` an zwei Stellen im Code einfach so auf magische Weise “aus der Umgebung“ benutzt wird und man gar nicht sieht wo das her kommt ohne den gesamten Code danach abzusuchen und dann festzustellen, dass diese Variable auf Modulebene existiert.

Die selben Namen und Attribute sollten nicht an unterschiedliche (Duck-)Typen gebunden werden. Beispielsweise werden die Attribute `GPS_NW` und `GPS_SO` mal an ganze Zahlen und mal an Zeichenketten gebunden.

In `FE.weiter()` macht `fe.show()` auch irgendwie überhaupt gar keinen Sinn. Welches Objekt soll denn da angezeigt werden? Im Code gibt es nur ein `FE`-Exemplar, also würde sich das `fe` auf dieses eine Objekt beziehen. Das wäre dann verständlicher wenn da ``self.show()`` stehen würde, aber `weiter()` ist ja mit einer Schaltfläche in diesem Fenster verbunden, das heisst um diese Methode aufzurufen muss man in dem Fenster den Button drücken, dazu muss das Fenster aber bereits angezeigt werden. Die Methode macht so keinen Sinn. Hier ist auch wieder blöd das die Namen so kryptisch sind, denn man kann jetzt nur sagen, dass die `weiter()`-Methode keinen Sinn macht, aufgrund des Button-Namens `pB_K` hat jetzt hier aber auch keiner eine Chance zu erraten was da *eigentlich* passieren sollte.

Das Argument welches `QApplication` beim erstellen mitgegeben wird, macht so überhaupt gar keinen Sinn.

Die beiden `closeIt()`-Methoden sind sinnfrei. Da wird einfach nur `close()`-Aufgerufen, also kann man an den Stellen wo `closeIt()` verwendet wird auch gleich `close()` nehmen.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
Antworten