Benutzung von Klassen (Vererbung von Tkinter-Klassen)

Fragen zu Tkinter.
Antworten
Quaaak
User
Beiträge: 13
Registriert: Samstag 8. April 2017, 21:42

Hi zusammen!
Ich bin noch relativ neu bei Python und Tkinter. Trotzdem habe ich mir vorgenommen, mir eine Art sehr einfaches 3d-Grafik-Programm zu basteln, wo man ein paar simple 3d-Objekte wie Prismen (Quader, Würfel,...) angezeigt bekommen kann. Allerdings ist mein Ziel, das komplett ohne OpenGL und ohne andere Frameworks zu machen. Ich will es vollständig zu Fuß machen, einfach aus Spaß an der Freude.
Ich will mich als Betrachter in der 3d-Welt frei umherbewegen können. Dazu habe ich verschiedene Klassen angelegt, um den später langen Code besser zu gliedern und übersichtlicher zu machen.

Code: Alles auswählen

import Tkinter as tk
class World:
    pass  # Definieren des 'Bodens'  der Welt, Weltkoordinatensystem


class sObject:
    def __init__(self, ):
        pass # Definieren der lokalen Koordinatensysteme für die 3d-Objekte


class Block(sObject):
    def __init__(self):
        self.eckpunkte = (...)
        self.flaechen = (...)


class Lamp: # Beleuchtung
    def __init__(self, position):
        self.position = position


class Eye: # Beobachter
    def __init__(self, position, ):
        self.position = position


class Canvas(tk.Canvas):
    def __init__(self, parent, *args, **kwargs):
        tk.Canvas.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        self.bind('<Button-1>', self.click_handle)
        self.bind('<ButtonRelease-1>', self.release_handle)
        self.bind('<B1-Motion>', self.motion_handle)
        self.bind('<Up>', self.key_handle)
        self.bind('<Down>', self.key_handle)
        self.bind('<Left>', self.key_handle)
        self.bind('<Right>', self.key_handle)


    def click_handle(self, event):
        #### Wie kann ich hier ein Funktion der Instanz der Klasse Handling erreichen?

    def motion_handle(self, event):
        #### same
        
    def key_handle(self, event):
        #### same

    def release_handle(self, event):
        #### same


class FrameSettings(tk.Frame): # Frame am rechten Rand für Einstellungen und weiteres
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        self.radiob = tk.IntVar()
        self.radiob.set(1)
        self.r1 = tk.Radiobutton(self,
                                 text='Beobachter',
                                 variable=self.radiob,
                                 bg='seashell2',
                                 value=1).grid(column=0, row=0, sticky='w')
        self.r2 = tk.Radiobutton(self,
                                 text='Block',
                                 variable=self.radiob,
                                 bg='seashell2',
                                 value=2).grid(column=0, row=1, sticky='w')
        self.r3 = tk.Radiobutton(self,
                                 text='Lampe',
                                 variable=self.radiob,
                                 bg='seashell2',
                                 value=3).grid(column=0, row=2, sticky='w')


class MainApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)


class Handling: # Verarbeitung aller Inputs, Errechnen der neuen Werte für Canvas, also Zentralprojektion 
    def __init__(self):
        self.edit_mode = 1  # 1: 'eye', 2: 'block', 3: 'lamp'
        self.frame_duration = 1 / 25 
        self.running = True
        
        self.root = MainApp()
        self.root.geometry('2000x1000+100+100')
        self.root.minsize(900, 250)
        self.root.title('3d')
        self.root.protocol('WM_DELETE_WINDOW', self.quit)

        self.canvas = Canvas(self.root, bg='cornsilk').pack(side='left',
                                                            fill='both', expand=True)
        self.framesettings = FrameSettings(self.root, bg='seashell2')
        self.framesettings.pack(anchor='e', fill='y', expand=True)
        print ('Handling almost initialized')
        self.mainloop()


    def wait_one_frame(self):
        time.sleep(self.frame_duration)

    def update_world(self):
        pass

    def draw_world(self):
        pass

    def mainloop(self):
        while self.running:
            print (self.edit_mode)
            self.edit_mode = self.framesettings.radiob.get()
            self.wait_one_frame()
            self.update_world()
            self.draw_world()
            self.root.update()

    def process_input_click(self, x, y):
        pass  # Verarbeitung von Klicks

    def process_input_motion(self, x, y):
        pass  # Verarbeitung von Mausbewegung mit gedrücktem Button-1

    def process_arrows(self, key):
        pass  # Verarbeitung der Pfeiltasten (Steuerung des Beobachters...

    def process_b1_release(self, x, y):
        pass  # Verarbeitung von Button-1-Release
       
    def quit(self):
        self.running = False
        self.root.destroy()



