How to rotate ellipse in tkinter canvas? - python

I have a simple ellipse (red) and I want to rotate the ellipse.
The following code only return flatten ellipse (blue) but it does not rotate:
import tkinter as tk
import numpy as np
import math
def rotate(points, angle):
new_points = list(points)
rad = angle * (math.pi/180)
cos_val = math.cos(rad)
sin_val = math.sin(rad)
for coords in new_points:
x_val = coords[0]
y_val = coords[1]
coords[0] = x_val * cos_val - y_val * sin_val
coords[1] = x_val * sin_val + y_val * cos_val
return new_points
window = tk.Tk()
window.geometry("500x300")
canvas = tk.Canvas(window, height=300, width=500)
canvas.grid(row=0, column=0, sticky='w')
# draw ellipse
size=80
x0,y0=100,100
width,height=0.25*size,0.5*size
x1,y1=x0+width,y0+height
xc=x0+width/2
yc=y0+height/2
ellipse = canvas.create_oval([x0, y0, x1,y1],fill='blue')
#draw rotated ellipse
coord1=canvas.coords(ellipse)
points=[[coord1[0],coord1[1]],[coord1[2],coord1[3]]]
point2=rotate(points,30)
coord2 = [item for sublist in point2 for item in sublist]
ellipse2 = canvas.create_oval(coord2,fill='red')
window.mainloop ()
Here is the result:
The red ellipse supposed to be rotated by 30 degree but instead of rotated, it just get flattened.
Question: How to rotate the ellipse in tkinter canvas?
Notes:
I am using python 3.6
I checked stackoverflow on similar question and it has no correct answer.
unlike polygon that we can simply rotate each vertices, ellipse has no vertex.

The oval item of tkinter canvas is not rotatable as an oval.
If you look at the docs, like effbot.org, you'll see that some items are created with a first position arg (a single point, like text), some with a bbox arg (two points, like rectangle and oval), and some with coords (variable, two or more points, like line and polygon).
You can rotate the coords items by (1) calculating new coords and (2) updating the item with the new coords, using the coords() method.
For the rest of the items, you're out of luck, except for text which supports an angle attribute which you can set with itemconfig(item, angle=new_angle)
But all hope is not lost. You can convert a rectangle or oval into a polygon (by creating a new polygon to replace your old bbox item, and then you'll be able to rotate the new item. With rectangle, it's easy-peasy. With oval, it's much trickier, as you have to simulate the oval using many, many polygon coordinates for it to look good.

There are 2 ways that I know of 1) plot every point on the edge of the ellipse. There are pages on the web that show how to calculate all of the points 2) save as an image and use tkinter's PIL library to rotate the image.

Related

how do i define the height of rectangle?

