Ha a tkinter modult használva a grafikus felhasználói felületen sokszögeket akarunk kirajzoltatni a vászon (Canvas) create_polygon() metódusával, akkor nem mindegy, hogy a sokszög csúcspontjait, azok koordinátáit milyen sorrendben adjuk át annak. Ugyanis a csúcspontok összekötése a pontok felsorolási sorrendjében történik. Ha nem a megfelelő sorrendben adjuk meg a csúcspontokat, akkor a kirajzoláskor a sokszögben keresztező vonalak is keletkeznek, ami nem a kívánt alakzatot eredményezi. Az alábbi ábra a) és b) képe a nem megfelelő, a c) a helyes csúcspont sorrendekre és az eredményül kapott alakzatokra mutat példát.

Mivel a csúcspontok mint adatok számos forrásból szárazhatnak (felhasználótól bekérés, fájl, hálózat), ezért a megfelelő sorrendjük nem garantált. Ezért érdemes egy olyan függvényt készíteni, amely a csúcspontokat olyan sorrendbe rakja, hogy a sokszög create_polygon() metódussal történő megjelenítése az elvárt legyen.
E függvény elve, hogy a csúcspontok rendezését azok középponthoz viszonyított szöge szerint tesszük meg. Ehhez először meghatározzuk a csúcspontok középpontját (súlypontját), ami, ahogy a geometriából ismert, a csúcspontok mint helyvektorok számtani közepe. Majd pedig kiszámítjuk az egyes csúcspontok középponttól vett szögét. Végül elvégezzük a szögek szerinti rendezést. E sort_vertices_for_proper_plotting nevű függvény definícióját mutatjuk alább. A kommentek segítik a megértést.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# Python 3.10+ from statistics import mean from math import atan2 from typing import Iterable import tkinter as tk def _flatten_xycoords(coords: Iterable) -> Iterable[int | float]: """Az x és y koordinátákat adja vissza egymás után függetlenül attól, hogy azokat az argumentum közvetlenül szolgáltatja, vagy iterálható objektumból származnak. Pl. coords elemei x1, y1, x2, y2 -> kimenet: x1, y1, x2, y2 coords elemei (x1, y1), [x2, y2] -> kimenet: x1, y1, x2, y2 """ for coord in coords: try: iter(coords) if isinstance(coord, (str, bytes, bytearray)): raise TypeError yield from _flatten_xycoords(coord) except TypeError: if isinstance(coord, (int, float)): yield coord else: raise TypeError('A koordináták csak valós számok lehetnek.') def sort_vertices_for_proper_plotting(*vertices_to_draw) -> tuple: """Bármilyen sorrendben vannak a kirajzolandó sokszög csúcspontjai megadva, a visszatérési érték a csúcspontok olyan sorozata lesz, amellyel a sokszög megfelelően, azaz keresztező vonalak nélkül lesz megjelenítve. A csúcspontokat az x, y koordináták egymást követő felsorolsával, vagy az x, y párokat szolgáltató iterálható objektumok felsorolásával lehet megadni. """ xy_coordinates = list(_flatten_xycoords(vertices_to_draw)) # A kapott lista: [x1, y1, x2, y2, ..., xn, yn] # Kinyerjük az x koordináták sorozatát és az y koordináták sorozatát. x_coords, y_coords = xy_coordinates[::2], xy_coordinates[1::2] # Meghatározzuk a csúcspontok súlypontját (középpontját), ami mint az a geometriából ismert, a csúcspontok számtani közepe. center_x, center_y = mean(x_coords), mean(y_coords) # Kiszámítjuk az egyes csúcspontok középponttól vett szögét. vertices_angles = {(x, y): atan2(x - center_x, y - center_y) for x, y in zip(x_coords, y_coords)} # A csúcspontokat a szögeik szerint rendezzük. sorted_vertices_angles = sorted(vertices_angles.items(), key=lambda t: t[1]) # A rendezett csúcspontokkal térünk vissza. return tuple(point for point, angle in sorted_vertices_angles) |
E függvényben használtunk egy – szintén a fenti programkódban látható – _flatten_xycoords nevű segédfüggvényt, aminek szerepe, hogy a csúcspontok x és y koordinátáit egymás után szolgáltassa függetlenül attól, hogy azok így lettek megadva, vagy iterálható objektumból származnak. E segédfüggvény egyébként egy korábbi, „Hogyan nyerjük ki az elemeket egy konténerbe többszörösen beágyazott iterálható objektumokból?” című bejegyzésben szereplő függvénynek e feladatra történt adaptációja.
A rendező függvényünk megfelelő működéséről a következő tesztfüggvénnyel meggyőződhetünk:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# TESZT def test_sort_points_to_draw_polygon_properly(vertices, sort=True): root = tk.Tk() cnv = tk.Canvas(root, bg='light yellow', width=500, height=500) cnv.pack() points = sort_vertices_for_proper_plotting(*vertices) if sort else vertices cnv.create_polygon(*points, fill='cyan') root.mainloop() # Ugyanarra a csúcspontsorozatra rendezés nélkül helytelen, rendezéssel a várt sokszög jelenik meg # a fenti ábra b) és c) képéhez hasonlóan. test_sort_points_to_draw_polygon_properly(((250, 50), (350, 450), (450, 250), (50, 250), (150, 450)), sort=False) test_sort_points_to_draw_polygon_properly(((250, 50), (350, 450), (450, 250), (50, 250), (150, 450)), sort=True) |
E bejegyzés témájához a Python tudásépítés lépésről lépésre című e-könyvben elsősorban a „Grafikus felhasználói felület készítése” fejezet kapcsolódik. E mellett érdemes még a „Különleges függvénydefiníciók” fejezetben az „Amikor a kígyó a farkába harap – függvényrekurzió” alfejezetet is átfutni.