if __name__ == '__main__':
    handling = Handling()

Ich dachte mir, um die optimale Kontrolle über das ganze Programm zu haben, mache ich eine Klasse (Handling), von der ich aus alle anderen Klassen instanziere, sodass ich bequem auf diese zurückgreifen kann. In Handling sollen die Inputs verarbeitet werden und mit Zentralprojektion alles ausgewertet werden.

Für meine Verhältnisse ist dieses Programm sehr ehrgeizig (weil schwierig). Deshalb würde ich mich über Tipps sehr freuen.
Meine Fragen:

Wie strukturiere ich mein Programm am besten?
Es gibt garantiert bessere Ansätze als meiner. Ist es sinnvoll und zweckmäßig, eine Steuerungsklasse zu machen oder sollte ich die anderen Klassen lieber außerhalb instanzieren?

Kennt ihr eine elegante Möglichkeit, bei <B1-Motion> delta x und delta y zu errechnen?
Wenn man mit event.y und event.y rangeht, bekommt man immer nur die aktuelle Position des Mauszeigers. Ich will aber wissen, um wie viel es sich zum letzten event.x und event.y verändert hat.
Ich habe eine Möglichkeit gefunden, allerdings ist diese ziemlich unelegant und lang.

Meine wichtigste Frage: Wie kann ich von der Klasse Canvas auf die Instanz von Handling zugreifen?
Ich habe in der Klasse Canvas die events gebunden

Code: Alles auswählen

self.bind('<ButtonRelease-1>', self.release_handle)
Wie kann ich diese Informationen, also die Koordinaten des Klicks an die Instanz der Klasse Handling weitergeben? Schließlich habe ich in der Klasse Handling die Klasse Canvas instanziert. Wenn ich

Code: Alles auswählen

Handling().process_input_click(x, y)
benutze, wird von Handling eine neue Instanz erstellt, wenn ich das richtig mitbekommen habe. Wenn ich die Klammern weglasse und nur

Code: Alles auswählen

Handling.process_input_click(x, y)
sage, greife ich nicht auf die Instanz der Klasse Handling zu, sondern auf die Funktion aus dieser Klasse. In diesem Fall erscheint der Fehler, dass ich für die Funktion process_input_click(self, x, y) keine Instanz als Parameter übergeben habe.
Wie kann ich das lösen? Wenn es auf diesem Wege nicht geht, wie kann ich das umgehen?

Ich benutze Python 2.7 auf MacOS.
Ich hoffe, so viele Fragen auf einmal zu stellen, ist ok :?

Vielen Dank für eure Antworten! Vergesst bitte nicht, dass ich neu in Python bin :lol:
Zuletzt geändert von Anonymous am Mittwoch 7. Juni 2017, 19:52, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Quaaak: `Handling` ist ein komischer Name. Da kann man sich alles oder nichts drunter vorstellen. `sObject` ist mir auch nicht klar was das sein soll. Das hat zudem einen Namen der nicht dem Style Guide for Python Code entspricht.

