Frame als Klasse / Offset berechnen / Variable aus Klasse extrahieren

Fragen zu Tkinter.
Antworten
NinoBaumann
User
Beiträge: 72
Registriert: Samstag 25. April 2020, 19:03

Hallo,

ich bin gerade dran eine Anwendung zu schreiben. Dazu muss ich Offsets über Buttons verändern. Da ich diese öfters mit verschiedenen Parametern brauche, wollte ich diese in ein separates Frame packen. Dieses Frame ist sehr einfach aufgebaut. Ich möchte
-einen Button 'Plus' mit dem ich den Offset erhöhen kann,
-einen Button 'Minus' mit dem ich den Offset verkleinern kann,
-ein Label in dem der aktuelle Offset angezeigt wird und
-ein Label in dem ich den Namen des Offsets sehe.
Um den Offset zu definieren brauche ich als Variablen, welche ich in den Frame geben möchte:
-einen Startwert (Standardwert von dem ab ich rechne),
-eine Schrittweite (bspw. 0.01mm),
-eine zulässige Anzahl an Schritten und
-den Namen des Offsets.
Die maximale und minimale Offsetgröße berechnet sich aus
-max_offset = Startwert + Schrittweite * Anzahl
-min_offset = Startwert - Schrittweite * Anzahl
Nachdem der Offset über die Buttons 'Plus' und 'Minus' vom Bediener geändert worden ist, möchte ich diesen neuen Offset in meiner Hauptapplikation wiederum als neue Variable benutzen können.
Soweit zur Problemstellung. Der Ablauf sollte so sein. Ich gebe in der Applikation die erforderlichen Variablen an und rufe damit die Klasse OffsetGenerator auf:
-name
-tick_size
-tick_range
-standard_offset
Jetzt möchte ich, dass im Label des aktuellen Offsets als erstes der Standard Offset steht. Über die Buttons 'Plus' und 'Minus' möchte ich jetzt Funktionen aufrufen, um den Offset zu vergrößern oder zu verkleinern:
offset_mod = offset + tick_size oder offset - tick_size
Natürlich mit einer geschalteten Prüfung, ob der Offset noch innerhalb der Grenzen liegt (siehe Formel oben). Dann brauche ich eine Funktion, mit der ich das Label mit dem aktuellen Offset aktualisiere. Das kann man ja evtl. über .config machen. Zum Schluss muss ich den veränderten Offset wieder als Variable in meine Hauptapplikation bringen, um dort wieder damit rechnen zu können.
Kann mir jemand mit dem Problem helfen?

Code: Alles auswählen

import tkinter as tk

class OffsetGenerator(tk.Frame):
    def __init__(self, master, name, tick_size, tick_range, standard_offset):
        tk.Frame.__init__(self, master)

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        
        name = 'cage'
        tick_size = 0.001
        tick_range = 3
        standard_offset = 0
        
        frm_cage_offset = OffsetGenerator(self, name, tick_size, tick_range, offset) 
        frm_cage_offset.pack()
        

if __name__ == '__main__':
    App().mainloop()
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Eine Klasse sollte passend benannt sein, ein OffsetGenerator würde Offsets generieren, Du willst aber einen OffsetEntry haben, also einen Objekt, in das man Offsets eingeben kann.
Dann braucht dieses OffsetEntry natürlich die von Dir beschriebenen Labels und Buttons; da hast Du im Moment noch nichts im Code stehen.
tick_size würde ich step_size nennen und tick_range sowas wie tick_count? Mit standard_offset habe ich so meine Probleme. Soll die Funktion nun einen Offset eingeben, oder einen Wert, der um einen bestimmten Offset von einem mittleren Wert abweichen darf? Dann wäre das ein standard_value.
Zum Speichern und Weiternutzen von Werten würde sich ein tk.DoubleVar eignen.
NinoBaumann
User
Beiträge: 72
Registriert: Samstag 25. April 2020, 19:03

Das hatte ich mal so als grobes Konstrukt geschrieben. Aber an dem Punkt hänge ich, da ich keinen richtigen Plan habe, wie ich den Startwert des Offsets einmal übergebe und danach diesen Wert immer wieder über Plus und Minus verändere. Deshalb stimmen innerhalb des Codes auch nicht die Namen für den Offset. Da ich nicht weiß, wie ich diese nun zu benennen habe. Wie bekomme ich den berechneten Offset aus der Funktion. Das habe ich gelesen, dass man es über eine globale Variable machen kann. Aber das soll man ja tunlichst vermeiden. Das sind so meine Probleme gerade..

Code: Alles auswählen

import tkinter as tk

