@Nobuddy: bei 881 Beiträgen hier im Forum ist es sehr unwahrscheinlich, dass nicht alle Anmerkungen schomal bei Dir gelandet sind, aber hier nochmal, in der Hoffnung dass es irgend wann einmal fruchtet:
Eingerückt wird immer mit 4 Leerzeichen pro Eben, KEINE Tabs.
`WindowBuilding` ist keine Klasse, es besteht nur aus __init__ indem nicht einmal self benutzt wird.
Dass hier ausschließlich mit `parent` gearbeitet wird, sagt aber eindeutig, dass das alles in der __init__-Methode der `parent`-Klasse stehen sollte.
`screenx` und `screeny` werden nicht benutzt.
Dass weitere Klasseninstanze erzeugt werden, ohne dass sie einer Variable zugewiesen werden oder sonst was damit gemacht wird, deutet auf einen Designfehler hin, später wohl noch mehr dazu.
In `ControlAutoScrollbar.set` wird tk.call benutzt, warum nicht direkt grid_remove?
raise sollte eine Exception-Instanz bekommen, kein Tuple. Woher hast Du denn sowas?
`AutoScrollBar` setzt Attribute in `parent`. Das ist total unübersichtlich, verhindert, dass man mehrere dieser Objekte im selben Fenster verwenden kann. Üblicherweise sind komplexere Widgets von Frame abgeleitet, die man dann im `parent` beliebig plazieren kann. `AutoScrollBar` ist dann der falsche Name, weil es sich ja nicht nur um Scrollbars handelt, sondern tatsächlich ein AutoScrollFrame ist.
`OutputWindow` ist dann wieder keine Klasse, weil wieder in __init__ nichteinmal `self` benutzt wird.
Das ganz gehört also wieder in die __init__-Methode der `parent`-Klasse.
Das was bei Dir an `parent.frame` gebunden ist, wird in `AutoScrollBar` erzeugt, dann aber erst in `OutputWindow` ins Canvas gesetzt. Sehr unübersichtlich. In wirklichkeit wird der Frame dann aber gar nicht benutzt.
`AttributeError` deutet normalerweise auf Programmierfehler hin, hier ist es ein Zeichen dafür, dass Du die Übersicht verloren hast, ob Attribute existieren, oder nicht.
Der ganze Code zum Ermitteln der Bildschirmgröße gehört gelöscht. Es ist Aufgabe des Fenstermanagers (der die Präferenzen des Nutzers umsetzt) ein Fenster zu platzieren, die einzelnen Programm sollten es nicht besser wiessen wollen.
`mainloop` sollte nicht in `__init__` stehen, sondern, denn dann läuft __init__ ewig, man hat also nie ein fertig initialisierte Klasse; der Kommentar `# Create window.` ist zudem noch falsch.
Die Klasse `ScreenCenter` zentriert gar kein Fenster (was auch Aufgabe einer Funktion wäre und nicht einer Klasse), ist wieder keine richtige Klasse, sollte eine Funktion mit dem Namen `get_screen_center` sein, fliegt aber eh raus, weil wie schon geschrieben, Fenster nicht explizit platziert werden.
Die Klasse `start` ist nicht nur falsch geschrieben, `start` ist auch kein Name für eine Klasse. Importe gehören an den Anfang der Datei, nicht in Funktionen, und Klasse gehören auch nicht in Funktionen.
`xpos` und `ypos` sind keine Attribute, weil sie nur innerhalb von `task` in einer Schleife benutzt werden.
Man speichert nicht zusammengehörende Daten, wie Label und Dataset in zwei Listen, sondern in einer Liste.
Die StringVar-Objekte werden gar nicht benutzt, sind eh nur Konstanten.
Die grid-Aufrufe sind überflüssig, da Du ja create_window benutzt. Würdest Du anchor=NW benutzen, wäre das die obere Linke Ecke zur Positionierung, was viel intuitiver wäre als die Mitte der rechten Seite.
Code: Alles auswählen
import tkinter as tk
class ControlAutoScrollbar(tk.Scrollbar):
def set(self, low, high):
if float(low) <= 0.0 and float(high) >= 1.0:
self.grid_remove()
else:
self.grid()
tk.Scrollbar.set(self, low, high)
def pack(self, **kw):
raise TclError("pack cannot be used with this widget")
def place(self, **kw):
raise TclError("place cannot be used with this widget")
class AutoScrollFrame(tk.Frame):
def __init__(self, parent, bg_scroll='green', scrollactiv='red', scrolltrough='yellow'):
tk.Frame.__init__(self, parent.win)
yscrollbar = ControlAutoScrollbar(self,
bg=bg_scroll, activebackground=scrollactiv,
troughcolor=scrolltrough)
yscrollbar.grid(row=0, column=1, sticky=tk.NS)
xscrollbar = ControlAutoScrollbar(self,
orient=tk.HORIZONTAL, bg=bg_scroll,
activebackground=scrollactiv,
troughcolor=scrolltrough)
xscrollbar.grid(row=1, column=0, sticky=tk.EW)
self.canvas = tk.Canvas(self,
yscrollcommand=yscrollbar.set, xscrollcommand=xscrollbar.set)
self.canvas.grid(row=0, column=0, sticky=tk.NSEW)
yscrollbar.config(command=self.canvas.yview)
xscrollbar.config(command=self.canvas.xview)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def update_size(self):
self.canvas.config(scrollregion=self.canvas.bbox(tk.ALL))
class Application(object):
def __init__(self):
# Create window by Tk or Toplevel.
self.win = tk.Tk()
self.last_widget = None
# Initializing root and create canvas and frame.
self.scrollframe = AutoScrollFrame(self)
self.scrollframe.pack(fill=tk.BOTH, expand=True)
self.create_tasks()
self.scrollframe.update_size()
def create_tasks(self):
"""
Create Widgets
"""
canvas = self.scrollframe.canvas
datasets = [
(str(i), f'Das ist Value {i} von Datensatz.')
for i in range(10)
]
ypos = 0
for label_text, dataset_text in datasets:
label_head = tk.Label(canvas, width=10, bd=1,
highlightthickness=1, text=label_text, anchor=tk.NW)
label_data = tk.Label(canvas, width=30, bd=1,
highlightthickness=1, text=dataset_text, anchor=tk.NW)
width = label_head.winfo_reqwidth()
canvas.create_window(0, ypos, window=label_head, anchor=tk.NW)
canvas.create_window(width, ypos, window=label_data, anchor=tk.NW)
if self.last_widget is None:
self.last_widget = label_data
label_data.focus_set()
ypos += label_head.winfo_reqheight()
def main():
application = Application()
application.win.mainloop()
if __name__ == '__main__':
main()
Zum Problem: ein Canvas kann beliebig in die positive und negative Richtung gehen, und da Du die Scrollbars an die Canvas-Größe anpasst, ging im ersten Programm der Scrollbar von -0.5 * label_height bis (n - 0.5) * label_height, so dass bei der default-Scrollbar-Position 0 nach oben noch ein bißchen Platz war.
Ein deinem zweiten Programm geht jetzt der Scrollbar von + 0.5 * label_height bis (n + 0.5) * label_height. Jetzt liegt der default-Wert 0 außerhalb des Scrollbar-Bereichs, wird also automatisch auf den kleinsten Wert gesetzt, also 0.5 * label_height, was jetzt den Anschein hat, dass alles paßt.
Das ist aber nicht der Fall, denn in beiden Fällen bist Du ein halbes Label daneben.