verdammt, ich kapier es einfach nicht

Fragen zu Tkinter.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Dienstag 19. August 2008, 14:15

Ja, könnte man so sagen.
Ein Frame gibt so viel Platz frei, wie die in ihm platzierten Widgets für die Anzeige benötigen bzw. einfordern. Mittels der Optionen des packers (fill und extend) kann man diesen Platzbedarf eben auch künstlich in die Höhe schrauben.

Und mittels verschachtelter Frames bekommst du letztlich alles hin, ohne dabei auf die Flexibilität des pack()-Managers gegenüber place() verzichten zu müssen.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Mittwoch 20. August 2008, 17:35

Sorry, muss mich korrigieren;
So ist es besser:

Canvas-Scroll-Resizing Pattern

Code: Alles auswählen

import Tkinter as tk

class Scroller(object):

  
  def __init__(self, root):

    self.root = root
    self.frame = tk.Frame(self.root)
    self.canvas = tk.Canvas(self.frame, width=100, height=100, bg='white')

    self.x_scroll = tk.Scrollbar(self.frame, orient='horizontal')
    self.canvas.config(xscrollcommand=self.x_scroll.set)
    self.x_scroll.config(command=self.canvas.xview)

    self.y_scroll = tk.Scrollbar(self.frame, orient='vertical')
    self.canvas.config(yscrollcommand=self.y_scroll.set)
    self.y_scroll.config(command=self.canvas.yview)

    w = 999  
    self.canvas.config(scrollregion=(0, 0, w, w))
    self.canvas.grid(row=0, column=0, sticky=tk.N+tk.S+tk.W+tk.E)

    self.frame.pack(fill=tk.BOTH, expand=1)
    self.y_scroll.grid(row=0, column=1, sticky=tk.N+tk.S)
    self.x_scroll.grid(row=1, column=0, sticky=tk.E+tk.W)

    # enable canvas resizing
    self.frame.grid_columnconfigure(0, weight=1)
    self.frame.grid_rowconfigure(0, weight=1)

    p0 = (0, 0)
    p1 = (w >> 1, w - 1)
    p2 = (w - 1, 0) 
    self.canvas.create_line(p0, p1, p2)


if __name__ == '__main__':
    
  root = tk.Tk()
  Scroller(root)
  root.mainloop()
Nimmt man pack() fuer die Scrollbars, so stehen
diese aufeinander, was sehr unschoen und 'unprofessionell'
aussieht, daher habe ich grid() genommen, damit diese
sich nur an einer Ecke beruehren.

Wie numerix schon sagte, kann man pack() und grid()
indirekt(!) mixen, indem man diese in verschiedenen Frames benutzt.

:wink:
yipyip
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Mittwoch 20. August 2008, 22:30

ich habe den Code von Numerix jetzt von links nach rechts, vor und zurück probiert. Ich muss sagen, ich resigniere gerade.

Was ich dabei nicht kapiere.

1. ich erstelle ein Hauptfenster mit :

Code: Alles auswählen

spiel = tk.Tk()
spiel.geometry("1024x768")
2. Da es ein Canvas und zwei Scrollbalken unter einem Hut
geben soll, wird danach ein Frame erstellt :

Code: Alles auswählen

frame1 = tk.Frame(spiel)
frame1.pack(side=tk.LEFT,fill=tk.BOTH,expand=True)
UND ich verstehe einfach nicht, warum es SOOOO komliziert ist,
diesen Frame in die linke obere Ecke zu platzieren ?

Geht das nicht einfacher ?
Warum kann man dieses nur links, rechts, oben und unten ?

Oder ist das nicht so ?

Wann benutze ich SIDE und wann benutze ich ANCHOR ?

Bisher war Python für mich schierig, aber immer wieder nach genügend nachdenken auch wieder logisch.
Hier hört meine Logik aber auf ?

Und daher noch einmal meine Frage -
Warum nicht place ?
Das wäre um ein vielfaches einfacher, wenn man eine Spielfläche Stück für Stück erweitern möchte.

... duck und weg

Kai
imac
20 Zoll
2,4 ghz
BlackJack

Mittwoch 20. August 2008, 23:39

Du gehst einfach falsch an die Sache heran. Du fängst mit einem grossen leeren Container mit einer festen Grösse an und willst da etwas oben links platzieren. Das geht so nicht.