Du willst auf keinen Fall selber die `mainloop()` stellen. Tk hat bereits eine, nutze die. Der Programmfluss bei GUI-Rahmenwerken funktioniert anders, da sollte man nicht kramphaft versuchen die Kontrolle zu behalten. `time.sleep()` ist aus dem gleichen Grund in GUI-Code auch keine gute Idee. Wenn man etwas zeitverzögert machen möchte, dann sagt man Tk das man eine Funktion oder Methode hat, die nach einer gegebenen Zeit aufgerufen werden soll (`Widget.after()`).

Auch sollte die `__init__()` zum Aufrufer zurückkehren damit der die Chance hat etwas mit dem Objekt zu machen.

In Python 2 sollte alles was keine Basisklasse hat, von `object` erben. Sonst hast Du keine „new style“-Klasse. Nicht weiter drüber nachdenken. Mit „old style“-Klassen funktioniert nicht alles was Python so bietet.

Magische Zahlen wie `self.edit_mode` sollte man vermeiden. Zeichenkettenkonstanten sind einen Tick besser, weil man die bei der Fehlersuche ausgeben lassen kann und dann etwas verständlicheres als eine Zahl bekommt. Noch besser wäre es das `enum34`-Modul zu installieren (Portierung des `enum`-Moduls aus der Python 3 Standardbibliothek) und ein Enum-Typ für so etwas zu erstellen. Am allerbesten wäre es wenn man sich die Indirektion sparen könnte, und gleich einen Wert verwenden könnte der auch Funktionalität bereit stellt.

`geometry()` würde ich nicht verwenden, beziehungsweise nur um vom letzten Programmlauf gespeicherte Werte wieder zu setzen.

Zur Motion-Frage: Wenn es die Werte nicht schon fertig gibt, müsste man sie berechnen. Also zum Beispiel die Koordinaten beim drücken der Taste merken und dann einfach die Differenz berechnen.

Und die wichtigste Frage: Du müsstest das Objekt halt einfach weitergeben. Allerdings ist das bei Dir etwas komisch angeordnet. Handling müsste sich dann selbst an das Objekt übergeben was es erzeugt hat. Das ist alles ziemlich stark aneinander gebunden. `Canvas` kann nicht ohne `Handling` und `Handling` kann nicht ohne `Canvas` *und* man kann auch keines dieser beiden Objekte durch ein anderes ersetzen.

Mir fehlt in dem Ansatz auch eine wirklich saubere, klare Trennung zwischen GUI und Programmlogik.

Ich würde kleiner anfangen und von unten aufbauen. Also erst die Geschäftslogik mit den Objekten die diese modellieren, und dann bei der GUI erst mal eine Zeichenfläche auf der das dargestellt werden kann. Und dann kann man nach und nach Funktionalität einbauen und die auch immer testen.

`MainApp` ist überflüssig.
Quaaak
User
Beiträge: 13
Registriert: Samstag 8. April 2017, 21:42

@BlackJack:

Ok, das mit den Namen und das Erben von (object) lässt sich ja schnell umändern. Das wusste ich nicht.
time.sleep() lässt sich also auch umgehen.
Zu Motion: Ja, schon klar, das kann man ja auch berechnen. Bloß es ist so unschön. Ich hatte eher an eine von Tkinter vorgefertigte Möglichkeit gedacht, die Mir bisher noch nicht über den Weg gelaufen ist.

Ja, MainApp mache ich selbstverständlich raus. Ok.

Die Trennung von Programmlogik und GUI ist mir allerdings noch nicht ganz klar. Schließlich müssen GUI und Programmlogik interagieren. Wo siehst du denn eine 'Vermischung'? In der Klasse Handling (was auf Deutsch auch Verarbeitung heißt) soll die Programmlogik stattfinden. Dort wird das ganze ausgerechnet. Und um das schließlich auch zu zeichnen, brauche ich einen Zugang zu den GUI - Klassen. Um einen möglichst einfachen Zugang zu haben, habe ich Instanzen von den GUI - Klassen in der Handling-Klasse erstellt.

Was meinst du denn mit 'komische Anordnung'? Wie würdest du das denn anordnen?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Quaaak: Ich denke mal nicht, dass bei Deinem Vorhaben der Gui Aufbau Priorität hat. In tkinter kann man scalieren und bewegen, rotieren aber nicht.

