ein sich drehener Pfeil

Fragen zu Tkinter.
Antworten
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Hallo,

ich habe ein Programm geschrieben, dass einen Pfeil erzeugt, die Pfeilform abhängig von einem Pfeilfaktor self._r_divisor ist und man den Pfeil hin- und herbewegen kann.

Kann man dieses Problem mit einem sich drehenden Pfeil auch anders lösen?

Code: Alles auswählen

try:
    import tkinter as tk
except:
    import Tkinter as tk
    
import numpy as np

def polar_to_cartesian(radius, phi, x_0=0, y_0=0):
    x = radius * np.cos(phi) + x_0
    y = radius * np.sin(phi) + y_0
    return x, y 

def cartesian_to_polar(x, y):
    phi = np.arctan(y/x)
    radius = y/(np.sin(phi))
    print ("myphi=%f, myrad=%f" %(phi, radius))
    return radius, phi

def degree_to_radiant(degree):
    radiant = (degree/180.)*np.pi
    return radiant
def radiant_to_degree(phi):
    degree=(phi/np.pi)*180.
    return degree

class Arrow():
    
    def __init__(self, canvas, x_0=100, y_0=100, radius=100, color='green'):
        self._canvas = canvas
        self._x_0 = x_0
        self._y_0 = y_0
        self._radius = radius
        self._color = color
        self._phi = 0
        self._r_divisor=4.665
        self._arrow_width = self._radius/self._r_divisor
        

        # self._canvas = tk.Canvas()
        self._canvas.create_oval(self._x_0-self._radius, self._y_0-self._radius, self._x_0+self._radius, self._y_0+self._radius)
        self._create_arrow()
        
    def _create_arrow(self):
        self._x_1, self._y_1 = polar_to_cartesian(self._radius, self._phi, x_0=self._x_0, y_0=self._y_0)
        
        self._x_2 = self._x_1-(self._radius/self._r_divisor) 
        self._y_2 = self._y_1+(self._radius/(2*self._r_divisor))

        self._x_3 = self._x_1-(self._radius/self._r_divisor)  
        self._y_3 = self._y_1-(self._radius/(2*self._r_divisor))

        self._arrowhead = self._canvas.create_polygon(self._x_1, self._y_1,     # P1
                                                      self._x_2, self._y_2,     # P2 
                                                      self._x_3, self._y_3,     # P3
                                                      fill='blue')
        
        self._arrow = self._canvas.create_line(self._x_0, 
                                               self._y_0, 
                                               self._x_1, 
                                               self._y_1)
        
        print ("x_0 ---> %d      y_0 ---> %d" %(self._x_0, self._y_0))                
        print ("x_1 ---> %d      y_1 ---> %d" %(self._x_1, self._y_1))
        print ("x_2 ---> %d      y_2 ---> %d" %(self._x_2, self._y_2))
        print ("x_3 ---> %d      y_3 ---> %d" %(self._x_3, self._y_3))
        
        self._radius_arrow, self._phi_arrow = cartesian_to_polar(self._x_2-self._x_0, self._y_2-self._y_0)
        print ("Radius ---> %d   phi ---> %d" %(self._radius_arrow, self._phi_arrow))        
        
    def _calculate_coords(self):
        self._x_1, self._y_1 = polar_to_cartesian(self._radius, self._phi, x_0=self._x_0, y_0=self._y_0)

        self._x_2, self._y_2 = polar_to_cartesian(self._radius_arrow, self._phi_arrow+self._phi, x_0=self._x_0, y_0=self._y_0)        
        self._x_3, self._y_3 = polar_to_cartesian(self._radius_arrow, -self._phi_arrow+self._phi, x_0=self._x_0, y_0=self._y_0)
        
        print ("x_0 ---> %d      y_0 ---> %d" %(self._x_0, self._y_0))
        print ("x_1 ---> %d      y_1 ---> %d" %(self._x_1, self._y_1))
        print ("x_2 ---> %d      y_2 ---> %d" %(self._x_2, self._y_2))
        print ("x_3 ---> %d      y_3 ---> %d" %(self._x_3, self._y_3))

    def set_phi(self, phi=0):
        self._phi=phi
        self._calculate_coords()
        
        self._canvas.coords(self._arrow, self._x_0, self._y_0, self._x_1, self._y_1)
        self._canvas.coords(self._arrowhead, self._x_1, self._y_1, self._x_2, self._y_2, self._x_3, self._y_3)
        
    def set_degree(self, angle):
        self.set_phi(degree_to_radiant(angle))
        
    def get_phi(self):
        return self._phi
    