Hör auf dem Fenster eine feste Grösse zu geben und platziere Deinen Frame da drin. Wenn Du dann rechts und unten weitere Elemente unterbringst, dann ist der Frame *automatisch* oben links.

Mit `pack()` kann man Widgets von links nach rechts, rechts nach links, oben nach unten, oder unten nach oben in einem Container anordnen. Da man auch Container wieder in Containern anordnen kann, ist letztendlich jede Richtung/Kombination möglich.

`side` benutzt man um zu sagen an welcher Seite im Container das Widget hinzu gefügt werden soll. `anchor` bestimmt, wo im zur Verfügung stehenden Platz das Widget verankert werden soll.

Du musst bei `pack()` und `grid()` gedanklich nicht von einem grossen Fenster ausgehen wo Sachen platziert werden, sondern von den einzelnen Widgets die zu grösseren zusammen gesetzt werden. Widgets haben in der Regel eine minimale Grösse und die Container legen sich da so eng wie möglich drum herum. Wenn ein Widget in einem Container von freier Fläche umgeben ist, weil andere Widgets im gleichen Container höher oder breiter sind, kann man mit `anchor` festlegen wo das Widget innerhalb der Freifläche "hin rutschen" soll. Wenn man nichts angibt, dann wird es mittig platziert. Oder man gibt mit `fill` an, dass das Widget nicht mit seiner mimimalen Grösse angezeigt werden soll, sondern vorhandenen Platz ausfüllen soll.

`place()` ist Mist, weil Du damit keine GUIs hinbekommst die überall korrekt angezeigt werden und Änderungen sehr kompliziert werden.
Benutzeravatar
wuf
User
Beiträge: 1481
Registriert: Sonntag 8. Juni 2003, 09:50

Donnerstag 21. August 2008, 10:58

Hallo derkai

Wer mit Tkinter eine GUI erstellt wird nicht um dessen Layout-Möglichkeiten herumkommen. Dabei ist das Wort 'Frustration' und 'Resignation' keine Seltenheit. Wenn einer behauptet er sei noch nie mit den beiden Zuständen konfrontiert worden lügt aus meiner Sicht. Gewisse Layouteigenschaften können sich auf den verschiedenen OS-Platformen und Tk-Versionen auch wieder unterschiedlich verhalten.

place() (präziser Plattenleger)
grid() (Gefängniswärter)
place() Künstler und Zauberer

Da muss sich schlussendlich jeder selbe entscheiden welchen Layoutmanager er einsetzen will.

Der Pack-Layoutmanager ist sicher keine schlechte Variante. Hier kannst du mit einer Unzahl von Frames fast alles hinkriegen. Ich würde aber die GUI wie hier schon einige Male erwähnt wurde planen. Es muss nicht unbedingt mit Bleistift, Papier und Radiergummi sein du kannst auch mit einem Zeichen-Programm z.B. 'Draw', welches zum OpenOffice gehört verwenden.

Meistens ist die Planung eine Top-Down Angelegenheit. Zuerst die äusseren Frames, dann die inneren. Auch im Skript sollten der Code für die die einzelnen Frames nicht Zeile an Zeile geschrieben werden. Zwischenräume sind und Dokumentation sind nicht verboten. Wird dies vernachlässigt, verliert einer schnell einmal die Übersicht.

Bei den Platzierungs-Optionen 'side' und 'anchor' würde ich einemal versuchen nur die 'side'-Option zu gebrauchen.

Hier ein Beispiel wie du eines deine Frames in der oberen linken Ecke platzierst:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# Skriptname: frame_test_01.py

import Tkinter as tk

if __name__ == '__main__':

    root = tk.Tk()

    #~~ Behälter in welchem mein Frame links oben platziert werden soll.
    #   Dieser Behälter wird an die linke Seite des noch nicht skalierten
    #   Hauptfenster 'root' geklebt.
    container_frame_left = tk.Frame(root)
    container_frame_left.pack(side='left', fill='y')

    #~~ Mein Frame das ich links oben platzieren möchte
    #   Das Frame wird an die obere Kante des Behälters 'container_frame_left'
    #   geklebt.
    my_left_top_frame = tk.Frame(container_frame_left, bg="steelblue3",
        width=200, height=200)
    my_left_top_frame.pack(side='top')

    #~~ Das Hauptfenster 'root' wird auf die gewünschte Grösse gebracht
    root.geometry("1024x764")

    root.mainloop()