Das mußt Du selber berechnen. Naja dürfte nicht allzu problematisch sein, wenn es nur zweidimensional ist.

Wenn es aber dreidimensional ist, dann wird es schwieriger.
Ich schlage vor, lass mal den Vogel rotieren und danach gibst Du ihm noch eine Dicke und skalierst und drehst ihn auch in anderer Richtung.
Und danach können wir uns über den Gui Aufbau unterhalten.

Hier der Vogel:

Code: Alles auswählen

def Access(canvas):

    coordinates = [None]

    def move_to(x,y):
        coordinates[0] = [x,y]
        
    def becier(*args):
        for index in range(0,len(args),6):

            px0 = coordinates[0][-2]
            py0 = coordinates[0][-1]
            px1 = args[index]
            py1 = args[index+1]
            px2 = args[index+2]
            py2 = args[index+3]
            px3 = args[index+4]
            py3 = args[index+5]

            for T in range(1,29):
                t = T/30
                Bx = (1-t)*(1-t)*(1-t)*px0 + 3*t*(1-t)*(1-t)*px1+3*t*t*(1-t)*px2+t*t*t*px3
                By = (1-t)*(1-t)*(1-t)*py0 + 3*t*(1-t)*(1-t)*py1+3*t*t*(1-t)*py2+t*t*t*py3
                coordinates[0].append(Bx)
                coordinates[0].append(By)
            coordinates[0].append(args[index+4])
            coordinates[0].append(args[index+5])


    def line_to(*args):
        x0 = coordinates[0][-2]
        y0 = coordinates[0][-1]
        coordinates[0].extend((x0,y0))
        for index in range(0,len(args),2):
            coordinates[0].append(args[index])
            coordinates[0].append(args[index+1])
            coordinates[0].append(args[index])
            coordinates[0].append(args[index+1])

    smooth = 1
    splinesteps = 12

    move_to(8988,4286)
    line_to(9770,3752,8655,3425)
    becier(7723,1788,5822,2642,5186,4199,4420,6075,2224,6760,2224,6760,3787,8619,5196,8954,6136,8880,7077,8806,9615,8066,9087,4766,9059,4595,9026,4435,8988,4286)
    canvas.create_polygon(*coordinates[0],fill='#2ba9e1',tags='new_tag',smooth=smooth,splinesteps=splinesteps)

    move_to(8655,3426)
    becier(8788,3660,8901,3945,8988,4286)
    line_to(9770,3753,8655,3426)
    canvas.create_polygon(*coordinates[0],fill='#2b3277',tags='new_tag',smooth=smooth,splinesteps=splinesteps)


    move_to(7412,5497)
    becier(7412,5497,4803,4959,4503,6411,4204,7863,6613,8705,7412,5497)
    canvas.create_polygon(*coordinates[0],fill='#2b3277',tags='new_tag',smooth=smooth,splinesteps=splinesteps)


    move_to(7915,3385)
    becier(7932,3541,7852,3678,7735,3691,7618,3705,7509,3589,7491,3433,7473,3278,7554,3140,7671,3127,7788,3114,7897,3229,7915,3385)
    canvas.create_polygon(*coordinates[0],fill='#2b3277',tags='new_tag',smooth=smooth,splinesteps=splinesteps)

    return 'bird'

def main():

    import tkinter as tk
    root = tk.Tk()
    canvas = tk.Canvas(root,width=400,height=400)
    canvas.pack()
    Access(canvas)
    scale = 0.05
    canvas.scale('new_tag',0,0,scale,scale)
    canvas.move('new_tag',-100,-100)
    root.mainloop()
 
