TreeView - Zeile auswählen

Fragen zu Tkinter.
Antworten
Tomax
User
Beiträge: 31
Registriert: Sonntag 21. August 2016, 08:05

Hallo Zusammen
Ich möchte ein Fenster öffnen in dem mir meine Daten aus einer SQLite-Tabelle angezeigt werden.
Klappt prima.

Jetzt möchte ich nach Doppelklick auf einen Eintrag etwas mit dem Eintrag machen.
Leider weiss ich nicht weiter.

Code: Alles auswählen

def pumpenliste():
    pwindow = tk.Toplevel(hauptfenster)
    pwindow.title("Pumpe auswählen")
    pwindow.configure(bg=BG_F1)
    pwindow.resizable(width=False, height=False)
    conn3 = sqlite3.connect("PumpPro.db")
    cur3 = conn3.cursor()
    cur3.execute("SELECT DISTINCT hersteller FROM Pumpen ORDER BY hersteller")
    sel_daten4 = cur3.fetchall()
    tree = ttk.Treeview(pwindow, columns=("size", "modified"))
    tree["columns"] = ("date", "time", "loc")

    tree.column("date", width=65)
    tree.column("time", width=40)
    tree.column("loc", width=100)

    tree.heading("date", text="Date")
    tree.heading("time", text="Time")
    tree.heading("loc", text="Loc")
    tree.bind('<Double-Button-1>', selectItem)
    for row4 in sel_daten4:
        id2 = tree.insert("", 'end', row4[0], text=row4[0])
        cur3.execute("SELECT * FROM Pumpen where hersteller = '" + row4[0] + "'")
        sel_daten3 = cur3.fetchall()
        for row3 in sel_daten3:
            tree.insert(id2, 'end', text=str(row3[2]), values=(row3[3], row3[0]))


    tree.grid()


def selectItem():
    print("jfggefjk")
    curItem = tree.focus()
    print(curItem)
"lambda" funktioniert leider auch nicht
Übergabe von self geht nicht,
:-(

Danke,

Thomas
Zuletzt geändert von Anonymous am Donnerstag 29. Dezember 2016, 10:59, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tomax: Parameter in SQL-Statements hineinzuformatieren ist verboten. Dafür gibt es Platzhalter: "SELECT * FROM Pumpen where hersteller = ?"
Sternchen-SELECTS sind übrigens auch schlechter Stil, weil man dann die Datenbankstruktur nie wieder ändern kann (Jedenfalls nicht ohne mulmiges Gefühl). Außerdem frägst Du Dinge ab, die Du gar nicht brauchst. Besser wäre sowieso eine Abfrage, da Du ja sowieso ALLE Zeilen abfrägst, ist die zusätzliche Abfrage nach den Herstellern eigentlich überflüssig.

Was sollen eigentlich die Nummern hinter Deinen Variablennamen. Die wirken auf mich zufällig. Gute Variablennamen sind für das Verstehen wichtig. Für Dich beim Entwickeln, beim finden von Fehlern und nächste Woche, wenn Du wieder verstehen willst, was Du damit eigentlich gemeint hast. Alle Deine Namen enthalten keinerlei Information.

Warum definierst Du erst zwei Spalten um sie gleich danach mit 3 wieder zu überschreiben, füllst dann aber doch wieder nur für 2 Spalten Daten ein? Ich hoffe mal, Date und Time sind nicht zwei Spalten in Deiner Tabelle. Dafür gibt es den Datentyp DATETIME.

Zu Deinem Problem: Du hast kein self, weil Du keine Klasse für Dein Fenster geschrieben hast. Das solltest Du ändern.
Tomax
User
Beiträge: 31
Registriert: Sonntag 21. August 2016, 08:05

Sirius3 hat geschrieben:@Tomax: Parameter in SQL-Statements hineinzuformatieren ist verboten. Dafür gibt es Platzhalter: "SELECT * FROM Pumpen where hersteller = ?"
Sternchen-SELECTS sind übrigens auch schlechter Stil, weil man dann die Datenbankstruktur nie wieder ändern kann (Jedenfalls nicht ohne mulmiges Gefühl). Außerdem frägst Du Dinge ab, die Du gar nicht brauchst. Besser wäre sowieso eine Abfrage, da Du ja sowieso ALLE Zeilen abfrägst, ist die zusätzliche Abfrage nach den Herstellern eigentlich überflüssig.
Da hast du natürlich recht, hab ich auch sofort geändert. Ich habe den eingefügten Code hier auch vereinfacht dargestellt. DIe Abfrage nach den Herstellern hilft mir bei der Gleiderung des Treeview. Sie werdeetzt als Top-Ebene verwendet.
Sirius3 hat geschrieben:Was sollen eigentlich die Nummern hinter Deinen Variablennamen. Die wirken auf mich zufällig. Gute Variablennamen sind für das Verstehen wichtig. Für Dich beim Entwickeln, beim finden von Fehlern und nächste Woche, wenn Du wieder verstehen willst, was Du damit eigentlich gemeint hast. Alle Deine Namen enthalten keinerlei Information.
Die Nummern helfen beim Finden des Codes wenn ich etwas suchen will. Zum Beispiel cur1 und cur2: die werden in anderen Programmteilen verwendet, sind aber ebenfalls cursor auf Datenbanken.
Sirius3 hat geschrieben:Warum definierst Du erst zwei Spalten um sie gleich danach mit 3 wieder zu überschreiben, füllst dann aber doch wieder nur für 2 Spalten Daten ein? Ich hoffe mal, Date und Time sind nicht zwei Spalten in Deiner Tabelle. Dafür gibt es den Datentyp DATETIME.
Liegt auch daran, dass ich den Code gekürzt habe. Ebenso wie Date und Time.... Ist natürlich EIN Feld.
Sirius3 hat geschrieben:Zu Deinem Problem: Du hast kein self, weil Du keine Klasse für Dein Fenster geschrieben hast. Das solltest Du ändern.
Besten Dank für deine Antwort. Leider kann ich das mit den classes nicht wirklich. Ich habe aber das Problem lösen können und mittlerweile läuft der code rund. :-)

