tkinter histogram prints downwards - python-3.x

I am trying to draw two histograms alongside one another using tkinter canvas. Everything sort of works ( looks extremely scruffy at the moment) but the histograms are drawn downwards. I have tried making the y0 value negative, but then nothing at all is drawn.
I am using two lists of numerical data, the first with 50 observations and the other with eleven observations, the scales are not the same, but it is the qualitative effect I want at the moment.
The offending code is as follows:
root = Tk()
canvas = Canvas(root, width=620, height=400, background = "salmon")
canvas.grid()
# draw x-axis lines
canvas.create_line(0,2, 500, 0, width = 2, fill = "firebrick")
canvas.create_line(505,2, 610, 0, width = 2, fill = "dark slate blue")
# draw histograms
for idx in range(len(main_counts[0])):
canvas.create_rectangle(idx*10, main_counts[0][idx], 10 +(idx*10), 0, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 40, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
for idx in range(len(star_counts[2])):
canvas.create_rectangle((505 + idx*10), star_counts[2][idx], (515 + (idx*10)), 0, fill = "gold", outline = "dark slate blue")
canvas.create_text(505 + idx*10 + 8, 120, text = idx + 1, font = ("Comic sans MS", 8) , fill = "dark slate blue")
root.mainloop()
I know that I am missing something quite simple and obvious to all of you, but I just can't see it or the way to make my y0 negative which will presumably solve the problem. I can also not see my x-axes, but that may be because they are occluded by the histogram bars.
Many thanks for your patience and help! Any other suggestions about formatting the graphs will be welcomed including suggestions of best font to use for small digit screen display

The system coordinates start in the upper-left corner so you should write something like:
main_counts =[[10, 20, 30]]
for idx in range(len(main_counts[0])):
canvas.create_rectangle(idx*10, 200 -main_counts[0][idx], 10 +(idx*10), 200, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 210, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
You can use enumerate for more readable code:
for idx, val in enumerate(main_counts[0]):
canvas.create_rectangle(idx*10, 200 -val, 10 +(idx*10), 200, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 210, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")

In simple terms - Try to create the histogram from a point on the coordinate according to your value as your second argument in rectangle_crete() function, and then go upto the point where you want your base of the histogram should,which will me constant for all your histograms. Because Tkinter coordinates starts from (0,0) and goes from up to down.
An example code is here -
from Tkinter import *
from random import randint # for testing histogram
master = Tk()
w = Canvas(master, width=1000, height=500)
w.pack()
start_point = 70
padding_y = 450
width = 50
height = 450
list = []
for i in range(1,10):
list.append(randint(1,4))
for i in range(1,10):
end_point = start_point+width
w.create_rectangle(start_point, list[i-1]*100, end_point, height, fill="blue")
start_point += width+20
mainloop()

Related

How to crop a square image from normalized vertices

I'm using this code to identify tops and bottoms of photographs:
( as of now I only have it working for tops. one thing at a time ;) )
def get_file(path):
client = vision.ImageAnnotatorClient()
for images in os.listdir(path):
# # Loads the image into memory
with io.open(images, "rb") as image_file:
content = image_file.read()
image = types.Image(content=content)
objects = client.object_localization(image=image).localized_object_annotations
im = Image.open(images)
width, height = im.size
print("Number of objects found: {}".format(len(objects)))
for object_ in objects:
if object_.name == "Top":
print("Top")
l1 = object_.bounding_poly.normalized_vertices[0].x
l2 = object_.bounding_poly.normalized_vertices[0].y
l3 = object_.bounding_poly.normalized_vertices[2].x
l4 = object_.bounding_poly.normalized_vertices[3].y
left = l1 * width
top = l2 * height
right = l3 * width
bottom = l4 * height
im = im.crop((left, top, right, bottom))
im.save('new_test_cropped.tif', 'tiff')
im.show()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Script to automatically crop images based on google vision predictions of 'tops' and 'bottoms'")
parser.add_argument('--path', help='Include the path to the images folder')
args = parser.parse_args()
get_file(args.path)
The images are opened, clothing is identified, and then the images are cropped and saved to a new file. (granted as of now they are being overwritten within the loop, but I'll fix that later)
What I cant figure out, is how to make the crop a 1:1 ratio. I need to save them out as square-cropped to be put on our website.
I'll be honest, the normalized_vertices make no sense to me. Hence why I'm having trouble.
Starting image:
Output:
Desired Output:
"Normalized" means the coordinates are divided by the width or height of the image, so normalized coordinates [1, 0.5] would indicate all the way (1) across the image and halfway down (0.5).
For a 1:1 aspect ratio you want right - left to be equal to top - bottom. So you want to find out which dimension (width or height) you need to increase, and by how much.
height = abs(top - bottom)
width = abs(right - left)
extrawidth = max(0, height - width)
extraheight = max(0, width - height)
If height > width, we want to increase width but not height. Since height - width > 0, the correct value will go into extrawidth. But because width - height < 0, extraheight will be 0.
Now let's say we want to increase the dimensions of our image symmetrically around the original crop rectangle.
top -= extraheight // 2
bottom += extraheight // 2
left -= extrawidth // 2
right += extrawidth // 2
And finally, do the crop:
im = im.crop((left, top, right, bottom))
For your image, let's say you get left = 93, right = 215, top = 49, and bottom = 205
Before:
After:

How to separate 2 label far apart with tkinter Python?

I would like to separate 2 elements far apart in Tkinter.
I have tried using column such that label_1 is column = 0, row = 0 and label 2 is column 19 and label 3 is column 20 but this still results in them being side by side in the middle. I have set my frame with pack(side =TOP).
I also tried using pack on my label such that label 2 & 3 are right and label 1 is left but still ended up with an unexpected result.
Hence is there a way to separate the 2 elements far apart?
Example
First I use a frame using pack() to display.
self.frameTop.pack(fill=BOTH, expand=NO)
Apparently the condition expand and fill played an important role in display the expected result.
The bottom shows the code for the layout of each element and its anchoring
# Monty Logo
self.icon = Label(self.frameTop, image = self.iconImage, font=('Zilla Slab', 16, 'bold'), borderwidth = 0, highlightthickness = 0, bg="#FFFFFF")
self.icon.pack(padx = 8, pady = 8, side = LEFT, anchor=NW)
# Use a canvas line to deine the cutting
self.labelSeperator = Separator(self.window, orient="horizontal")#Label(self.frameTop, bg="#000000", height= 2, width = int(ws)).pack(side= BOTTOM)
self.labelTitle_time = Label(self.frameTop, font=('Zilla Slab', 16), anchor = "w", borderwidth = 0, highlightthickness = 0, bg="#FFFFFF")
self.labelTitle_time.pack(padx=8,side = RIGHT, anchor=CENTER)
self.labelTitle_day = Label(self.frameTop, font=('Zilla Slab', 16, 'bold'), borderwidth = 0, highlightthickness = 0,bg="#FFFFFF")
self.labelTitle_day.pack(side = RIGHT, anchor=CENTER)

Expanding label widget to fill cell width using grid

trying to get the label to fill the cell entirely using tkinter in Python. I have tried this code, and essentially IMO I am not able to get 'EW' to work. Please help!
from tkinter import *
root = Tk()
root.configure(background='#696969')
root.title('Title')
root.geometry('310x510')
root.resizable(False, False)
f = Frame(root, width=300, height=500, bg='silver', bd=0, cursor='arrow', highlightbackground='silver', highlightcolor='silver', highlightthickness=0, relief=FLAT)
f.grid(row = 0, column = 0, padx = 5, pady = 5, ipadx = 0, ipady = 0, rowspan = 1, columnspan = 1, sticky = 'NSEW')
f.grid_propagate(0)
l = Label(f, text = 'Algorithms', justify = CENTER, bd = 0, anchor = CENTER, bg = '#313233', fg = 'white', wraplength = 0, font = 'Helvetica 9 bold')
l.grid(row = 0, column = 0, padx = 0, pady = 0, ipadx = 0, ipady = 0, rowspan = 1, columnspan = 1, sticky = 'EW')
l.grid_propagate(0)
root.mainloop()
Actually, the Label does fill the cell completely. The problem is that the column is only as wide as its contents, which is the width of the Label. You could make the Label, and therefore the column, wider by using width=... on the Label, but it would be difficult to make it exactly as wide as you want because the width of a Label is measured in character units.
What you really want is the column to expand to the width of the Frame. You can do this by giving the column a weight with columnconfigure. Unused width is distributed to columns with a non-zero weight, relative to the weigth they have (a column with weight=2 will get twice the space of a column with weight=1). Because you only have one column, you can basically use any non-zero number to distribute all unused space to this column. Since we're talking about the grid inside the Frame, you should call columnconfigure on the Frame widget:
f.columnconfigure(0, weight=1)

Multiple stacked chart

I'm trying to reproduce this kind of chart:
So a bar chart with some stacked bar and some non stacked.
The closest I come by is this code:
import matplotlib.pyplot as plt
fooMeans = (20, 35, 30, 35, 27)
barMeans = (25, 32, 34, 20, 25)
ind = list(range(len(fooMeans)))
p1 = plt.bar(ind, barMeans, align='edge', width= 0.4)
p2 = plt.bar(ind, fooMeans, align='edge', width= 0.2)
p3 = plt.bar(ind, barMeans, bottom=fooMeans, align='edge', width= 0.2)
p4 = plt.bar(ind, fooMeans, align='edge', width= -0.2)
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(range(0, 81, 10))
plt.legend((p1[0], p2[0], p3[0], p4[0]), ('Foo', 'Bar','Fii', 'Fuu'))
plt.savefig('foo.png', bbox_inches='tight')
Which draws this:
That's not so bad but the x ticks are not aligned (probably because of the align='edge' of the bar) and the width trick that I use to show the bar next to each other (and not draw all the bar one on top of another) kind of look like an hack, is there any cleaner, by the books way to do this?
Okay, so I find a cleaner way.
The best seems to play with the x position list (ind in my previous code sample).
So I've come up with a little function that can handle it for me:
def make_indice_list(indices, bar_number, bar_width, spacing_ratio=0):
# "Center" the bar number around 0, not clear but if you have 3 bar,
# bar_number_indices = [-1, 0, 1]
bar_number_indices = [i - int(bar_number/2) for i in range(bar_number)]
indices_list = []
for number in bar_number_indices:
indices_list.append([ ( ((number* bar_width) + (spacing_ratio*bar_width) * number) + ind) for ind in indices])
return indices_list
And I do this to use it:
indice_list = make_indice_list(ind, 3, 0.2, 0.1)
p1 = plt.bar(indice_list[0], barMeans, width= 0.2)
p2 = plt.bar(indice_list[1], fooMeans, width= 0.2)
p3 = plt.bar(indice_list[1], barMeans, bottom=fooMeans, width= 0.2)
p4 = plt.bar(indice_list[2], fooMeans, width= -0.2)
No more center, nor weird width, you just have to use the same indices for the stacked bar (and consider them as one when calling the function).
Wich, at the end, draw this:
You could probably make some improvement for the make_indice_list function, mostly removing the comprehension list and using numpy list but I think that's a sweet solution to me.

Can i make the variable used in commands lock when using buttons and changing variables

I am making a Rubik's cube solver using Python and Tkinter and I have encountered a problem when trying to reduce the size of my code.
The peice of code I am showing is for getting to know the situation of the Cube. It draws a net of a cube using buttons with images, but they don't have fixed variables and so when i change a the variable the are defined with, the command variable also changes. is there a way to get round it simply
#Defines Images
White = PhotoImage(file="White.gif")
Yellow = PhotoImage(file="Yellow.gif")
Blue = PhotoImage(file="Blue.gif")
Green = PhotoImage(file="Green.gif")
Red = PhotoImage(file="Red.gif")
Orange = PhotoImage(file="Orange.gif")
#List with Images, positions and values
Colours = [[White, 550, 40, 0, "White"],
[Yellow, 550, 520, 1, "Yellow"],
[Blue, 790, 280, 2, "Blue"],
[Green, 310, 280, 3, "Green"],
[Red, 550, 280, 4, "Red"],
[Orange, 70, 280, 5, "Orange"]]
#Testing Function
def swicth(a):
global Colours
print(Colours[a][4])
#Creates the Buttons in specific locations
for i in Colours:
for j in range(3):
yOffset = (j * 80) + i[2]
for k in range(3):
xOffset = (k * 80) + i[1]
Button(Solver, image = i[0], command=lambda:swicth(i[3])).place(x=xOffset, y=yOffset)
This code works for the most part, but all the buttons have the same outcome of 5. I have got ways of getting around by having 6 sets of for loops for the six colours, but it would be great if anyone could help
change your lambda to this:
..., command=lambda arg=i[3]:swicth(arg)
This will cause i[3] to be bound to the lambda at the time the lambda is created.

Resources