Canvas GUI

Fragen zu Tkinter.
Antworten
duodiscus
User
Beiträge: 97
Registriert: Sonntag 6. April 2014, 16:10

Hallo zusammen,
ich habe eine GUI erstellt und zwar soll diese Rechtecke zufällig auf der Canvas positionieren.
Ich habe die GUI soweit aufgebaut und das Rechteck auch mittels einer Funktion erstellt. Leider weiß ich jetzt nicht wie ich die Rechtecke mittels for-Schleife jetzt zufällig anordnen lassen, sodass sie auch stehen bleiben. Könnt Ihr mir da helfen?

Code: Alles auswählen

def Rechteck():
       
    groesse = 10, 10, 35, 25
    Rechteck1 = ZF.create_rectangle(groesse, fill = 'red')

def CBKnopf(): 
    for i in range(Wert+1):  # hier weiß ich nicht weiter, mit der zufälligen Anordnung

#der Wert wird per Entry Eingabe abgerufen. Und mit einem Button (CBKnopf) sollen dann die n Rechtecke erstellt werden.     
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

duodiscus hat geschrieben:ich habe eine GUI erstellt und zwar soll diese Rechtecke zufällig auf der Canvas positionieren.
Wenn du die Frage im passenden GUI-Unterforum gestellt hättest, dann müssten wir jetzt nicht nachfragen, von welchem GUI-Toolkit du eigentlich redest.
duodiscus
User
Beiträge: 97
Registriert: Sonntag 6. April 2014, 16:10

Ahso, ups sorry.

tkinter

Code: Alles auswählen

from tkinter import *
import random
root = Tk()
HFrame = Frame(root)
HFrame.pack()

Rahmen1 = Frame(HFrame)
Rahmen1.pack(side = TOP)
ZF = Canvas(Rahmen1)
ZF['width'] = 600
ZF['height'] = 300
ZF['bg'] = 'yellow'
ZF.pack()

Rahmen2 = Frame(HFrame)
Rahmen2.pack(side = BOTTOM)

Eingabe = Entry(Rahmen2)
Eingabe['width'] = 26
Eingabe['font'] = ('Courier', 10, 'bold')
Eingabe.insert(0, 'Anzahl Rechtecke eingeben.')
Eingabe.pack()

def Rechteck():
    Wert = float(Eingabe.get())
    global Wert
    groesse = 10, 10, 35, 25
    Rechteck1 = ZF.create_rectangle(groesse, fill = 'red')

def CBKnopf(): 
    for i in range(Wert+1):
       pass 
        

Rahmen3 = Frame(HFrame)
Rahmen3.pack(side = BOTTOM)

Knopf = Button(Rahmen3, text = 'Erzeugen')
Knopf['command'] = CBKnopf
Knopf.pack()



root.mainloop()

BlackJack

Ein paar Anmerkungen zu dem Quelltext:

Vermeide Sternchenimporte. Damit ist irgendwann nur noch schwer nachvollziehbar wo welcher Wert herkommt. Und man müllt sich den Namensraum voll, insbesondere bei Modulen wie `tkinter` das um die 190 Namen enthält. Da riskiert man dann auch Namenskollisionen. Bei `tkinter` ist es üblich das unter dem Namen `tk` zu importieren: ``import tkinter as tk``. Dann muss man die Objekte darin über den Namen `tk` referenzieren.

Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden und nicht das Hauptprogramm stehen. Schon gar nicht sollte man Hauptprogramm und Funktionsdefinitionen abwechselnd auf Modulebene stehen haben. Das macht alles nur noch unübersichtlicher. Das Hauptprogramm wird üblicherweise in eine `main()`-Funktion gesteckt und mit folgendem Idiom aufgerufen:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Dann kann man das Modul sowohl als Programm direkt ausführen, als auch als Modul importieren ohne das die `main()` sofort ausgeführt wird, um zum Beispiel einzelne Funktionen zu testen, oder in anderen Modulen zu verwenden.

Bezüglich der Namensschreibweise gibt es gewisse Konventionen: Style Guide for Python Code. In dem Dokument steht auch etwas über Leerzeichensetzung.

