Seite 14 von 16

Verfasst: Montag 18. August 2008, 19:03
von derkai
sagt mal, kann ich denn nicht auch grid nehmen ?

Kai

Verfasst: Montag 18. August 2008, 19:18
von numerix
derkai hat geschrieben:sagt mal, kann ich denn nicht auch grid nehmen ?
Klar, kannst du. Aber einfacher wird es dadurch m.E. nicht.
Da du - bisher jedenfalls - auch keine Widgets hast, die gleiche Abmessungen bekommen sollen, musst du sehen, dass du durch geeignetes Zusammenfassen von Zellen entsprechende Megazellen bekommst. Ich persönlich finde das wesentlich unpraktischer als die Arbeit mit dem pack-Manager und ggf. einigen verschachtelten Frames. Wenn man denen ordentliche Namen gibt, dann behält man auch bei etwas Verschachtelung gut den Überblick.

Verfasst: Montag 18. August 2008, 19:24
von yipyip
...musste auch erst einen Abend und einen Nachmittag
googeln und experimentieren bis ich's so hatte wie es sein soll:

Das 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')

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

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

    w = 999  
    self.canvas.config(scrollregion=(0, 0, w, w))
    self.canvas.pack(side=tk.LEFT, expand=1, fill=tk.BOTH)

    self.frame.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
    yscroll.grid(row=0, column=1, sticky=tk.N+tk.S)
    xscroll.grid(row=1, column=0, sticky=tk.E+tk.W)

    self.root.grid_columnconfigure(0, weight=1)
    self.root.grid_rowconfigure(0, weight=1)

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


if __name__ == '__main__':
    
  root = tk.Tk()
  Scroller(root)
  root.mainloop()
Versuche das mal. :D

Bei Dir fehlte u.a. ein

Code: Alles auswählen