if __name__ == '__main__':
    main()
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Du wirst Dich übernehmen, denke ich mir. Es ist ein gewaltiger Unterschied, ob Du nur etwas mit Matplotlib visualisieren möchtest, obwohl da auch bereits vieles nicht ganz so einfach sein dürfte, oder ob Du selbst ein Renderprogramm entwickeln möchtest. Mit Terragen habe ich mal gearbeitet, wenn ich eines weiß, dass ich Vergleichbares nie selbst beginnen würde. Ich wüsste nicht einmal so genau, wie sich mit Canvas gefertigte 3D Objekte mit Texturen überziehen lassen. Das Modellieren könntest Du Dir bei Blender anschauen, um einen ersten Eindruck zu erhalten.
Quaaak hat geschrieben:Ich will aber wissen, um wie viel es sich zum letzten event.x und event.y verändert hat.
Werte lassen sich zwischenspeichern.
BlackJack

@Quaaak: Wenn in der Klasse `Handling` die Programmlogik stattfinden soll, dann sehe ich genau da die Vermischung, denn die enthält ja GUI-Code. Die Programmlogik/Geschäftslogik zeichnet nichts, dafür ist die GUI zuständig. Das heisst die GUI braucht zugriff auf die Geschäftslogik, nicht umgekehrt. Für so eine 3D-Darstellung würde man traditionell eine Klasse `World` oder `Scene` haben, die nichts mit GUI zu tun hat, und eine GUI-Klasse der man dieses Welt-Objekt übergibt. Und die GUI kann dann die notwendigen Daten von dem `World`-Objekt abfragen, die sie braucht um sie darzustellen. Dem `World`-Objekt muss es egal sein ob das letztendlich auf einem Tk-Canvas, einem Pygame-Surface, oder in Qt oder Gtk gezeichnet wird, oder ob man eine PDF- oder SVG-Datei aus den Daten erstellt. Oder ob man das ganze als POV-Ray-Quelltext exportiert. Denn das 3D-Modell und die Umrechnung in 2D-Daten zum Zeichnen sind von der konkreten GUI unabhängig.

Noch mal zum Namen: `Handling` ist nicht üblich und supergenerisch. Der Leser möchte ja wissen *was* Verarbeitet wird, denn verarbeiten oder behandeln machen sehr viele Objekte. Bei so einem generischen Namen besteht auch die Gefahr dass das zu einer Gottklasse wird die am Ende irgendwie *alles* behandelt/verarbeitet. Wenn der Name denn verbreiteter wäre, dann wäre das genau so ein „code smell“ wie `ParrotHandler` oder `ParrotManager`, weil eben `Handler` und `Manager` nicht so wirklich einen Mehrwert haben. Und selbst wenn man solche Objekte hat, nur `Handler` oder `Manager` ohne ein konkretes `Parrot` davor, möchte man die ziemlich sicher nicht nennen.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Diese Trennung von Programmlogik und GUI-Code ist aber für einen Einsteiger schwer verständlich und ich musste feststellen, dass ich da auch noch arge Probleme habe. Mit JavaScript ist es z.B. eine Selbstverständlichkeit, dass mehr als drei Zeilen Code nicht in HTML, sondern in einer eigenen JS-Datei gespeichert werden, die dann mit einer einzigen Zeile in beinahe jeder beliebigen HTML-Seite geladen werden kann. In PHP wird man ebenfalls alle Scripts in Dateien ablegen, um die letztendlich nur zu includen. Bei Python will bzw. muss ich gelegentlich erst einmal Testreihen machen, wie man das am günstigsten hinbekommt.

@Quaaak: Und genau das ist es, was ich Dir auch empfehlen würde, beginne mit Testreihen und lerne dabei die Sprache und Deine eigenen Fähigkeiten besser kennen.
Zuletzt geändert von Melewo am Donnerstag 8. Juni 2017, 10:09, insgesamt 1-mal geändert.
BlackJack

@Melewo: Am besten trennt man es einfach. Also implementiert die Geschäftslogik ohne die GUI und setzt die dann danach auf die Geschäftslogik. Bei grösseren Programmen und beim erweitern von vorhandenen GUI-Programmen natürlich nicht erst die gesamte Geschäftslogik und dann erst die GUI, aber dort halt auch erst die Geschäftslogik erweitern, und danach dann die GUI-Änderungen nachziehen.