def main ():

    print ("in main...")
        
    root = tk.Tk()
    canvas_frame = tk.Frame(root)
    canvas = tk.Canvas(canvas_frame)
    canvas.pack()

    myarrow = Arrow(canvas, x_0 = 120, y_0=120)
    
    def do_exit():
        root.quit()
        root.destroy()
        
    def do_move_arrow_pos():
        phi = myarrow.get_phi()
        degree = radiant_to_degree(phi)
        degree += 10
        phi = degree_to_radiant(degree)
        myarrow.set_phi(phi)

    def do_move_arrow_neg():
        phi = myarrow.get_phi()
        degree = radiant_to_degree(phi)
        degree -= 10
        phi = degree_to_radiant(degree)
        myarrow.set_phi(phi)
        
            
    button_frame = tk.Frame(root)
    
    beenden_button = tk.Button(button_frame, text="Beenden", width=40, height=3, command=do_exit)
    move_arrow_button_pos = tk.Button(button_frame, text="--->", width=40, height=3, command=do_move_arrow_pos)
    move_arrow_button_neg = tk.Button(button_frame, text="<---", width=40, height=3, command=do_move_arrow_neg)
    
    move_arrow_button_pos.pack()
    move_arrow_button_neg.pack()
    beenden_button.pack()
        
    canvas_frame.grid(row=0, column=1)
    button_frame.grid(row=0, column=2)
    
    root.mainloop()
    
if __name__ == '__main__':
    main ()    
    
    print ("Programm ende ...")
BlackJack

@Papp Nase: Habe nur bisher nur kurz drüber geschaut und frage mich ob Du Numpy tatsächlich brauchst. Das ist eine ziemlich fette Abhängigkeit und soweit ich das sehe arbeitest Du nirgends mit Arrays sondern nur mit skalaren Werten. Dafür gibt es in der Standardbibliothek das `math`-Modul. Dort findet man auch bereits fertige Funktionen um zwischen Grad und Bogenmass umzurechnen. Wenn Du die Punkte als komplexe Zahlen (`complex`) auffasst, dann gibt es im `cmath`-Modul in der Standardbibliothek auch schon die Umrechnungsfunktionen für kartesisch und polar (`polar()`/`rect()`).

Mit komplexen Zahlen kann man es aber auch noch einfacher haben, weil da folgende Formel zum rotieren (um den Nullpunkt) geht: ``complex(x, y) * cmath.exp(math.radians(angle_in_degrees) * 1j)``. Das Ergebnis steht dann in den Attributen `real` und `imag`.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Hi,

vielen Dank für Deine Antwort und den Tipp mit der anderen Bibliothek. Ich habe in dem Teil, wo der Zeiger eingebettet wird, auch schon das numpy immer benutzt, von daher hab ich es jetzt irgendwie immer eingebunden. Ich werde es aber mal mit der anderen Bibliothek probieren.

Ich werd auch mal probieren, die Umrechnung von polar-Koordinaten in das andere mit den fertigen Funktionen zu benutzen und ebenso den Ansatz mit den komplexen Zahlen. Wenn ich es dann fertig hab, dann kann ich es Dir ja nochmal zuschicken.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo

Hier noch etwas für müde Programmierer, scheue Mathematiker und Modulabhängige: :-)

Code: Alles auswählen

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

from functools import partial
try:
    import tkinter as tk
