variablen werden nicht mehr übernommen

Fragen zu Tkinter.
Antworten
cybrixx252
User
Beiträge: 30
Registriert: Freitag 27. August 2021, 14:06

Seit dem ich die Selenium eingeschrieben hab und auf eine Webpage gehen will werden keine Variablen mehr vom Entry übernommen.

Bin leider aber auch neu hab bisher alle Programme mit consolen gemachen und wollte nun lernen wie man mit Tkinter bzw Customtkinter GUIs macht.
Deswegen versuch ich eigentlich ein fenster zu machen wo man erst ein login sieht
dann wenn es stimmt sollte sich eine homepage öffnen

fehler ist nur das ich seit neuersten keine variablen mehr bekomme sprich die entry textvariable sind immer leer
außerdem wenn ich die textvariable zb username drinnen stehen hab gehen keine placeholder mehr anzuzeigen



Code: Alles auswählen

import customtkinter
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

# Themes
customtkinter.set_appearance_mode("dark")
customtkinter.set_default_color_theme("blue")

# Fenstergröße und Titel
root = customtkinter.CTk()
root = customtkinter.CTk()
root.title("Winterdienst Alarmierung")
root.geometry("500x250")


# Fenster wird geöffnet
frame = customtkinter.CTkFrame(master=root)
frame.pack(pady=20, padx=60, fill="both", expand=True)

# Überschrift
label = customtkinter.CTkLabel(master=frame, text="Winterdienst Alamierung", font=("Roboto", 24))
label.pack(pady=12 , padx=10)

# Username login input box
username = customtkinter.StringVar()
username_entry = customtkinter.CTkEntry(master=frame, textvariable=username)
username_entry.pack(pady=12 , padx=10)

# Password login input box
password = customtkinter.StringVar()
password_entry = customtkinter.CTkEntry(master=frame, textvariable=password)
password_entry.pack(pady=12 , padx=10)

# Funktion Login
def login():
    if username.get() and password.get() == "1":
        loggedin = True
        print(loggedin)
        print(username.get())
        root.title("Winterdienst Alarmierung")
        chromeöffnen()
    else:
        loggedin = False
        root.title("PASSWORT FALSCH")
        print(username.get())
        print(loggedin)

# Button
login_btn = customtkinter.CTkButton(master=frame, text="Login", command=login)
login_btn.pack(pady=12, padx=10)

# Funktion Chromebrowser update und öffnen
def chromeöffnen():
    print("geht!")
    options=Options()
    options.add_experimental_option("detach", True)
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=options)
    driver.get("https://www.google.com/")
    driver.maximize_window()



# Dauerschleife für die GUI
root.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 13160
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@cybrixx252: @cybrixx252: Ein offensichtlicher Fehler ist, dass Du zwei `Tk`- beziehungsweise `Ctk`-Objekte erstellst. Davon darf es nur eines geben im Programm. Das ist *das* Hauptfenster, wo der Tcl-Interpreter dran hängt. Da Du bei den `StringVar`-Objekten kein `master` angibst, verwenden die implizit das erste `Tk`-Objekt, die GUI verwendet aber anscheinend immer das zweite.

Dann sollte auf Modulebene nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptrogramm steht üblicherweise in einer Funktion die `main()` heisst.

Funktionen bekommen alles was sie ausser Konstanten benötigen, als Argument(e) übergeben.

Das bedeutet, dass man bei GUI-Programmierung nicht um objektorientierte Programmierung (OOP) herum kommt. Oder zumindest `functools.partial()` für triviale Fälle.

`loggedin` macht als lokaler Name innerhalb der Funktion keinen Sinn.

``username.get() and password.get() == "1"`` ist unnötig umständlich für einfach nur ``password.get() == "1"``. Das "1" keine leere Zeichenkette ist ergibt sich ja daraus, dass da ein Zeichen drin ist.

Da sind zu viele unnütze Kommentare. Kommentare sollten dem Leser einen Mehrwert über den Code bieten. Faustregel: Ein Kommentar beschreibt nicht was der Code macht, denn das steht dort bereits als Code, sondern warum er das so macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in der Regel was in der Dokumentation von Python oder den verwendeten Bibliotheken steht. Beispielsweise muss bei Funktionen nicht kommentieren das es Funktionen sind, das sieht man ja schon durch die Definition der Funktion selbst. Und den Funktionsnamen muss man auch nicht noch mal in den Kommentar schreiben, denn auch das sieht man an der Funktionsdefinition. Was eine Funktion macht, sollte aus dem Namen klar werden. Falls es da mehr zu sagen gibt, und man keinen passenderen Namen findet, würde statt eines Kommentars auch eher einen Docstring für die Funktion schreiben.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
Benutzeravatar
__blackjack__
User
Beiträge: 13160
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import partial