Bei JavaScript und PHP gibt es doch genau die gleichen Herausforderungen bei der Trennung. Das Du JavaScript in eine eigene Datei schreibst und nicht in die HTML-Datei bedeutet noch nicht das GUI-Code und Geschäftslogik getrennt sind! Bei PHP das gleiche. Aber auch dort sollte man das sauber trennen.

Was helfen kann sind Unit-Tests. Je stärker man Dinge vermischt die nicht zusammen gehören, um so schwieriger/aufwändiger ist das in der Regel testbar.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Melewo: in PHP und Javascript ist es ganz und gar nicht üblich, dass man mit verschiedenen Dateien arbeitet, jedenfalls nicht, wenn man sich so in Frontend-lastigen Projekten umschaut. Noch viel weniger findet man, dass Geschäftslogik und Darstellung (HTML-Manipulation) sauber getrennt sind.

Dabei ist die Trennung eigentlich meist ganz einfach: man fängt an, die Funktionalität ohne Darstellung (GUI, HTML, etc) zu schreiben und erst wenn diese für sich sinnvoll lauffähig ist (ich will hier nicht schreiben vollfunktionsfähig, denn das "voll" erreicht man bekanntlich nie) und gut mit Tests abgedeckt ist, dann fängt man an, eine GUI davor zu setzen.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Na ja, dass schreibt sich für einen erfahrenen Programmierer leicht. Ich gehe ja bei mir nur von 2D aus, an 3D denke ich überhaupt nicht. Und da wäre als erstes ein Script für einen Ken-Burns-Effekt, was ich mir nicht so schwer vorstelle, doch ohne dem kein Foto mehr in einem vernünftigen Video eingeblendet wird. Also mit einem Bildausschnitt beginnen, auf einen anderen Bildausschnitt schwenken und dann erst aus dem Bild herauszoomen, um es in der Vollansicht zu zeigen.

Ja und dann vielleicht noch für andere Gelegenheiten die eine oder andere kleinere 2D-Animation. Und da stelle ich mir bislang drei Fenster vor, eins für den Schnelldurchlauf, das sollte nur ein Raster als Hintergrund erhalten. Ein zweites für die Vorschau, beide brauchen keine oder kaum sonstige Elemente enthalten, da die ohnehin schon groß genug werden. Und dann halt ein Fenster mit Auswahlmöglichkeiten, wo dann das eine oder andere gesteuert werden kann. Was aber unbedingt getrennt werden müsste, ist die eigentliche Berechnung für den Ablauf, da die ja austauschbar bleiben sollte. Und irgendwie steht ja am Ende alles in einer gewissen Wechselwirkung und ohne ein Fenster würde nichts laufen.
BlackJack

@Melewo: Was Du da eigentlich machst ist ja Grafikprogrammierung, das ist natürlich ohne Grafik schlecht machbar. Du kannst aber trotzdem den Teil abtrennen der nicht GUI ist. Zum Beispiel in dem Du nicht nur die GUI als Ausgabe verwendest, sondern auch Bilder/Video. Schon hast Du zwei verschiedene Ziele und merkst recht schnell wenn Du Code für die GUI und die Bilder/Video-Ausgabe doppelt schreibst — das gehört dann in den Geschäftslogik-Teil. Und vom Aufbau ist 2D und 3D hier doch gar nicht so verschieden. Das kann man fast identisch machen, mit dem einzigen Unterschied das die Punkte/Koordinaten nur 2 statt 3 Komponenten haben, und die Umrechnung zwischen Welt-/Modell- und Anzeigekoordinaten deutlich einfacher ist.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Ja, deutlich einfache als 3D dürfte es sein. Eigentlich dachte ich noch nicht daran, als ich mit Python begann, hat sich nur zwischenzeitlich so ergeben und da der Import des Bilderstapels in ein Filmschnittprogramm so gut geklappt hatte, werde ich es wohl umsetzen. Die Berechnungen für eine Scene müssten raus, damit die austauschbar bleiben. Somit halt noch einige Testreihen machen und lernen, wie es sich am günstigsten umsetzen lässt.
Quaaak
User
Beiträge: 13
Registriert: Samstag 8. April 2017, 21:42

