@Rigoletto: Als erstes sollte man den Hauptframe nicht mit `pack()` oder einem anderen Layout-Manager auf das `Canvas` packen, denn dann expandiert das nicht nach unten ins ”unsichtbare”, sondern ist weiterhin durch das Fenster begrenzt. Wenn man Widgets als Objekte auf dem `Canvas` haben möchte gibt es die `create_window()`-Methode auf `Canvas`-Exemplaren.
Dann gehört der Scrollbalken nicht auf das `Canvas` sondern daneben.
Und zuguter Letzt muss man beim Canvas noch die 'scrollregion' passend setzen nachdem die Widgets im darauf angezeigten `Frame` layoutet wurden, damit der Scrollbalken passend dargestellt werden und steuern kann.
Sonstiges: Die Namensschreibweisen halten sich nicht an den
Style Guide for Python Code. Klassen fangen per Konvention mit einem Grossbuchstaben an, und Methoden `klein_mit_unterstrichen`. Es gibt auch einige sehr schlechte Namen. Einbuchstabige Namen ausser den weit verbreiteten `i`, `j`, `k` für ganzzahlige Index/Laufvariablen sind ausserhalb sehr begrenzter Gültigkeitsbereiche („list comprehension”, Generatorausdruck, ``lambda``-Funktion) selten wirklich geeignet um dem Leser zu vermitteln was die Werte dahinter *bedeuten*. Diese nichtsagenden Namen innerhalb einer Funktion dann auch noch an verschiedene Dinge zu binden (`e` in `xmlFrame()`) ist schon fast kriminell.
Die Klassenattribute haben da nichts zu suchen, die gehören in die `__init__()` denn das sind alles Attribute die auf das Exemplar gehören. Die API dieser Klasse ist auch ein wenig komisch verteilt. Damit das irgendetwas sinnvolles macht *muss* man nach dem erstellen die `test()`-Methode aufrufen. Damit gehört zumindest ein Teil davon eigentlich in die `__init__()`-Methode.
Das was Du da in der `__init__()` als `parent` bezeichnest ist etwas anderes. `Tk`-Exemplate haben kein Elternwidget, das ist *das* Hauptfenster, dementsprechend erwartet deren `__init__()` auch kein solches Argument. Kannst ja mal in der Dokumentation nachsehen was Du da stattdessen auf `None` setzt.
`self.xmltree` wird nicht wirklich verwendet. An der Stelle empfehle ich `lxml.etree` anstelle des `ElementTree` aus der Standardbibliothek, denn dort gibt es neben `iterparse()` auch eine `iterwalk()`-Funktion die statt eine Datei zu parsen die gleiche API für einen bereits bestehenden `ElementTree` beziehungsweise für ein beliebiges `Element` bietet.
`xmlFrame()` ist keine Methode. Das sollte man entweder entsprechend deutlich machen in dem man eine `staticmethod()` daraus macht, oder es als die Funktion aus der Klasse herausziehen die das eigentlich ist.
Ich komme dann ungefähr bei so etwas heraus (ungetestet):
Code: Alles auswählen
import tkinter as tk
from lxml import etree
def create_label_frame(parent, text):
frame = tk.Frame(parent, background='red', relief=tk.RAISED, borderwidth=5)
tk.Label(frame, text=text).pack(side=tk.TOP)
return frame
class CodeGui(tk.Tk):
def __init__(self, xml_filename):
tk.Tk.__init__(self)
self.xml_tree = etree.parse(xml_filename)
self.xml_root = self.xml_tree.getroot()
self.canvas = tk.Canvas(self, background='green')
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.xml_frame = tk.Frame(
self.canvas, background='yellow', relief=tk.RAISED, borderwidth=10
)
self.canvas.create_window((0, 0), anchor=tk.NW, window=self.xml_frame)
self.vscrollbar = tk.Scrollbar(
self,
background='blue',
orient=tk.VERTICAL,
command=self.canvas.yview
)
self.vscrollbar.pack(side=tk.LEFT, fill=tk.Y)
self.canvas['yscrollcommand'] = self.vscrollbar.set
self._frames_from_xml()
def dump_slaves(self):
for slave in self.slaves():
print(type(slave), slave)
def _frames_from_xml(self):
parent_frames_stack = []
parent_frames_stack.append(self.xml_frame)
for event, node in etree.iterwalk(
self.xml_root, events=('start', 'end')
):
if event == 'start':
frame = create_label_frame(parent_frames_stack[-1], str(node))
frame.pack(side=tk.TOP)
parent_frames_stack.append(frame)
elif event == 'end':
parent_frames_stack.pop()
else:
assert False, event
self.update_idletasks()
self.canvas['scrollregion'] = self.canvas.bbox(tk.ALL)
def main():
gui = CodeGui('test.xml')
# gui.dump_slaves()
gui.mainloop()
if __name__ == '__main__':
main()