import customtkinter
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager


def oeffne_chrome():
    print("geht!")
    options = Options()
    options.add_experimental_option("detach", True)
    driver = webdriver.Chrome(
        service=Service(ChromeDriverManager().install()), options=options
    )
    driver.get("https://www.google.com/")
    driver.maximize_window()


def login(window, password_var, username_var):
    if (password_var.get(), username_var.get()) == ("1", "Albert"):
        window.title("Winterdienst Alarmierung")
        oeffne_chrome()
    else:
        window.title("Passwort oder Benutzername falsch")


def main():
    customtkinter.set_appearance_mode("dark")
    customtkinter.set_default_color_theme("blue")

    root = customtkinter.CTk()
    root.title("Winterdienst Alarmierung")

    frame = customtkinter.CTkFrame(root)
    frame.pack(pady=20, padx=60)

    options = {"padx": 10, "pady": 12}
    customtkinter.CTkLabel(
        frame, text="Winterdienst Alamierung", font=("Roboto", 24)
    ).pack(**options)

    username_var = customtkinter.StringVar()
    customtkinter.CTkEntry(frame, textvariable=username_var).pack(**options)
    password_var = customtkinter.StringVar()
    customtkinter.CTkEntry(frame, textvariable=password_var).pack(**options)

    customtkinter.CTkButton(
        frame,
        text="Login",
        command=partial(login, root, username_var, password_var),
    ).pack(**options)

    root.mainloop()


if __name__ == "__main__":
    main()
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
cybrixx252
User
Beiträge: 30
Registriert: Freitag 27. August 2021, 14:06

ah du meinst das root=customtkinter.ctk()

das muss ich anscheinend unabsichtlich kopiert haben
die kommentare sind nur für mich als übersicht gedacht.

Dadurch das ich das erste mal mit GUIs arbeite (außer html bzw vba) viele der variablen wie zb "Loggedin" waren zu fehler finden und kommen dann natürlich wieder weg.
so hab ich nur gesehen das es in the console drinnen steht.


functools.partial() werd ich mir heute in der nachtschicht noch anschauen


aber oihne die root=...geht es wieder :)
cybrixx252
User
Beiträge: 30
Registriert: Freitag 27. August 2021, 14:06

ich hab nun ein bissl was umgestellt und versucht das ganze in klassen und methoden zu erstellen
jedoch steh ich nun gerade wieder an es gibt ja globale variablen welche vorher definiert werden dann mit

username1 = "xxx"
dann in der def mit
global username1
username1 = self.txt_username.text()

zb abgerufen werden

jedoch wenn ich nun in einer neuen klasse gehe kann ich ja mit dieser variable nichts mehr machen oder?
ich möchte eigentlich nur jeden eingeloggten user von der SQLLite db begrüßen und nicht mit dem system .getuser()

versucht hätte ich es schon mit
def login():
.
.
return username1

dann bei der __init__ von der neuen klasse als parameter (self, username1)


allerdings schreibt er dann immer not defined

Code: Alles auswählen

from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6 import QtSql
import sys
from ui.frm_main import Ui_MainWindow
from ui.frm_menu import Ui_frm_menu


### 17.12.2022 eingeloggter User in menu form übernehme