Was möchtest du als nächstes?

Gruss wuf :wink:
Take it easy Mates!
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Donnerstag 21. August 2008, 17:03

gut, ich habe es einmal mit meinen "Worten" nachgebildet :

Code: Alles auswählen

import Tkinter as tk
from funktionen import *

# hier wird das Spielfenster erstellt
spiel = tk.Tk()

# hier wird der linke Container der Spielflaeche erstellt
container1=tk.Frame(spiel)
container1.pack(side=tk.LEFT,fill=tk.Y)

#hier wird der Container der Spielflaeche erstellt
container2=tk.Frame(container1,bd=3,relief=tk.SUNKEN,width=500,height=450)
container2.pack(side=tk.TOP,padx=5,pady=5)

spiel.geometry("1024x768")
spiel.mainloop()

Als nächstes kämen die Scrollbalken dran.

Kai
imac
20 Zoll
2,4 ghz
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Donnerstag 21. August 2008, 17:17

und jetzt kommt mein erstes Problem.
Die Xscrollbar passt, die Y nicht :

Code: Alles auswählen

import Tkinter as tk
from funktionen import *

# hier wird das Spielfenster erstellt
spiel = tk.Tk()

# hier wird der linke Container der Spielflaeche erstellt
container1=tk.Frame(master=spiel)
container1.pack(side=tk.LEFT,fill=tk.Y)

# hier wird der Container der Spielflaeche erstellt
container2=tk.Frame(container1,bd=3,relief=tk.SUNKEN,width=500,height=450)
container2.pack(side=tk.TOP,padx=5,pady=5)

# erstellen der Scrollbalken
schieber_x = tk.Scrollbar(container1,orient=tk.HORIZONTAL)
schieber_x.pack(side=tk.TOP,fill=tk.X)
schieber_y = tk.Scrollbar(container1,orient=tk.VERTICAL)
schieber_y.pack(side=tk.LEFT,fill=tk.Y)




spiel.geometry("1024x768")
spiel.mainloop()
imac
20 Zoll
2,4 ghz
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Donnerstag 21. August 2008, 19:48

Ich verstehe dich nicht.

Ich habe dir doch hier http://www.python-forum.de/topic-15264,270.html den fertigen Code für deine geplante GUI gepostet (Mo Aug 18, 2008 22:24).

Analysier das doch mal.
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Donnerstag 21. August 2008, 19:56

...das habe ich ja, aber ich verstehe es nicht !

und ich möchte nun einmal nichts übernehmen, was ich nicht verstehe.
Das würde mich zwar vorerst weiter bringen, aber bisher habe ich an allem
so lange "rumgemacht" bis der Groschen gefallen ist.
Daher war mir (und das soll Deinen Code ja auch gar nicht schmälern)
das Angebot von wuf auch sehr gelegen es erneut und Stück für Stück aufzubauen.

Wie Du siehst, habe ich mir ja Gedanken gemacht. Aber das sind mir
zu viele Schritte auf einmal.

Ich hänge an der Y Scrollbar fest.
Die muss man doch an meinen Schnipsel anhängen können ?!

Kai
imac
20 Zoll
2,4 ghz
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Donnerstag 21. August 2008, 20:29

Nun denn ...

Die Scrollbars sollen doch rechts und unten sein (denke ich). Dann musst du sie auch nach rechts und unten setzen - nicht nach oben und nach links.

Außerdem würde ich sie eher an container2 als container1 binden.

Mach das mal. Ergebnis sollte sein, dass container2 ziemlich klein ist und nur die Ansätze von Scrollbars erkennbar sind. Das liegt daran, dass die Größenangaben für das Frame-Widget nichts bringen.
Du musst im Frame (container2) ein Widget platzieren, das eine bestimmte Größe für sich beansprucht, also z.B. ein Canvas-Widget mit vorgegebener Größe.
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Donnerstag 21. August 2008, 21:04

das habe ich geändert - vielen Dank
Aber es führt nicht zu dem Ergebnis, dass die Y Bar rechts neben
dem Container 2 auftaucht :

Code: Alles auswählen

import Tkinter as tk
from funktionen import *

# hier wird das Spielfenster erstellt
spiel = tk.Tk()

# hier wird der linke Container der Spielflaeche erstellt
container1=tk.Frame(master=spiel,bd=3,relief=tk.RAISED)
container1.pack(side=tk.LEFT,fill=tk.Y)