Darüber hinaus sollte man keine Abkürzungen in Namen verwenden die nicht allgemein bekannt sind. Namen sollen dem Leser verraten was die Werte dahinter im Programmkontext bedeuten, und nicht zum Rätselraten zwingen.

Man sollte sich auf *eine* natürliche Sprache bei der Namenswahl beschränken. Das ist normalerweise Englisch. Insbesondere wenn man ähnliche Werte in unterschiedlichen Sprachen benennt, zum Beispel mal irgend etwas mit `frame` und mal `rahmen` für `Frame`-Objekte, dann belastet man den Leser oder Programmierer zusätzlich mit der Frage wie vorhandene `Frame`-Objekte denn nun benannt sind, oder welche Sprache man für neue `Frame`-Objekte verwenden sollte.

Das durchnummerieren von Namen ist auch keine gute Idee. Das ist in der Regel ein Hinweis darauf, dass man eigentlich eine Datenstruktur statt einzelner Namen verwenden möchte. Oft eine Liste. Oder aber wie im Fall von `Rahmen1` bis `Rahmen3`, dass man einfach den selben Namen wiederverwenden kann, weil der jeweils nur einen eng begrenzten Abschnitt lang für jeweils ein Rahmenobjekt verwendet wird. Oder man lässt die komplett weg, denn in jedem dieser Rahmen steckt jeweils nur *ein* Widget. Da kann man die Widgets auch direkt verwenden.

Selbst der `HFrame` scheint überflüssig zu sein.

Statt der ganzen Indexzugriffe um die einzelnen Optionen für die Widgets zu setzen, könnte man die gleich beim Erstellen als Schlüsselwortargumente angeben. Das spart Quelltextzeilen mit Code-Wiederholungen.

Funktionsnamen sind in der Regel Tätigkeiten, weil sie etwas tun. `Rechteck` passt da nicht, das ist ein ”Ding” was eher zu einem Wert passt der ein Rechteck repräsentiert, oder in dieser Schreibweise, mit einem grossen Anfangsbuchstaben, zu einem Datentyp der ein Rechteck modelliert.

`Wert` wird in der `Rechteck()`-Funktion überhaupt nicht verwendet. Das sollte wohl in der `CBKnopf()`-Funktion stehen.

``global`` sollte in einem Programm nicht vorkommen. Das deutet in 99,9% der Fälle auf einen unsauberen Entwurf hin. Werte, ausser Konstanten, sollten eine Funktion als Argument betreten und als Rückgabewert verlassen, und nicht einfach so aus ”der Umgebung” kommen oder dort gesetzt werden. Das ist sehr unübersichtlich und schlecht zu testen.

Da man sich bei der ereignisorientierten GUI-Programmierung Zustände über Funktionsaufrufe hinweg merken muss, braucht man Closures oder objektorientierte Programmierung. In Python wird ganz klar OOP bevorzugt, weil Python eine von Grund auf objektorientierte Programmiersprache ist. Darum ist IMHO OOP eine Voraussetzung bevor man sich an GUI-Programmierung setzt.

Ich lande dann als Zwischenschritt bei so etwas:

Code: Alles auswählen

from __future__ import print_function
import random
try:
    import Tkinter as tk
    range = xrange
except ImportError:
    import tkinter as tk
from functools import partial


def draw_rectangle(canvas):
    canvas.create_rectangle((10, 10, 35, 25), fill='red')


def do_create_rectangles(rectangle_count_entry, canvas):
    for i in range(int(rectangle_count_entry.get())):
        print(i)


def main():
    root = tk.Tk()

    canvas = tk.Canvas(root, width=600, height=300, bg='yellow')
    canvas.pack(side=tk.TOP)

    rectangle_count_entry = tk.Entry(
        root, width=26, font=('Courier', 10, 'bold')
    )
    rectangle_count_entry.insert(0, 'Anzahl Rechtecke eingeben.')
    rectangle_count_entry.pack(side=tk.BOTTOM)

    create_rectangles_button = tk.Button(
        root,
        text='Erzeugen',
        command=partial(do_create_rectangles, rectangle_count_entry, canvas)
    )
    create_rectangles_button.pack(side=tk.BOTTOM)

    root.mainloop()


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