Danke für eure Antworten. Diese haben mich erstmal in die richtige Richtung gelenkt, hoffe ich. Ich werde jetzt erstmal anfangen, mich mit der Mathematik hinter dem Ganzen zu beschäftigen und später werde ich GUI hinzufügen. Ich fand das Konzept Model-View-Controller sehr interessant. Wahrscheinlich werde ich mich daran halten.

Mich würde jetzt noch interessieren, warum man bei GUIs lieber nicht time.sleep() einsetzt. Würde das nicht funktionieren?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Quaaak hat geschrieben:Mich würde jetzt noch interessieren, warum man bei GUIs lieber nicht time.sleep() einsetzt. Würde das nicht funktionieren?
Damit legst Du ja auch die GUI schlafen. Die geht dann auch während sleep nicht. Und wenn es zu lange dauert, kommt es vielleicht auch zum Abbruch des Programmes wegen Zeitüberschreitung.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Quaaak hat geschrieben:Ich werde jetzt erstmal anfangen, mich mit der Mathematik hinter dem Ganzen zu beschäftigen und später werde ich GUI hinzufügen.
Ganz ohne Berechnungen geht es nicht, doch zu viel davon auf einmal ist auch nicht immer gut. Für die Ausleuchtung beim Rendering sollte wohl diese Formel gut sein:

https://de.wikipedia.org/wiki/Rendergleichung

Da ist dann unter Literatur eine PDF verlinkt, die unter anderem als Beispiel einen Pseudo-Code enthält:

http://www0.cs.ucl.ac.uk/research/vr/Pr ... nments.pdf

Dann werden für die Modellierung von Objekten wohl häufig deren Oberflächen mit einem Netz aus Polygonen überzogen. Einfache Dinge sollten sich für den Anfang auch mit Tkinter und Canvas gestalten lassen.

http://effbot.org/tkinterbook/canvas.htm

Die nächste Seite enthält ein Beispiel und durch after( ... ) berechne neu und config sollte sich das vermutlich auch im Raum bewegen, nur die Formel dafür müsstest Du halt selber schreiben, wie jeder einzelne Punkt sich bei einer Drehung um 360° verändern soll. Hier würde ich wieder als Problem sehen, dass 360° nur eine einfache Drehung ist und noch keine Bewegung, die sich erst aus 360° in jeder beliebigen Richtung ergeben würde.

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

Möchte jetzt hier nicht noch mehr verlinken, doch nebenher könntest Du in der Wikipedia noch unter den Stichpunkten "Geometrische Modellierung", Bildsynthese (rendern) und Computergrafik nachschlagen, falls bisher noch nicht geschehen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Quaaak: habe mal Deine GUI geladen und gespeichert. Na soviel scheint da wohl noch nicht los zu sein:

Code: Alles auswählen

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk


class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.title('3d')
        self.minsize(900, 250)
        # widget definitions ===================================
        self.canvas = tk.Canvas(self,bg='cornsilk')
        self.frame = Frame_1(self)
        self.canvas.pack(expand=1, fill='both', side='left')
        self.frame.pack(expand=1, anchor='e', fill='y')

class Frame_1(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.config(bg='seashell2')
        # widget definitions ===================================
        self.radiobutton = tk.Radiobutton(self,text='Beobachter', bg='seashell2', value=1)
        self.radiobutton.grid(row=0, sticky='w')
        self.radiobutton = tk.Radiobutton(self,text='Block', bg='seashell2', value=2)
        self.radiobutton.grid(row=1, sticky='w')
        self.radiobutton = tk.Radiobutton(self,text='Lampe', bg='seashell2', value=3)
        self.radiobutton.grid(row=2, sticky='w')

if __name__ == '__main__':
    Application().mainloop()
Antworten