class Frm_main(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.opendb()
        self.actionClose.triggered.connect(self.schließen)
        self.btn_login.clicked.connect(self.login)


    def opendb(self):
        self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName("database/database.sqlite")
        if not self.db.open():
            print("db nicht geöffnet")
        self.query = QtSql.QSqlQuery()
    
    def login(self):
        username1 = self.txt_username.text()
        password1 = self.txt_password.text()
        self.query.exec("select * from user where username ='%s' and password = '%s';"%(username1, password1))
        self.query.first()
        if self.query.value("username") != None and self.query.value("password") != None:
            print("login successfull")
            frm_menu.show()
            frm_main.close()
        else:
            print("login failed")


    def schließen(self):
        self.close()


class Frm_menu(QMainWindow, Ui_frm_menu):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.actionClose.triggered.connect(self.schließen)


    def schließen(self):
        self.close()

app = QApplication()
frm_main = Frm_main()
frm_menu = Frm_menu()

frm_main.show()
app.exec()
Benutzeravatar
__blackjack__
User
Beiträge: 13160
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@cybrixx252: Es gibt kein ``global``, vergiss das. Das macht man ja schon so nicht, aber bei objektorientierter Programmierung gibt es schon erst recht keinen Grund mehr so etwas unsinniges zu machen.

Das Hauptprogramm sollte in einer Funktion stehen, denn im Moment hast Du immer noch `app`, `frm_menu`, und `frm_main` als globale Variablen und benutzt zwei davon sogar auf magische Weise in Methoden. Wobei eines davon überhaupt keinen Sinn macht weil in der Methode auf das Objekt selbst über den global verfügbaren Namen zugegriffen wird, statt einfach über `self`.

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen. Und falls `frm` eigentlich `form` heissen sollte: so heisst das weder in Python noch in Qt.

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste. Aber hier macht die 1 beim Benutzernamen und beim Password überhaupt gar keinen Sinn.

Die Importe von den UI-Klassen sehen falsch aus. Entweder hast Du die generierten Dateien umbenannt, oder noch mal einzeln in jeweils ein Package zusammen mit einer `__init__.py` gesteckt. Warum? Und eigentlich generiert man keinen Quelltext sondern lädt die *.ui-Dateien zur Laufzeit.

Inhaltlich sind die Namen auch falsch. Das vorgeschaltete Loginfenster ist ja kein *Haupt*fenster. Wäre es auch vom Typ her nicht — `QMainWindow` macht da nicht wirklich Sinn. Normalerweise erstellt man für so etwas ein Hauptfenster und packt dann den Anmelde*dialog* als modalen *Dialog* dazu.

Falls man das so herum macht wie Du das machst, muss man ganz normal alles was die Methoden ausser Konstanten benötigen als Argument(e) übergeben. Wenn der Anmeldedialog etwas mit dem Hauptfenster machen will, muss das Hauptfenster übergbenen werden und wird Teil des Zustands des Anmeldedialogs.

Das `print()` falls die Datenbank nicht geöffnet wurde ist ein bisschen zu milde, weil man dann ja nicht einfach so weitermachen kann als wäre nichts passiert.

Bei der Abfrage formatierst Du *Benutzereingaben* direkt als Zeichenketten in SQL. Ganz böser Fehler.

Dann liefert `QSqlQuery.first()` einen Rückgabewert, wenn der `False` ist, macht es keinen Sinn auf Teile des Abfrageergebniss zuzugreifen. Ich habe es jetzt nicht ausprobiert, aber `None` sollte da auch in dem Fall nicht für Felder geliefert werden, ausser das ist das Python-Objekt auf das `PySide` `QVariant`-Objekte abbildet bei denen `isValid()` den Wert `False` liefert.

Falls man keine `QSql*Model` verwendet, würde ich Datenbanken aber auch nicht so umständlich über die Qt-API ansprechen wollen. Das ist viel umständlicher als einfach das `sqlite3`-Modul aus der Standardbibliothek zu verwenden.

Zwischenergebnis (ungetestet):

Code: Alles auswählen

from PySide6 import QtSql
from PySide6.QtWidgets import QApplication, QMainWindow
from ui.frm_main import Ui_MainWindow
from ui.frm_menu import Ui_frm_menu


class MainWindow(QMainWindow, Ui_frm_menu):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.actionClose.triggered.connect(self.close)


class LoginWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, next_window):
        super().__init__()
        self.setupUi(self)
        self.next_window = next_window
        db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName("database/database.sqlite")
        if not db.open():
            raise RuntimeError("Datenbank konnte nicht geöffnet werden!")
        self.actionClose.triggered.connect(self.close)
        self.btn_login.clicked.connect(self.login)

    def login(self):
        query = QtSql.QSqlQuery()
        query.prepare(
            "SELECT * FROM user WHERE username = ? AND password = ?;"
        )
        query.addBoundValue(self.txt_username.text().strip())
        query.addBoundValue(self.txt_password.text().strip())
        if query.exec() and query.first() and not query.next():
            print("login successfull")
            self.next_window.show()
            self.close()
        else:
            print("login failed")


def main():
    app = QApplication()
    main_window = MainWindow()
    login_window = LoginWindow(main_window)
    login_window.show()
    app.exec()


if __name__ == "__main__":
    main()
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
Antworten