# hier wird der Container der Spielflaeche erstellt
container2=tk.Frame(container1,bd=3,relief=tk.SUNKEN)
container2.pack(side=tk.TOP,padx=5,pady=5)

# hier wird das Spieldfeld erstellt und in Container2 eingebunden
spielfeld = tk.Canvas(container2,bg="white",width=500,height=450)
erstelle_hexfeldobjekte ()
for x in hexdic.iterkeys() :    
    spielfeld.create_polygon (hexdic[x][0],fill=hexdic[x][2],outline="black")
spielfeld.pack()

# hier werden die Scrollbalken erstellt
schieber_x = tk.Scrollbar(container2,orient=tk.HORIZONTAL)
schieber_x.pack(side=tk.BOTTOM,fill=tk.X)
schieber_y = tk.Scrollbar(container2,orient=tk.VERTICAL)
schieber_y.pack(side=tk.RIGHT,fill=tk.Y)

spiel.geometry("1024x768")
spiel.mainloop()


... es wird immer unterhalb des Canvas erstellt.


Kai
imac
20 Zoll
2,4 ghz
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Donnerstag 21. August 2008, 21:21

ha, ich habs !!!
so geht es - warum muss ich zwar selber noch einmal drüber nachdenken,
aber so geht es

Kai :

Code: Alles auswählen

import Tkinter as tk
from funktionen import *

# hier wird das Spielfenster erstellt
spiel = tk.Tk()

# hier wird der linke Container der Spielflaeche erstellt
container1=tk.Frame(master=spiel,bd=3,relief=tk.RAISED)
container1.pack(side=tk.LEFT,fill=tk.Y)

# hier wird der Container der Spielflaeche erstellt
container2=tk.Frame(container1,bd=3,relief=tk.SUNKEN)
container2.pack(side=tk.TOP,padx=5,pady=5)

# hier werden die Scrollbalken erstellt
schieber_x = tk.Scrollbar(container2,orient=tk.HORIZONTAL)
schieber_x.pack(side=tk.BOTTOM,fill=tk.X)
schieber_y = tk.Scrollbar(container2,orient=tk.VERTICAL)
schieber_y.pack(side=tk.RIGHT,fill=tk.Y)

# hier wird das Spieldfeld erstellt und in Container2 eingebunden
spielfeld = tk.Canvas(container2,bg="white",width=500,height=450)
erstelle_hexfeldobjekte ()
for x in hexdic.iterkeys() :    
    spielfeld.create_polygon (hexdic[x][0],fill=hexdic[x][2],outline="black")
spielfeld.pack()


spiel.geometry("1024x768")
spiel.mainloop()




imac
20 Zoll
2,4 ghz
Benutzeravatar
wuf
User
Beiträge: 1481
Registriert: Sonntag 8. Juni 2003, 09:50

Freitag 22. August 2008, 11:46

Hallo derkai

Hier dein Code-Snippet leicht verbessert und erweitert. Bei mir hinterlässt die Polygon-Generierung eine Fehlermeldung. Darum ist dieser Abschnitt auskommentiert:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# Skriptname: frame_test_02.py

import Tkinter as tk
from funktionen import *

# Scrollbacken-Kontrastfarbe
COLOR_BG_SCROLLBAR = 'gray80'

# hier wird das Spielfenster erstellt
spiel = tk.Tk()

# hier wird der linke Container der Spielflaeche erstellt
container1=tk.Frame(master=spiel,bd=3,relief=tk.RAISED)
container1.pack(side=tk.LEFT,fill=tk.Y)

# Container der Spielflaeche erstellt
container2=tk.Frame(container1,bd=0,relief=tk.SUNKEN)
container2.pack(side=tk.TOP,padx=5,pady=5)

# Container fuer Spielfeld und den vertikalen Scrollbalken
container3 = tk.Frame(container2)
container3.pack(side=tk.TOP, fill=tk.BOTH)

# Container fuer den horizontalen Scrollbalken und Freifeld
container4 = tk.Frame(container2)
container4.pack(side=tk.BOTTOM, fill=tk.X)

# hier werden die Scrollbalken erstellt
schieber_x = tk.Scrollbar(container4, bd=1, bg=COLOR_BG_SCROLLBAR,
    orient=tk.HORIZONTAL)
schieber_x.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES)
schieber_y = tk.Scrollbar(container3, bd=1, bg=COLOR_BG_SCROLLBAR,
    orient=tk.VERTICAL)