class OffsetGenerator(tk.Frame):
    def __init__(self, master, name, tick_size, tick_range, offset1):
        tk.Frame.__init__(self, master)
        
        def calculate_offset_plus(tick_size, tick_range, offset1):
            if offset1 < tick_range*tick_size:
                offset = offset1 + tick_size

        def calculate_offset_minus(tick_size, tick_range, offset1):
            if offset1 > -tick_range * tick_size:
                offset = offset1 - tick_size

        def counter():
            offset_label.config(text = offset)
        
        plus_button = tk.Button(master,
                                text='Plus',
                                command= lambda: [calculate_offset_plus(tick_size, tick_range, offset1), counter()])
        plus_button.pack()
        offset_label = tk.Label(master,
                                text=offset)
        offset_label.pack()
        minus_button = tk.Button(master,
                                 text='Minus',
                                 command= lambda: [calculate_offset_minus(tick_size, tick_range, offset1), counter()])
        minus_button.pack()
        offset_name = tk.Label(master,
                               text=name)
        offset_name.pack()

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        
        name = 'cage'
        tick_size = 0.001
        tick_range = 3
        offset = 0
        
        frm_cage_offset = OffsetGenerator(self, name, tick_size, tick_range, offset) 
        frm_cage_offset.pack()       

if __name__ == '__main__':
    App().mainloop()
NinoBaumann
User
Beiträge: 72
Registriert: Samstag 25. April 2020, 19:03

Kleines Update. Ich habe einen Teil auf folgendem Weg gelöst. Wen man es überhaupt so macht. Wie bekomme ich jetzt den neuen Offset von der Klasse zurück?

Code: Alles auswählen

import tkinter as tk

class OffsetGenerator(tk.Frame):
    def __init__(self, master, name, tick_size, tick_range, offset1):
        tk.Frame.__init__(self, master)
        
        offset = tk.DoubleVar()
        offset.set(offset1)
        
        def calculate_offset_plus(tick_size, tick_range, offset1):
            if offset1 < tick_range*tick_size:
                offset_mod = offset1 + tick_size
                offset.set(offset_mod)

        def calculate_offset_minus(tick_size, tick_range, offset1):
            if offset1 > -tick_range * tick_size:
                offset_mod = offset1 - tick_size
                offset.set(offset_mod)

        def counter():
            offset_label.config(text = offset.get())
        
        plus_button = tk.Button(master,
                                text='Plus',
                                command= lambda: [calculate_offset_plus(tick_size, tick_range, offset.get()), counter()])
        plus_button.pack()

        offset_label = tk.Label(master,
                                text=offset.get())
        offset_label.pack()
        minus_button = tk.Button(master,
                                 text='Minus',
                                 command= lambda: [calculate_offset_minus(tick_size, tick_range, offset.get()), counter()])
        minus_button.pack()
        offset_name = tk.Label(master,
                               text=name)
        offset_name.pack()

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        
        name = 'cage'
        tick_size = 0.001
        tick_range = 3
        offset = 0
        
        frm_cage_offset = OffsetGenerator(self, name, tick_size, tick_range, offset) 
        frm_cage_offset.pack()
        

if __name__ == '__main__':
    App().mainloop()
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Der OffsetGenerator generiert immer noch keine Offsets und der `offset` selbst ist immer noch kein Offset; diese Frage und die anderen hast Du mir noch nicht beantwortet. Warum heißt der offset nun offset1?
Man definiert keine Funktionen innerhalb von Funktioen, vor allem nicht, wenn man doch eine Klasse zur Verfügung hat. Und eine Liste ist garantiert kein Weg, um mehrere Funktionen aufzurufen, dazu schreibt man eine Funktion.
Man benutzt kein Lambda, wo man doch eine Klasse zur Verfügung hat. Eine Methode, die kein self benutzt, ist keine Methode, und da Du das nicht einmal in __init__ verwendest, hast Du eigentlich gar keine Klasse.
DoubleVar sollte auch ein root-Objekt übergeben werden, und Deine ganzen Buttons sollten nicht im `master` landen, sondern im Frame, den Du ja extra dafür erzeugst.
Wenn Du schon ein DoubleVar hast, dann solltest Du das auch nutzen.
Bevor Du tiefer in die GUI-Programmierung einsteigst, solltest Du dringend lernen, was Klassen sind und wie man sie benutzt.

Code: Alles auswählen

import tkinter as tk

class SpinWidget(tk.Frame):
    def __init__(self, master, name, step_size, step_count, value):
        tk.Frame.__init__(self, master)
        self.step_size = step_size
        self.step_count = step_count
        self.initial_value = value
        self.step = 0
        self.value = tk.DoubleVar(self, value)
        self.plus_button = tk.Button(self, text='Plus', command=self.step_up)
        self.plus_button.pack()
        tk.Label(self, textvariable=self.value).pack()
        self.minus_button = tk.Button(self, text='Minus', command=self.step_down)
        self.minus_button.pack()
        tk.Label(self, text=name).pack()

    def update(self):
        self.value.set(self.initial_value + self.step * self.step_size)
        self.plus_button['state'] = tk.DISABLED if self.step >= self.step_count else tk.ACTIVE
        self.minus_button['state'] = tk.DISABLED if self.step <= -self.step_count else tk.ACTIVE

    def step_up(self):
        if self.step < self.step_count:
            self.step += 1
            self.update()

    def step_down(self):
        if self.step > -self.step_count:
            self.step -= 1
            self.update()
        

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        cage_offset = SpinWidget(self, "Cage", step_size=0.001, step_count=3, value=0)
        cage_offset.pack()
        

def main():
    App().mainloop()

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