"Create a rectangle with coordinates x1=15, y1=20, width=120, height=150"
Does it have to do something with the coordinates? Cause there's no 'height' option
from tkinter import *
window = Tk()
root.geometry("300x300")
canv = Canvas(window, width=200, height=250, bg="white")
canv.pack(pady=15)
coords = [15, 20, 0, 0]
rect = canvas.create_rectangle(coords, width=120)
window.mainloop()```
Yes, your coords define the positions of two opposite corners of the rectangle, [x1, y1, x2, y2]. You have the coordinates of the top left corner already, so you need to figure out the coordinates of the bottom right corner using the given height and width - you simply add these to the coordinates.
In this example you simply would change your coords from [15, 20, 0, 0] to [15, 20, 135, 170].
One thing to note is that the coordinate system in Tkinter is defined with (0,0) at the top left of the window, so a higher y coordinate means further down the window and higher x coordinate means further right.
The coordinates are how you define the height and width of the rectangle. No height definition is required.
This is from : https://pythonguides.com/python-tkinter-canvas/
Python Tkinter Canvas Rectangle
Python Tkinter Canvas has built-in features to create shapes.
To create a rectangle create_rectangle() method is used.
This method accepts 4 parameters x1, y1, x2, y2. Here x1 and y1 are the coordinates for the top left corner and x2 and y2 are the coordinates for the bottom right corner.

Can we draw rectangle using matplotlib.Patches by just specifying its corner?

I want to conceal few points on a plot, I am using patches to draw a rectangle, so is there any way of plotting a rectangle with just specifying the corners?
I only know how to draw by height and width parameters.
patch= ax1.add_patch(patches.Rectangle((x, y), 0.3, 0.5)
how can i modify the code to draw rectangle by just using say coordinates like these (x1,y1),(x2,y2)(x3,y3)(x4,y4).
I assume that the coordinates to be ordered in the following way:
top_left = [2,2]
bottom_left = [2, 1]
top_right = [4,2]
bottm_right = [4, 1]
So you can easily calculate the width and height and input them to patches
w = top_left[0]-top_right[0]
h = top_left[1]-bottom_left[1]
NOTE
If they are not ordered the logic is simple, you find to points where the x position is identical and calculate in absolute value the the difference and obtain the width (and symmetrically the height)
The selected answer still just calculates the length and width (and ignores any angle if one was desired). It could be made to work by calculating the angle and adding that too, but it's still hacking around your intention if you've already calculated all of the vertices.
Another option you have is to just use the patches.Polygon class.
points = [(x1,y1),(x2,y2)(x3,y3)(x4,y4)]
rect = patches.Polygon(points, linewidth=1, edgecolor='r', facecolor='none')
ax.add_patch(rect)
will end up just drawing a rectangle if that's what those points specify. Note, the order of the points matters, but that isn't a big deal. Here is an image of where I just did this. The green boxes + are my calculated points, and the red rectangles are my polygons
sample of where I did this

How can I draw random semi circle, same size but different orientations in python?

I would like to plot different semicircles same radius but different orientation different places. Any advice would be appreciated.
You can use the patch collection in matplotlib.
The Wedge object plots a circle wedge at center = (x,y) of radius r and you give it the start (theta1) and end (theta2) angles. If you want a semicircle, then theta2 = theta1 + 180, and theta1 would be how much you rotate the "canonical" semicircle (the one that goes from 0 to 180 degrees). To declare the objects:
wedges = []
for row in semicircle_specs:
wedge = Wedge((row['x'], row['y']), RADIUS, row['angle'], row['angle'] + 180)
wedges.append(wedge)
If you don't wish to work with matplotlib, This link provides a solution with Tkinter

Draw a polygon using draw.polygon

import PIL.ImageDraw as ImageDraw,PIL.Image as Image, PIL.ImageShow as ImageShow
#that s my class Point(2D)
class Pnt(namedtuple('Pnt', 'x y')):
__slots__ = ()
def __init__(self, *args):
super(Pnt, self).__init__(*args)
Here is the vector of the vertices(convex polygon)
vertix = []
vertix.append(Pnt(50, 100))
vertix.append(Pnt(100, 200))
vertix.append(Pnt(200, 200))
vertix.append(Pnt(300, 0))
vertix.append(Pnt(250, -200))
vertix.append(Pnt(100, -100))
Here I want to draw the polygon. The problem is it is not centered, so almost half the polynom is outside the frame.
im = Image.new("RGB", (600,800))
draw = ImageDraw.Draw(im)
draw.polygon(vertix, fill=230, outline=255)
im.show()
If you want to center your polygon in the Image you can
1) determine the bounding box of your polygon,
2) calculate the coordinates of the center of the bounding box,
3) calculate the vector required to translate the center of the bounding box to the center of the Image rectangle,
4) create a new polygon by translating the coords of the vertices of the old poly by the vector found in step 3.

python arrange images on canvas in a circle

I have bunch of images (say 10) I have generated both as array or PIL object.
I need to integrate them into a circular fashion to display them and it should adjust itself to the resolution of the screen, is there anything in python that can do this?
I have tried using paste, but figuring out the resolution canvas and positions to paste is painful, wondering if there is an easier solution?
We can say that points are arranged evenly in a circle when there is a constant angle theta between neighboring points. theta can be calculated as 2*pi radians divided by the number of points. The first point is at angle 0 with respect to the x axis, the second point at angle theta*1, the third point at angle theta*2, etc.
Using simple trigonometry, you can find the X and Y coordinates of any point that lies on the edge of a circle. For a point at angle ohm lying on a circle with radius r:
xFromCenter = r*cos(ohm)
yFromCenter = r*sin(ohm)
Using this math, it is possible to arrange your images evenly on a circle:
import math
from PIL import Image
def arrangeImagesInCircle(masterImage, imagesToArrange):
imgWidth, imgHeight = masterImage.size
#we want the circle to be as large as possible.
#but the circle shouldn't extend all the way to the edge of the image.
#If we do that, then when we paste images onto the circle, those images will partially fall over the edge.
#so we reduce the diameter of the circle by the width/height of the widest/tallest image.
diameter = min(
imgWidth - max(img.size[0] for img in imagesToArrange),
imgHeight - max(img.size[1] for img in imagesToArrange)
)
radius = diameter / 2
circleCenterX = imgWidth / 2
circleCenterY = imgHeight / 2
theta = 2*math.pi / len(imagesToArrange)
for i, curImg in enumerate(imagesToArrange):
angle = i * theta
dx = int(radius * math.cos(angle))
dy = int(radius * math.sin(angle))
#dx and dy give the coordinates of where the center of our images would go.
#so we must subtract half the height/width of the image to find where their top-left corners should be.
pos = (
circleCenterX + dx - curImg.size[0]/2,
circleCenterY + dy - curImg.size[1]/2
)
masterImage.paste(curImg, pos)
img = Image.new("RGB", (500,500), (255,255,255))
#red.png, blue.png, green.png are simple 50x50 pngs of solid color
imageFilenames = ["red.png", "blue.png", "green.png"] * 5
images = [Image.open(filename) for filename in imageFilenames]
arrangeImagesInCircle(img, images)
img.save("output.png")
Result:

Categories

Resources