Code: Alles auswählen

tree = ttk.Treeview(hauptfenster, columns=("size", "modified"), height=6)
tree["columns"] = ("1", "2", "3")
tree.column("1", width=85)
tree.column("2", width=30)
tree.column("3", width=30)
tree.heading("1", text="Ser#")
tree.heading("2", text="ID")
tree.heading("3", text="User")
tree.bind('<Double-Button-1>', selectItem)
cur3 = conn.cursor()
cur3.execute("SELECT DISTINCT hersteller FROM Pumpen ORDER BY hersteller")
sel_daten4 = cur3.fetchall()
for row4 in sel_daten4:
    id2 = tree.insert("", 'end', row4[0], text=row4[0])
    cur3.execute("SELECT ID, hersteller, typ, serial, benutzer FROM Pumpen where hersteller = '" + row4[0] + "'")
    sel_daten3 = cur3.fetchall()
    for row3 in sel_daten3:
        tree.insert(id2, 'end', text=str(row3[2]), values=(row3[3], row3[0], row3[4]))

tree.grid(row=16, column=0, columnspan=12, sticky="ew")
Zuletzt geändert von Anonymous am Samstag 31. Dezember 2016, 14:00, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Tomax: Die Begründung für die Nummerierung ist ziemlich unsinnig. Das würde nur helfen wenn man sich merkt was all diese Nummern *bedeuten* statt diese Bedeutung direkt in den Namen zu schreiben. Das mag jetzt vielleicht noch funktionieren, aber such mal in einem halben Jahr wieder nach etwas, da wird Dir die Nummer gar nichts mehr nützen, aber vernünftige Namen würden beim lesen und verstehen dann sehr hilfreich sein. Noch schlimmer sind diese nummerierten `row`-Namen mit Indexzugriffen. Da kann man ja nur durcheinander kommen.

Wenn Du Klassen nicht kannst, dann wird das mit der GUI-Programmierung auch nichts.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tomax: zum Finden von Code helfen gute Funktionsnamen und nicht Nummern. In der neuen Version hat sich nichts verbessert, nur dass aus Spaltennamen Spaltennummern geworden sind.

Dass Du ein Problem jetzt wahrscheinlich durch noch mehr globale Variablen „gelöst“ hast, zeigst Du erst gar nicht. Hier mal nur mit einem SELECT dafür mit itertools.groupby

Code: Alles auswählen

pumps_tree = ttk.Treeview(hauptfenster, columns=("Serial", "ID", "User"), height=6)
pumps_tree.column("Serial", width=85)
pumps_tree.column("ID", width=30)
pumps_tree.column("User", width=30)
pumps_tree.heading("Serial", text="Ser#")
pumps_tree.heading("ID", text="ID")
pumps_tree.heading("User", text="User")
cursor = conn.cursor()
cursor.execute("SELECT hersteller, typ, serial, ID, benutzer FROM Pumpen ORDER BY hersteller")
for producer, pumps in groupby(cursor, lambda row: row[0]):
    producer_id = pumps_tree.insert("", 'end', producer)
    for row in pumps:
        pumps_tree.insert(producer_id, 'end', text=row[1], values=row[2:])
Tomax
User
Beiträge: 31
Registriert: Sonntag 21. August 2016, 08:05

@Sirius3: Besten Dank für deinen Beispielcode.

1. Nein, ich habe nicht zusätzliche globale Variablen verwendet.