schieber_y.pack(side=tk.RIGHT,fill=tk.Y, expand=tk.YES)

# Freifeld (unten links)
space_square = tk.Frame(container4, bd=0, width=16, height=16)
space_square.pack(side=tk.LEFT, fill=tk.BOTH)

# hier wird das Spieldfeld erstellt und in Container3 eingebunden
spielfeld = tk.Canvas(container3, bd= 3, relief=tk.SUNKEN, bg="white",
    width=500,height=450)
spielfeld.pack(side=tk.LEFT, fill=tk.BOTH)

# Binde die vertikale & horizontale Ziehleisten an die Canvas-Flaeche
spielfeld['xscrollcommand'] = schieber_x.set
spielfeld['yscrollcommand'] = schieber_y.set
schieber_x['command'] = spielfeld.xview
schieber_y['command'] = spielfeld.yview

erstelle_hexfeldobjekte (40,30)
# for x in hexdic.iterkeys() :
#     spielfeld.create_polygon (hexdic[x],fill=hexdic[x][2],outline="black")
#     spielfeld.create_polygon (hexdic[x][0:12],fill='red',outline="black")

if spielfeld.bbox('all'):
    # Das Spielfeld enthaelt Garfikobjekte. Passe den Ziehberiech der
    # Scrollbalken auf den neuen Inhalt (Hexagons) des Spielfeldes an
    x1, y1, x2, y2 = spielfeld.bbox('all')
    spielfeld.configure(scrollregion=(x1, y1, x2+2, y2+2))

spiel.geometry("1024x768")

spiel.mainloop()
Gruss wuf :wink:
Take it easy Mates!
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Freitag 22. August 2008, 18:31

Hallo Wuf, wieder einmal toll, dass Du Dir Gedanken gemacht hast.
Ich komme mir fast vor wie in der Schule.

Dein Code bietet mir wieder neues und altes, was ich sowiso
jetzt als Nächstes hätte in Angriff nehmen wollen.

Die Anzahl und Anordnung der Container muss ich mir noch einmal
in Ruhe anschauen. Es ergeben sich aber direkt zwei Fragen.

1) diese Zeile hier :

Code: Alles auswählen

# Binde die vertikale & horizontale Ziehleisten an die Canvas-Flaeche
spielfeld['xscrollcommand'] = schieber_x.set
ist doch nur ein anderes Format für : schieber_x.config(command=spielfeld.xview)

oder ?

Wenn ich das aber in meinen Code einfüge, dann verschwindet aus
dem Scrollbalken der Slider.

Muss hier eine einbindung erfolgen ? Was macht die ?
Mein Code funktioniert auch sooo, allerdings bin ich mit den Scrollbalken
bei mir aber auch nicht zufrieden, da der Bereich irre groß ist.

Und das bringt mich dann zu Frage 2 )

Code: Alles auswählen

if spielfeld.bbox('all'):
    #Das Spielfeld enthaelt Garfikobjekte. Passe den Ziehberiech der
    #Scrollbalken auf den neuen Inhalt (Hexagons) des Spielfeldes an
    x1, y1, x2, y2 = spielfeld.bbox('all')
    spielfeld.configure(scrollregion=(x1, y1, x2+2, y2+2))
Was ist das ?
bbox muss demnach eine Funktion sein, der Du "all" übergibst.
Wofür steht denn "all" ?

Danach wird etwas entpackt und den Namen x1, usw zugewiesen.
Dann wird die Spielfläche nachträglich konfiguriert und eine Scrollregion
mit den Werten x1 usw... festgelegt.

Was das alles macht, ist mir klar, aber nicht wie es funktioniert.

- Was ist also bbox ?
- Was wird hier mit "all" übergeben ?
- und was ist scrollregion ?

Tja, ich weiss, Fragen über Fragen.
Aber es geht ja um´s verstehen

Kai
imac
20 Zoll
2,4 ghz
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Freitag 22. August 2008, 18:53

derkai hat geschrieben: - Was ist also bbox ?
- Was wird hier mit "all" übergeben ?
- und was ist scrollregion ?
Benutzt du eigentlich auch eine Tkinter-Referenz? Da steht sowas doch alles drin. Hier zum Beispiel:

http://infohost.nmt.edu/tcc/help/pubs/t ... thods.html
Antworten