except:
    import Tkinter as tk
from PIL import Image, ImageTk

import io
import base64

# Arrow picture ( Arrow length 100 pixel)
ARROW_B64 = """
iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkCCh8Ije9+fwAAABl0RVh0Q29tbWVudABDcmVhdGVk
IHdpdGggR0lNUFeBDhcAAAE5SURBVHja7dtBDoMgEEBRNG65/0E5AF3WKCpGFxN5b+8G+ztDmqYE
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAC8pj56eHCDjBJJvPz07QD6uPpkmJghfnyC1/XnPAoFNILdDsWIx6tpVe9Yu
geB+ksphKAJBJCfTxB2EEe8gHffyLBAEchXK4gCj7sUEeA+TQOIx1WN84ZggcHUHEQjsJvf/x0OB
IIxGGAKBzTrVIhBMjRMCQRgCQRhr/X+cEgimBoyrOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCWH/+EGTi6Gvx4AAAA
AElFTkSuQmCC"""

ARROW_IMAGE = 'arrow_01.png'
RADIUS = 100
XORG = 150
YORG = 150

class RotatingArrow(tk.Canvas):
    def __init__(self, app, **kwargs):
        self.app = app
        tk.Canvas.__init__(self, app, width=300, height=300)

        self.angle = 0
        self.create_oval(XORG-RADIUS, YORG-RADIUS,
            XORG+RADIUS, YORG+RADIUS)

        try:
            # Process image file
            self.image = Image.open(ARROW_IMAGE)
        except:
            # Is the image file not available process the Base64 data instead                
            jpg_str = base64.b64decode(ARROW_B64)
            jpg_data = io.BytesIO(jpg_str)    
            self.image = Image.open(jpg_data)
            
        self.tkimage = ImageTk.PhotoImage(self.image)
        self.create_image(XORG, YORG, image=self.tkimage, tag='Arrow')
        self.create_text(XORG, YORG-RADIUS/2, text=self.angle, fill='darkgreen',
            font=('Helvetica', 30, 'bold'), tag='Degrees')

    def move_arrow(self, step=10, direction='cw'):
        if direction == 'cw':
            if self.angle <= 0:
                self.angle = 360
            self.angle -= step
                
        if direction == 'ccw':
            if self.angle >= 360:
                self.angle = 0
            self.angle += step
               
        self.tkimage = ImageTk.PhotoImage(self.image.rotate(self.angle))
        self.itemconfigure('Arrow', image=self.tkimage)
        self.itemconfigure('Degrees', text=self.angle)

def main():
    app_win = tk.Tk()
    app_win.title('Arrow as rotating image')
    rotating_arrow = RotatingArrow(app_win)
    rotating_arrow.pack()
    button_frame = tk.Frame(app_win)

    tk.Button(button_frame, text="--->", width=40, height=3,
        command=partial(rotating_arrow.move_arrow, direction='cw')).pack()
    tk.Button(button_frame, text="<---", width=40, height=3,
        command=partial(rotating_arrow.move_arrow, direction='ccw')).pack()
    tk.Button(button_frame, text="Beenden", width=40, height=3,
        command=app_win.destroy).pack()
     
    rotating_arrow.grid(row=0, column=1)
    button_frame.grid(row=0, column=2)
   
    app_win.mainloop()

main()
Gruss wuf :wink:
Take it easy Mates!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@wuf: Du solltest dir mal den Modulo-Operator anschauen, damit vereinfachen sich die Zeilen 55 bis 63 ein wenig. Das entspricht dann zwar nicht ganz deinem Ergebnis, aber bei einer Drehung von 355 Grad in Richtung +10 Grad wolltest du wohl eh lieber 5 Grad als Ergebnis haben und nicht 10 ;-)
Das Leben ist wie ein Tennisball.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi EyDu

Danke für deine Antwort. Habe leider nicht genau verstanden was du meinst. Könntest du mir bitte mit einem Code-Snippet zeigen was du meinst? Ich modifizierte mein Skript in der Zwischenzeit ein wenig:

Code: Alles auswählen

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

from functools import partial
try:
    import tkinter as tk
except:
    import Tkinter as tk
from PIL import Image, ImageTk

import io
import base64

# Arrow picture ( Arrow length 100 pixel)
ARROW_B64 = """
iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gkCCh8Ije9+fwAAABl0RVh0Q29tbWVudABDcmVhdGVk
IHdpdGggR0lNUFeBDhcAAAE5SURBVHja7dtBDoMgEEBRNG65/0E5AF3WKCpGFxN5b+8G+ztDmqYE
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAC8pj56eHCDjBJJvPz07QD6uPpkmJghfnyC1/XnPAoFNILdDsWIx6tpVe9Yu
geB+ksphKAJBJCfTxB2EEe8gHffyLBAEchXK4gCj7sUEeA+TQOIx1WN84ZggcHUHEQjsJvf/x0OB
IIxGGAKBzTrVIhBMjRMCQRgCQRhr/X+cEgimBoyrOAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCWH/+EGTi6Gvx4AAAA
AElFTkSuQmCC"""

ARROW_IMAGE = 'arrow_01.png'
RADIUS = 100
XORG = 150
YORG = 150
STEP = 5

class RotatingArrow(tk.Canvas):
    def __init__(self, app, **kwargs):
        self.app = app
        tk.Canvas.__init__(self, app, width=300, height=300)

        self.angle = 0
        self.steps = 0
        self.create_oval(XORG-RADIUS, YORG-RADIUS,
            XORG+RADIUS, YORG+RADIUS)

        try:
            # Process image file
            self.image = Image.open(ARROW_IMAGE)
        except:
            # Is the image file not available process the Base64 data instead                
            jpg_str = base64.b64decode(ARROW_B64)
            jpg_data = io.BytesIO(jpg_str)    
            self.image = Image.open(jpg_data)

        self.create_text(XORG, YORG-RADIUS/2, text=self.angle, fill='darkgreen',
            font=('Helvetica', 30, 'bold'), tag='Degrees')            
        self.tkimage = ImageTk.PhotoImage(self.image)
        self.create_image(XORG, YORG, image=self.tkimage, tag='Arrow')

    def move_arrow(self, step):
        self.steps += step
        self.angle = self.steps%360
                 
        self.tkimage = ImageTk.PhotoImage(self.image.rotate(self.angle))
        self.itemconfigure('Arrow', image=self.tkimage)
        self.itemconfigure('Degrees', text=self.angle)

def main():
    app_win = tk.Tk()
    app_win.title('Arrow as rotating image')
    rotating_arrow = RotatingArrow(app_win)
    rotating_arrow.pack()
    button_frame = tk.Frame(app_win)

    tk.Button(button_frame, text="--->", height=3,
        command=partial(rotating_arrow.move_arrow, STEP)).pack(fill='x')
    tk.Button(button_frame, text="<---", height=3,
        command=partial(rotating_arrow.move_arrow, -STEP)).pack(fill='x')
    tk.Button(button_frame, text="Beenden", width=40, height=3,
        command=app_win.destroy).pack()
     
    rotating_arrow.grid(row=0, column=1)
    button_frame.grid(row=0, column=2)
   
    app_win.mainloop()

main()
Gruss wuf :wink:
Take it easy Mates!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Im Prinzip hast du es genau so wie ich meinte, nur das steps-Attribut würde ich noch weglassen:

Code: Alles auswählen

def move_arrow(self, step):
    self.angle = (self.angle + step) % 360

    ...
In Zeile 45 solltest du das except noch anpassen, so dass es nur die behandelbaren Fehler auch behandelt. In diesem Fall wohl einen IOError.
Das Leben ist wie ein Tennisball.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

OK EyDu

Habe deine Anregungen noch in mein Skript einfliessen lassen. Besten Dank für deine Tipps und Hilfe.

Gruss wuf :wink:
Take it easy Mates!
Antworten