Hilfe benötigt bei Label-Tabelle fürs Studium

Fragen zu Tkinter.
Antworten
xTras
User
Beiträge: 1
Registriert: Sonntag 15. Juni 2014, 12:09

Halloo erstmal :)

Ich habe eine wichtige Frage an euch und ich hoffe ihr könnt mir die beantworten. Ihr würdet mir den allerärtesten retten.
Vorab ich bin kein Programmierer und ich hätte am Anfang als ich das Projekt vom Professor angenommen habe auch nicht mit soviel Stress gerechnet,
aber das ist ein anderes Thema :P


Also ich habe ein Frame/Window (PageTwo) erstellt, welches für mein Studienprojekt Variablen(aus einem CAN-Telegramm gelesen) in einer Matrix/Tabelle anzeigen soll
Hier ist einmal der Code für PageTwo/ das Hauptframe

Code: Alles auswählen

class PageTwo(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Zelltemperatur", font=TITLE_FONT)
        label.pack(side="top", fill="x", pady=10)

        abschnitt=tk.Frame(self)
        abschnitt.pack()
        
        t1=TkTabelle(abschnitt,6,4)
        
        button= tk.Button(self, text="Startoberflaeche", 
                           command=lambda: controller.show_frame(StartPage))
        button.pack()
Wie man sieht beinhaltet t1 meine Tabelle mit 6 Zeilen und 4 Spalten.

Hier ist der Code für die Tabelle
Im Prinzip sind das einfach Labels die mit .grid so angereiht werden dass es eine Tabelle ergibt.

Code: Alles auswählen

class TkTabelle:
    def __init__(self, frame, zeilen, spalten):
        self.frame=frame
        self.zeilen=zeilen
        self.spalten=spalten
        self.tabelle=[]

        for z in range(zeilen):
            self.tabelle.append([])

            for s in range(spalten):
                label=tk.Label(self.frame,width=2,height=2,bg="white",relief="solid")
                label.grid(row=z, column=s)
                self.tabelle[-1].append(label)
                
    def einfuegen(self,zeile,spalte,zahl):
        self.tabelle[zeile][spalte].config(text=zahl)

Ich habe also jetzt eine 6x4 Matrix in PageTwo erstellt welches mir auch perfekt angezeigt wird. Nur natürlich erstmal leer.

Der Nächste Schritt wäre folglich Daten in die Tabelle zu schreiben.
Aber genau hier ist mein Problem.
Wie kann ich Daten in die Tabelle schreiben?
Bzw. wie kann ich außerhalb von der __init__ von PageTwo also in einem Thread z.B. Daten einfügen?

Ich habe angenommen, dass folgendes Ausreicht um auf t1 in PageTwo zuzugreifen aber dem ist nicht so...

Code: Alles auswählen

Temperatur=[20,40,50,60,70,80]

class Daten(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
   
    def run(self):
        while 42:
            PageTwo.t1.einfuegen(0,1,Temperatur[0]) #soll an Zeile 0 Spalte 1 den Wert von Temperatur[0] eintragen
            time.sleep(2)
            print "Ich bin im Thread"
Bei PageTwo.t1.einfuegen gibt es immer eine Fehlermeldung...


Um also alles nochmal ein wenig kürzer zusammen zu fassen:

Wie kriege ich es hin auf t1 in class PageTwo zuzugreifen damit ich dann die Funktion einfuegen benutzen kann,
um Daten einzulesen und das Periodisch, da ich immer wieder Daten vom CAN-Bus einlese und diese dann darstellen möchte.


Wenn mir einer das sagen kann, dann würde ich hier vor Freude ausrasten!!

Bin seit einer Woche damit beschäftigt und keine Lösung gefunden ...


Wenn ihr mein Problem nicht ganz verstanden habt, dann einfach nachfragen ... :)

Mit besten Grüßen
xTras
BlackJack

@xTras: Da fehlen Grundlagen von objektorientierter Programmierung würde ich mal sagen. Weder die `PageTwo`-Klasse noch Objekte von diesem Typ haben ein `t1`-Attribut. Und wenn die Objekte so etwas hätten könnte man auch nicht über die Klasse darauf zugreifen. Vom Entwurf her sollte man vielleicht auch gar nicht so direkt darauf zugreifen, sondern `PageTwo` eine Methode geben die entsprechend das Datum an die richtige Stelle setzt.

Bei der `TkTabelle` ist komisch dass der `tk.Frame` von ausserhalb kommt. Normalerweise würde man so etwas von `tk.Frame` ableiten. Das die Zeilen beim erstellen nicht an einen Namen gebunden werden und man deshalb auf die letzte Zeile mit -1 als Index zugreifen muss, ist nicht so wirklich schön.

`einfuegen()` ist als Methodenname etwas irreführend weil normalerweise damit (bzw. mit `insert()`) gemeint ist, dass nichts überschrieben wird, sondern andere Inhalte entsprechend verschoben werden und erhalten bleiben.

Ein weiteres Problem sind Threads: Man darf nur von dem Thread aus etwas an der GUI ändern in dem deren `mainloop()` läuft. Versucht man das von anderen Threads aus, dann ist das verhalten undefiniert und das Programm kann sich sehr komisch verhalten oder sogar hart abstürzen. Man benutzt deshalb üblicherweise eine `Queue.Queue` um von Threads aus mit der GUI zu kommunizieren und fragt die im GUI-Thread regelmässig ab. Das kann man zum Beispiel bei Tk machen in dem man mit der `after()`-Methode auf Widgets immer wieder eine Funktion/Methode registriert die nach der angegebenen Zeit aufgerufen wird.

Wenn man nur eine Funktion hat die asynchron ausgeführt werden soll, dann braucht man übrigens nicht unbedingt eine Unterklasse von `Thread` erstellen, sondern kann die `target`- und `args`-Argumente dieses Datentyps verwenden.
BlackJack

Kleines Beispiel

Code: Alles auswählen

from __future__ import division, print_function, unicode_literals
import Tkinter as tk
from random import randrange
from threading import Thread
from time import sleep
from Queue import Empty, Queue


class TkTable(tk.Frame):
    def __init__(self, master, row_count, column_count):
        tk.Frame.__init__(self, master)
        self.cells = list()
        for i in xrange(row_count):
            row = list()
            for j in xrange(column_count):
                label = tk.Label(
                    self, width=2, height=2, bg='white', relief=tk.SOLID
                )
                label.grid(row=i, column=j)
                row.append(label)
            self.cells.append(row)

    def __setitem__(self, coordinate, value):
        column, row = coordinate
        self.cells[row][column]['text'] = value


def create_values(queue, row_count, column_count, delay):
    while True:
        queue.put(
            ((randrange(column_count), randrange(row_count)), randrange(100))
        )
        sleep(delay)


def poll(queue, table, delay):
    try:
        while True:
            coordinate, value = queue.get_nowait()
            table[coordinate] = value
    except Empty:
        pass
    table.after(delay, poll, queue, table, delay)


def main():
    row_count, column_count = 6, 4
    delay = 1  # In seconds.
    queue = Queue()

    value_thread = Thread(
        target=create_values, args=(queue, row_count, column_count, delay)
    )
    value_thread.setDaemon(True)
    value_thread.start()

    root = tk.Tk()
    table = TkTable(root, row_count, column_count)
    table.pack()
    poll(queue, table, delay * 1000 // 2)
    root.mainloop()


if __name__ == '__main__':
    main()
Antworten