fenster1.pack(side=tk.LEFT, ...
...mehr sage ich lieber nicht...
:roll:

Wichtigste Regel:
Tkinter-Funktionalitaet nie mit eigenem umfangreichen Code testen, sondern nur mit Minimalfunktionen.
:wink:
yipyip

Verfasst: Montag 18. August 2008, 19:26
von derkai
ja, aber warum bekomme ich denn dann keinen Frame mehr RECHTS NEBEN die Spielfläche ?

Kai

Verfasst: Montag 18. August 2008, 19:28
von yipyip
ups, war etwas zu spaet...

Verfasst: Montag 18. August 2008, 19:34
von derkai
@yip yip

ne ne, nicht zu spät. ich gucke mir alles an
ich glaube aber inzwischen, dass die CANVAS bereits etwas
eingebaut haben, was die Funktion xview vereinfacht.

schönen Dank -
cih schaue es mir gleich mal an.
Ich habe aber gesehen, dass auch Du grid verwendet hast.

Davon raten ja leider alle ab.

Kai

Verfasst: Montag 18. August 2008, 19:51
von numerix
derkai hat geschrieben:ja, aber warum bekomme ich denn dann keinen Frame mehr RECHTS NEBEN die Spielfläche ?
Weil du den pack()-Manager nicht richtig einsetzt ...

Guckst du hier:

Code: Alles auswählen

import Tkinter as tk

spiel = tk.Tk()
spiel.geometry("1024x768")
linkeseite = tk.Frame(spiel,bg="tomato",width=100)
linkeseite.pack(side=tk.LEFT,fill=tk.Y)
rechteseite = tk.Frame(spiel,bg="peachpuff",bd=3,relief=tk.SUNKEN)
rechteseite.pack(side=tk.LEFT,fill=tk.BOTH,expand=True)
spflaeche = tk.Canvas(rechteseite,bg="white")
spflaeche.pack(expand=True,fill=tk.BOTH,padx=25,pady=30)
rechtsnebencanvas = tk.Frame(spiel,bg="lightblue",width=100)
rechtsnebencanvas.pack(side=tk.LEFT,fill=tk.Y)
schieber = tk.Scrollbar(rechteseite,orient=tk.HORIZONTAL)
schieber.pack(fill=tk.X)
spiel.mainloop()

Verfasst: Montag 18. August 2008, 20:05
von derkai
ok, aber wenn ich jetzt mal einen Teil aus deinem Code nehme :


Code: Alles auswählen

import Tkinter as tk

spiel = tk.Tk()
spiel.geometry("1024x768")

linkeseite = tk.Frame(spiel,bg="tomato",width=100)
linkeseite.pack(side=tk.LEFT,fill=tk.Y)

spiel.mainloop()
dann füllst Du nach oben auf : fill=tk.Y

lasse ich das jetzt weg und begrenze das Spielfeld auch in der Höhe,
dann komme ich mit side=tk.LEFT nicht weiter.

dann hilft nur :anchor=tk.nw


Code: Alles auswählen

import Tkinter as tk

spiel = tk.Tk()
spiel.geometry("1024x768")

linkeseite = tk.Frame(spiel,bg="tomato",width=100,height=100)
linkeseite.pack(side=tk.LEFT)

spiel.mainloop()

Verfasst: Montag 18. August 2008, 20:08
von derkai
kommt jetzt die rechte seite dazu, dann baut der die aber nicht nach rechts, sondern drunter :

Code: Alles auswählen

import Tkinter as tk

spiel = tk.Tk()
spiel.geometry("1024x768")

linkeseite = tk.Frame(spiel,bg="tomato",width=100,height=100)
linkeseite.pack(anchor=tk.NW)

rechteseite = tk.Frame(spiel,bg="peachpuff",bd=3,relief=tk.SUNKEN)
rechteseite.pack(side=tk.LEFT,fill=tk.BOTH,expand=True)

spiel.mainloop()

auch anchor hilft dann nicht mehr weiter - ist immer noch drunter :

Code: Alles auswählen

rechteseite = tk.Frame(spiel,bg="peachpuff",bd=3,relief=tk.SUNKEN)
rechteseite.pack(anchor=tk.NE,fill=tk.BOTH,expand=True)


Verfasst: Montag 18. August 2008, 20:12
von numerix
Das wird sich hier noch reichlich in die Länge ziehen, wenn du immer Scheibchenweise mit dem herausrückst, was deine GUI sonst noch für Features haben soll ...

Wenn ich etwas aufwändigere GUIs zu gestalten habe, dann nehme ich erstmal Papier und Stift (manchmal sogar Buntstifte und Geodreieck ...) und mache per Hand einen Entwurf. Meistens sieht es zwar am Ende dann doch (gewollt) anders aus, aber es hilft mir ungemein, mit einer Vorlage neben der Tastatur an dem zu bauen, was herauskommen soll.

In deinem Fall könntest du z.B. mit einem Zeichenprogramm mal einen Oberflächenentwurf aufbauen, der so ist, wie du es (am Ende veraussichtlich) gerne hättest - mit allem drum und dran. Und das zeigst du uns dann. Oder du beschreibst den vorgesehenen Endaufbau so präzise, dass man sich ein ausreichend genaues Bild davon machen kann.

Und dann sehen wir weiter ...

Verfasst: Montag 18. August 2008, 20:45
von derkai
das ist jetzt aber nicht ganz fair.

Für mich soll das ein Test sein, ob die bisher programmierten
Zeilen auch in einer GUI funktionieren.

Ziel :

Linke Seite :
- einmal Canvas inkl. Scrollbalken mit vorgegener Spielfeldgröße
oben Luft und unten Luft
- einmal Canvas mittig drunter für eine Miniaturansicht der
gesamten Spielfäche

Rechte Seite - mit Luft rum herum :
- Frame mit drei Schaltflächen
oben, links und rechts.
Damit wird dann der Panzer übers Spielfeld bewegt.

das war es doch schon.

Ich habe das ja auch mit meinem Code gezeigt und gefragt,
warum ich nach Erstellung der Spielfläche links, einem Canvas darunter

DANN keinen Frame mehr auf die rechte Seite bekomme.

Kai

Verfasst: Montag 18. August 2008, 20:52
von derkai
und mit "place" sähe das dann so aus :

Code: Alles auswählen

import Tkinter as tk
from funktionen import *

#Hauptfenster erzeugen
spiel = tk.Tk()
spiel.geometry("1024x768")
spiel.title("Kai s Battle Game")
spiel.config(bg="grey")

#Frame1 erzeugen
fenster1 = tk.Frame(master=spiel,bg="white",width=500,
                    height=500,bd=2,relief=tk.SUNKEN)
fenster1.place(x=10,y=20)

#Scrollbar erzeugen
xleiste = tk.Scrollbar(master=fenster1,orient=tk.HORIZONTAL)
yleiste = tk.Scrollbar(master=fenster1,orient=tk.VERTICAL)
xleiste.pack(side=tk.BOTTOM,pady=2,fill=tk.X)
yleiste.pack(side=tk.RIGHT,pady=2,fill=tk.Y)

#Canvas im Hauptfenster
spflaeche = tk.Canvas (master=fenster1,width=495,height=495)
erstelle_hexfeldobjekte (spielreihen, ungerade_spalten)

for x in hexdic.iterkeys() :
    
    spflaeche.create_polygon (hexdic[x][0],fill=hexdic[x][2],outline="black")

spflaeche.pack()


#Verknuepfung Scrollbar
xleiste.config(command=spflaeche.xview)
yleiste.config(command=spflaeche.yview)

#Frame2 erzeugen -> Miniaturansicht
fenster2 = tk.Frame(master=spiel,bg="white",width=200,height=200,
                   bd=2,relief=tk.SUNKEN)
fenster2.place(x=600,y=80)

#Frame3 erzeugen -> Miniaturansicht
fenster3 = tk.Frame(master=spiel,bg="white",width=200,height=200,
                   bd=2,relief=tk.SUNKEN)
fenster3.place(x=10,y=550)


spiel.mainloop()

Da dann aber gesagt wurde, dies besser mit Pack zu versuchen,
kam die Diskussion dann ins Rollen

Kai

Verfasst: Montag 18. August 2008, 21:24
von numerix
Wenn ich deine verbale Beschreibung richtig verstehe, dann soll es so aussehen:

Code: Alles auswählen

import Tkinter as tk

spiel = tk.Tk()
spiel.geometry("1024x768")

linkeseite = tk.Frame(spiel)
linkeseite.pack(side=tk.LEFT,fill=tk.BOTH,expand=True)
spielfeld = tk.Canvas(linkeseite,bg="white",bd=2,relief=tk.SUNKEN)
spielfeld.pack(expand=True,fill=tk.BOTH,padx=25,pady=20)
schieber_x = tk.Scrollbar(spielfeld,orient=tk.HORIZONTAL)
schieber_x.pack(side=tk.BOTTOM,fill=tk.X)
schieber_y = tk.Scrollbar(spielfeld,orient=tk.VERTICAL)
schieber_y.pack(side=tk.RIGHT,fill=tk.Y)
vorschau = tk.Canvas(linkeseite,bg="white",bd=2,relief=tk.SUNKEN,width=180,height=120) 
vorschau.pack(pady=20)

rechteseite = tk.Frame(spiel,bd=3,relief=tk.GROOVE)
rechteseite.pack(side=tk.RIGHT,anchor=tk.N,padx=20,pady=20)
knopfoben = tk.Button(rechteseite,text="oben")
knopfoben.pack(pady=10)
knopfbereich = tk.Frame(rechteseite)
knopfbereich.pack(padx=20,pady=10)
knopflinks = tk.Button(knopfbereich,text="links")
knopflinks.pack(side=tk.LEFT,padx=10)
knopfrechts = tk.Button(knopfbereich,text="rechts")
knopfrechts.pack(side=tk.LEFT,padx=10)
spiel.mainloop()
Dein letzter Code mit der pack/place-Mischung liefert bei mir aber nicht das, was deine verbale Beschreibung angibt.

Verfasst: Montag 18. August 2008, 22:35
von derkai
ja, die Buttons fehlen !

ich wußte nicht, dass man SIDE und ANCHOR gleichzeitig
benutzen kann. Jetzt wird mir einiges klarer.

Du benutzt immer einen Frame, ohne eine Größe vorzugeben
und packst die Elemente einfach rein, richtig ?

ich habe es mal ergänzt um das Spielfeld + bewegliche Schieber :


Code: Alles auswählen

import Tkinter as tk
from funktionen import *

spiel = tk.Tk()
spiel.geometry("1024x768")

frame1 = tk.Frame(spiel)
frame1.pack(side=tk.LEFT,fill=tk.BOTH,expand=True)

spielfeld = tk.Canvas(frame1,bg="white",bd=2,relief=tk.SUNKEN)
erstelle_hexfeldobjekte ()

for x in hexdic.iterkeys() :
    
    spielfeld.create_polygon (hexdic[x][0],fill=hexdic[x][2],outline="black")

spielfeld.pack(expand=True,fill=tk.BOTH,padx=25,pady=20)


schieber_x = tk.Scrollbar(spielfeld,orient=tk.HORIZONTAL)
schieber_x.pack(side=tk.BOTTOM,fill=tk.X)

schieber_y = tk.Scrollbar(spielfeld,orient=tk.VERTICAL)
schieber_y.pack(side=tk.RIGHT,fill=tk.Y)

schieber_x.config(command=spielfeld.xview)
schieber_y.config(command=spielfeld.yview)

vorschau = tk.Canvas(frame1,bg="white",bd=2,relief=tk.SUNKEN,width=200,height=200)
vorschau.pack(pady=20)

frame2 = tk.Frame(spiel,bd=3,relief=tk.GROOVE)
frame2.pack(side=tk.RIGHT,anchor=tk.N,padx=20,pady=20)

knopfoben = tk.Button(frame2,text="oben")
knopfoben.pack(pady=10)
knopfbereich = tk.Frame(frame2)
knopfbereich.pack(padx=20,pady=10)
knopflinks = tk.Button(frame2,text="links")
knopflinks.pack(side=tk.LEFT,padx=10)
knopfrechts = tk.Button(frame2,text="rechts")
knopfrechts.pack(side=tk.LEFT,padx=10)


spiel.mainloop()
Vielen DANK für Deine unendliche Geduld

Kai

Verfasst: Montag 18. August 2008, 22:47
von derkai
"vergrössert" sich quasi der Frame hier um das Widget Vorschau ?

Code: Alles auswählen

vorschau = tk.Canvas(frame1,bg="white",bd=2,relief=tk.SUNKEN,width=220,height=220)
vorschau.pack(anchor=tk.W,pady=10,padx=25)
Kai

Verfasst: Dienstag 19. August 2008, 14:15
von numerix
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.

Verfasst: Mittwoch 20. August 2008, 17:35
von yipyip
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

Verfasst: Mittwoch 20. August 2008, 22:30
von derkai
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

Verfasst: Mittwoch 20. August 2008, 23:39
von BlackJack
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.

Verfasst: Donnerstag 21. August 2008, 10:58
von wuf
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: