Label bewegen mit Python 3 und tkinter

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
010010
User
Beiträge: 14
Registriert: Freitag 3. Juni 2016, 20:33

Guten Tag habe mal versucht eine alte Idee aus der Schule die in C# einfach umzusetzen war. In Python 3 zu verwirklichen. Dabei versuche ich so wenig Datenbanken zu nutzen. Erst recht nicht unnötig kompliziertes aufzubauen. Nur halt ein Fenster in dem ich ein Label bewegen kann. Das ganze ist mir auch so ein bisschen gelungen. Nur rechnet der in meiner jetzigen Form nur + 5 Pixel dazu und hat dann die neue Position x = 10. Zu verständnis mal mein kleines Script wo der Test drinne ist. Damit ich lernen kann wie es in Python 3 funktioniert.

Code:

from tkinter import Tk, Canvas, mainloop, Label, Button, messagebox
from tkinter import *
import math, os , time

nx = 5
ny = 5

window = Tk(className="LabelBewegen")
window.geometry("800x600")

l1 = Label(window, bg = 'red', height = 1, width = 2)
l1.place(x = nx, y = ny)


def right():
l1.place(x = nx + 5)

windowleft = Button(window, text=">>", height = 2, width = 2, command = right)
windowleft.place(x = 200, y = 200)

window.mainloop()


So damit es alle wissen. Es geht darum so wenig Datenbanken zu nutzen. Mehr halt nur tkinter für die Fenster Layout und ein paar Sachen grafisch beeinflussen. Danke schonmal im Vorraus.

mfg Binary the old Programming Noob. :D
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Das ganze ist mir auch so ein bisschen gelungen.
Heißt was genau? Was funktioniert, wie du es dir vorstellst, was nicht? Und was ist eigentlich deine konkrete Frage...?
Dabei versuche ich so wenig Datenbanken zu nutzen.
Verstehe ich nicht. Du brauchst hier doch so oder so keine Datenbank...?

Ansonsten solltest du mal deiner Importe durchschauen, weil du Sachen doppelt importierst. Am besten importierst du Tk als `import tkinter as tk`.
Die Verwendung von `place` ist auch meistens keine gute Idee, weil das Layout mal schnell so sein kann, wie es nicht sein soll. Besonders, wenn man die Fenstergröße ändert. Besser sind `grid` oder `place`.
Des Weiteren solltest du deine GUI in eine Klasse packen. Als, was über mega-triviale GUIs hinaus geht, ist so besser strukturiert und besser wart- und erweiterbar.

Gruß, noisefloor
Sirius3
User
Beiträge: 18268
Registriert: Sonntag 21. Oktober 2012, 17:20

time, os und math werden importiert, aber nicht gebraucht. Alles was eine Funktion braucht, sollte sie auch über ihre Argumente bekommen, l1 und nx aber kommen aus dem Nichts.

Du willst also, dass das Rechteck immer weiterrückt, dann mußt Du zur aktuellen Position etwas addieren und nicht zur anfänglichen.

Code: Alles auswählen

from functools import partial
import tkinter as tk

def move(rectangle, dx, dy):
    info = rectangle.place_info()
    rectangle.place(x=int(info['x']) + dx, y=int(info['y']) + dy)

def main():
    window = tk.Tk(className="LabelBewegen")
    window.geometry("800x600")

    red_rectangle = tk.Label(window, bg='red', height=1, width=2)
    red_rectangle.place(x=5, y=5)
    tk.Button(window, text=">>", height=2, width=2,
        command=partial(move, red_rectangle, 5, 0)).place(x=200, y=200)

    window.mainloop()

if __name__ == '__main__':
    main()
Man merkt recht schnell, dass man viele Parameter für Funktionen übergeben muß und sich irgendwann auch mehr Zustand merken muß. Dann solltest Du das ganze in eine Klasse packen.
Benutzeravatar
__blackjack__
User
Beiträge: 14036
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@010010: Neben den Mehrfachimporten sind da auch explizite Importe dabei die überhaupt nicht verwendet werden.

Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Definitionen von Funktionen mitten in anderen Code zu schreiben ist zudem noch einmal extra-unübersichtlich.

Abgekürzte Namen sollte man vermeiden, denn ein Name soll dem Leser vermitteln was der Wert bedeutet und nicht zum rätselraten zwingen. Mir ist zum Beispiel so gar nicht klar was das `n` in `nx` und `ny` bedeuten soll.

Namen sollten nicht nummeriert werden. Wenn man das tut, will man sich entweder bessere Namen ausdenken, oder eine Datenstruktur verwenden, statt einzelner Namen. Im Falle von `l1` ist die Zahl aber einfach nur überflüssig, und das `l` zu kurz für einen sinnvollen Namen. Das könnte sinnvoller einfach `label` heissen.

Das `className`-Argument ist nicht dazu da um den Titel von Fenstern zu setzen. Dazu gibt es die `title`-Methode. Die Verändert dann auch nichts an der Gross-/Kleinschreibung des übergebenen Wertes.

Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argumente übergeben bekommen und nicht auf magische Weise irgendwie aus der Umgebung verwenden. Das betrifft das `Label`-Objekt in `right()`. Hier braucht man dann entweder objektorientierte Programmierung (OOP); oder ein Closure per `functools.partial()`, oder ``lambda``-Ausdruck.