2. Wieso sollte das GUI nicht funktionieren? Ich bin sehr zufrieden und bisher habe ich noch jedes Problem lösen können.

Leider klappt dein Code nicht ganz: Der Herstellername wird in der Ebene1 nicht angezeigt.

Ich habe aber schon einiges daraus gelernt. Vielleicht finde ich den Fehler ja noch.

Danke,

Thomas
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tomax: in Deinem ersten Post versuchst Du die globale Variable »tree« zu verwenden, was nicht funktioniert, weil sie nicht definiert ist. Daher meine Vermutung, von der ich immer noch überzeugt bin.
Tomax
User
Beiträge: 31
Registriert: Sonntag 21. August 2016, 08:05

Doch doch, der tree funktioniert. ist nur der lokale baum. Hab ich jetzt in pumps_tree umbenannt
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Tomax hat geschrieben:Doch doch, der tree funktioniert. ist nur der lokale baum. Hab ich jetzt in pumps_tree umbenannt
Wenn dir nicht klar ist, warum tree (bzw. jetzt pumps_tree) tatsächlich global ist, solltest du nochmal ins Python-Tutorial schauen und dich informieren, wie das mit den Namensrämen und den Geltungsbereichen von Variablen funktioniert. Kleiner Hinweis dazu: das Keyword global zu verwenden macht eine Variable nicht global.
In specifications, Murphy's Law supersedes Ohm's.
Tomax
User
Beiträge: 31
Registriert: Sonntag 21. August 2016, 08:05

Ist ja schon gut, ich bin halt einer von den lästigen Typen mit dem gefährlichen Halbwissen.

Trotzdem funzt meine Software und macht genau das was ich will. Von mir aus ist der tree auch global.... ist das schlimm oder entspricht das einfach nicht eurem Ethos?

Ich habe nur noch nicht verstanden, warum Sirius3's code fast alles richtig macht, ausser den Bezeichnungen auf Ebene 1.

Die Kombination von For..., groupby und lambda: habe ich einfach noch nicht durchschaut.

Thomas
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tomax: wir wollen aus Deinem Halbwissen gerne Wissen machen. Globale Variablen erfordern globales Wissen. Man kann keinen Teil seines Programms mehr ändern, ohne Gefahr zu laufen, dass an irgendeiner anderen Stelle etwas dabei kaputt geht. Du wirst sehr schnell merken, dass wenn Dein Programm wächst, es immer schwieriger wird, alles am Laufen zu halten. Auch wenn es Dir schwer fällt, nicht sofort weiter tolle neue Features zu Deinem Programm hinzufügen zu können, setzt Dich hin, nimm das Tutorial Deines Vertrauens, lerne OOP, schreibe Dein Programm auf Klassen um, und werde glücklich.
Tomax
User
Beiträge: 31
Registriert: Sonntag 21. August 2016, 08:05

@sirius3:
Ich habe das schon mal angefangen. Ist sicher der richtige Weg. Aber ich muss gestehen, dass mich das ZUsammenspiel von Objekten mit Bildschirmdarstellung und Datenbanken doch noch überfordert.

Beispiel: Das Objekt Pumpe soll einige Parameter berechnen aus Werten, die auf dem Bildschirm in Entry-Feldern dargestellt werden.
Dort sind die Werte scheinbar immer als Strings dargestellt. Das heisst doch, dass ich immer wenn ich die Berechnungsroutine starte, vorher die Bildschirmfelder abfragen , umwandeln und in die Felder des Objekts eintragen muss, oder?
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Tomax: wenn Dich das Zusammenspiel zwischen Objekten überfordert, dann hast Du noch mehr Schwierigkeiten, wenn Du keine saubere Trennung in Objekte machst. Beispiel: es sollen Berechnungen aufgrund von Eingabefeldern getätigt werden; es wird eine große Funktion geschrieben, die die Eingabefelder liest und das Ergebnis wieder in ein Feld schreibt. Alternativ gibt es eine Methode, die Eingabefelder in Zahlen umwandelt, diese an eine Method des Objekts Pumpe übergibt und dessen Ergebnis darstellt. Im ersten Fall mußt Du auch irgendwo die Umwandlung machen (weil Du sonst nichts rechnen kannst). Das passiert aber irgendwo, wo es gerade gebraucht wird. Von welchen Eingaben die Funktion abhängig ist, siehst Du nur, wenn Du Dir die Funktion komplett durchliest. Die Berechnung läßt sich nur schwer testen, da man dafür immer irgendwelche Eingabefelder füllen muß, statt direkt eine Methode mit Werten aufzurufen. Die Überforderung kommt nur daher, dass man sich selbst die direkten Wege verbaut und sich deshalb immer die Frage stellen muß, wie kommen die Daten von A nach B.
Antworten