Funktionen werden üblicherweise nach ihrer Tätigkeit benannt. Also beispielsweise `move_right()` statt `right()`.

Um Gleichheitszeichen bei Schlüsselwortargumenten werden per Konvention *keine* Leerzeichen gesetzt.

Dem `tk.Button`-Objekt braucht man eigentlich keinen Namen geben. Auch sollte man das nicht mittels `place()` anordnen, denn so steht das ja mitten im Fenster. Beim `Label` gibt es ja den Grund, dass das frei bewegt werden können soll, aber `place()` sollte die Ausnahme sein.

Zwischenstand wäre dann so etwas in der Art:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import partial
import tkinter as tk
# 
# TODO Find better name to explain the `N_` prefix.
# 
N_X = 5
N_Y = 5


def move_right(label):
    label.place(x=N_X + 5)


def main():
    window = tk.Tk()
    window.title('Label Bewegen')
    window.geometry('800x600')

    label = tk.Label(window, bg='red', height=1, width=2)
    label.place(x=N_X, y=N_Y)

    tk.Button(
        window, text='>>', height=2, width=2, command=partial(move_right, label)
    ).pack(side=tk.BOTTOM)

    window.mainloop()


if __name__ == '__main__':
    main()
Falls die versteckte Frage sein sollte warum das nur einen Schritt geht und bei weiteren klicks auf die Schaltfläche stehen bleibt: Weil es genau so im Code steht. Bei Klick das Label 5 Pixel neben der Konstanten `N_X` setzen.

Wenn das weiter wandern soll bei jedem Klick, dann darf man das nicht relativ zu einer Konstante, sondern muss den tatsächlich aktuellen X-Wert zugrunde legen. Da käme man zwar mit `pack_info()` heran, allerdings merkt man da das Tcl/Tk ”stringly typed” ist, also eigentlich nur Zeichenketten als einzigen Datentyp kennt. Bevor man da also anfängt wieder in Zahlen zu konvertieren um in Python damit rechnen zu können, sollte man besser auf OOP setzen und sich die Position selbst merken.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
010010
User
Beiträge: 14
Registriert: Freitag 3. Juni 2016, 20:33

Oh hätte ich mal sagen sollen. Ich habe mehr als nur ein Projekt. Bei anderen Sachen brauchte ich mal alle Importe. Die behalte ich nur bei. Da wir alle über 4GB Ram Speicher haben und CPUs die 20.000 Nachkomma stellen von Pi bereichnen können in Sekunden und schneller macht es nichts.

Ich will das Label frei durch das Fenster bewegen mit einem Button erst und danach mit Keyboardanbindung. In C# ist das simpel gewesen. In Python muss man Design mit tkinter bauen. Denkt euch import ist nur tkinter drin. Hab ich mit C und C++ damals beim arbeiten auch so gemacht. Hat niemand sich dran gestört. Nur der Professor für Infortmatik. :D
Es soll halt so ein kleine Projekt sein um eine Character Steuerung zu bauen. Später dann wenn es läuft und arbeitet mache ich gifs dazu um ein extrem Simples und nur auf Python 3 + tkinter Basirendes Arcade Spiel zu bauen. Damit ein alter Kinderwunsch mal zu ende gebracht wird.

Jetzt kurz zum schluss. Bin etwas Hecktisch drauf. Deswegen halbe Fragestellungen. Doch sehe ich zwei gute Vorschläge und werde mich daran heute mal halten.
Gilt mein Dank an __blackjack__ und Sirius3.
Werde es mir zu herzen nehmen und lernen wie es in Python Bündig funktioniert.
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

Tkinter ist nicht 1. Wahl für Spiele. Schau' dir dann mal besser pygame an.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 14036
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@010010: Wenn das ein Spiel werden soll würde ich ja nicht Labels auf einem Fenster oder Frame bewegen sondern ein `Canvas` für die Spielfläche verwenden. Dann kann man auch Transparenz in den GIFs sinnvoll verwenden.

Die unnötigen Importe haben nichts mit Speicherverbrauch zu tun, sondern damit das der Leser nicht denkt das Modul hätte Abhängigkeiten die es tatsächlich gar nicht hat. Man fängt ein Projekt nicht mit der/den Datei(en) vom letzten Projekt an.

@noisefloor: Vielleicht nicht grundsätzlich erste Wahl, aber total ungeeignet auch nicht. Je nach dem was man machen will, ist es auch einfacher als in Pygame, wo man sich vieles selber basteln muss. Und man kann Pygame auch in ein Tkinter-Fenster einbetten, wenn man beides haben möchte. :-)
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
010010
User
Beiträge: 14
Registriert: Freitag 3. Juni 2016, 20:33

So ich habe es mir angesehen. Verstehe jetzt was ich falsch gemacht habe so langsam. Die Funktion muss eine main und eine für die Bewegung sein. Damit in der Main Startposition drin ist und sie addiert wird. Damit es mehr als einmal läuft. In meine alten Variante wurde es nur auf ein maximum addiert.
Das mit den Canvas hatte ich mal vor gehabt. Soll auch alles noch kommen. Doch will ich die Grundlagen einmal alle drin haben. Dann kann man sie leichter verbinden.
Zu PyGames kann ich nur sagen. Super für blender3D Games. Da kann man das noch besser anwenden. Dann hat man das Design schon fertig.
Antworten